# Tracksolid — Docker & Remote Database Command Reference **Server:** `rahamafresh.com` · **User:** `kianiadee` **DB:** `tracksolid_db` · **DB user:** `postgres` **Container:** TimescaleDB — name includes a Coolify-generated suffix that changes on every redeploy --- ## Table of Contents 1. [How It Works](#1-how-it-works) 2. [Setup — Shell Function on the Server](#2-setup--shell-function-on-the-server) 3. [Connecting From Your Mac](#3-connecting-from-your-mac) 4. [Fleet & Live Positions](#4-fleet--live-positions) 5. [Trips & Movement](#5-trips--movement) 6. [Alarms](#6-alarms) 7. [Parking & Idle Time](#7-parking--idle-time) 8. [Position History & Route Replay](#8-position-history--route-replay) 9. [Ingestion Pipeline Health](#9-ingestion-pipeline-health) 10. [Device & Fleet Registry](#10-device--fleet-registry) 11. [Schema & Migrations](#11-schema--migrations) 12. [Container & Service Operations](#12-container--service-operations) 13. [Quick Reference — Flag Meanings](#13-quick-reference--flag-meanings) --- ## 1. How It Works All remote database access uses two layers: ``` Your Mac → SSH to rahamafresh.com → docker exec into timescale_db → psql ``` **Layer 1 — find the container.** Coolify appends a random suffix to container names on every redeploy, so the container name is never hardcoded. Instead, resolve it dynamically: ```bash TS_DB=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) ``` **Layer 2 — run psql inside it.** ```bash docker exec -i "$TS_DB" psql -U postgres -d tracksolid_db -c "SELECT ..." ``` Use `-i` (not `-it`) when running non-interactively or piping input — the `-t` TTY flag conflicts with stdin redirection and causes errors. --- ## 2. Setup — Shell Function on the Server Add this once to `~/.zshrc` on the server. It resolves the container automatically and passes all arguments through to `psql`. ```bash # Auto-resolves current TimescaleDB container — survives Coolify redeployments tsdb() { local container container=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) if [[ -z "$container" ]]; then echo "ERROR: no running timescale_db container found" >&2 return 1 fi docker exec -it "$container" psql -U postgres -d tracksolid_db "$@" } ``` **Activate:** ```bash source ~/.zshrc ``` **Usage on the server:** ```bash tsdb # open interactive psql prompt tsdb -c "SELECT COUNT(*) FROM tracksolid.trips;" tsdb -c "\dt tracksolid.*" # list all tracksolid tables tsdb -c "\d tracksolid.trips" # describe a table tsdb -f /app/my_query.sql # run a SQL file ``` --- ## 3. Connecting From Your Mac ### Open an interactive psql session ```bash ssh kianiadee@rahamafresh.com -t \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -it "$TS" psql -U postgres -d tracksolid_db' ``` ### Run a single query and return output to your terminal ```bash ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -i "$TS" psql -U postgres -d tracksolid_db -c "SELECT now();"' ``` ### Pipe a local SQL file into the remote database ```bash ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -i "$TS" psql -U postgres -d tracksolid_db' \ < /path/to/local_query.sql ``` ### Run a migration file from your Mac ```bash ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -i "$TS" psql -U postgres -d tracksolid_db' \ < 05_enhancement_migration.sql ``` > **Note on quoting:** When embedding SQL in a shell one-liner, single-quote literals inside the SQL need to be escaped as `'"'"'`. The `tsdb()` function avoids this entirely — use it for complex queries. --- ## 4. Fleet & Live Positions ### All devices with a position in the last hour ```bash ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -i "$TS" psql -U postgres -d tracksolid_db -c " SELECT d.device_name, d.mc_type, ROUND(lp.lat::numeric, 5) AS lat, ROUND(lp.lng::numeric, 5) AS lng, lp.speed, lp.acc_status, lp.gps_signal, lp.gps_num AS satellites, lp.gps_time AT TIME ZONE '"'"'Africa/Nairobi'"'"' AS last_fix_eat, ROUND(EXTRACT(EPOCH FROM (now() - lp.gps_time)) / 60.0, 0) AS mins_ago FROM tracksolid.live_positions lp JOIN tracksolid.devices d ON d.imei = lp.imei WHERE lp.gps_time > now() - interval '"'"'1 hour'"'"' ORDER BY lp.gps_time DESC;"' ``` ### All 19 live positions — current snapshot ```sql SELECT d.device_name, d.mc_type, ROUND(lp.lat::numeric, 5) AS lat, ROUND(lp.lng::numeric, 5) AS lng, lp.speed, lp.acc_status, lp.gps_time AT TIME ZONE 'Africa/Nairobi' AS last_fix_eat, ROUND(EXTRACT(EPOCH FROM (now() - lp.gps_time)) / 3600.0, 1) AS hours_ago, lp.current_mileage AS odometer_km FROM tracksolid.live_positions lp JOIN tracksolid.devices d ON d.imei = lp.imei ORDER BY lp.gps_time DESC; ``` ### Devices with no position (silent fleet) ```sql SELECT d.imei, d.device_name, d.mc_type, d.sim, d.expiration::date FROM tracksolid.devices d LEFT JOIN tracksolid.live_positions lp ON lp.imei = d.imei WHERE lp.imei IS NULL ORDER BY d.mc_type, d.device_name; ``` ### Vehicles currently moving (ACC on OR speed > 0) ```sql SELECT d.device_name, lp.speed, lp.acc_status, lp.gps_time AT TIME ZONE 'Africa/Nairobi' AS last_fix_eat FROM tracksolid.live_positions lp JOIN tracksolid.devices d ON d.imei = lp.imei WHERE lp.speed > 0 OR lp.acc_status = '1' ORDER BY lp.speed DESC; ``` ### Geographic anomaly check — vehicles outside Kenya ```sql SELECT d.device_name, ROUND(lp.lat::numeric, 4) AS lat, ROUND(lp.lng::numeric, 4) AS lng, lp.gps_time AT TIME ZONE 'Africa/Nairobi' AS last_fix_eat FROM tracksolid.live_positions lp JOIN tracksolid.devices d ON d.imei = lp.imei WHERE lp.lat NOT BETWEEN -5.0 AND 5.0 OR lp.lng NOT BETWEEN 33.9 AND 42.0; ``` --- ## 5. Trips & Movement ### Trips today — summary per vehicle ```sql SELECT d.device_name, d.vehicle_number, d.driver_name, COUNT(*) AS trips, ROUND(SUM(t.distance_km)::numeric, 2) AS total_km, ROUND(AVG(t.avg_speed_kmh)::numeric, 1) AS avg_speed_kmh, MAX(t.max_speed_kmh) AS top_speed_kmh, ROUND(SUM(t.driving_time_s) / 3600.0, 2) AS drive_hours, ROUND(SUM(t.idle_time_s) / 3600.0, 2) AS idle_hours, MIN(t.start_time AT TIME ZONE 'Africa/Nairobi') AS day_start, MAX(t.end_time AT TIME ZONE 'Africa/Nairobi') AS day_end FROM tracksolid.trips t JOIN tracksolid.devices d ON d.imei = t.imei WHERE t.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' GROUP BY d.device_name, d.vehicle_number, d.driver_name ORDER BY total_km DESC; ``` ### Trips in last 24 hours — full detail ```sql SELECT d.device_name, t.start_time AT TIME ZONE 'Africa/Nairobi' AS start_eat, t.end_time AT TIME ZONE 'Africa/Nairobi' AS end_eat, ROUND(t.distance_km::numeric, 2) AS distance_km, t.avg_speed_kmh, t.max_speed_kmh, ROUND(t.driving_time_s / 60.0, 0) AS drive_mins, ROUND(t.idle_time_s / 60.0, 0) AS idle_mins, t.source FROM tracksolid.trips t JOIN tracksolid.devices d ON d.imei = t.imei WHERE t.start_time > now() - interval '24 hours' ORDER BY t.start_time DESC; ``` ### Speeding incidents — trips where max speed exceeded threshold ```sql -- Change 80 to your speed limit in km/h SELECT d.device_name, d.driver_name, t.start_time AT TIME ZONE 'Africa/Nairobi' AS trip_start, t.max_speed_kmh, ROUND(t.distance_km::numeric, 2) AS distance_km FROM tracksolid.trips t JOIN tracksolid.devices d ON d.imei = t.imei WHERE t.max_speed_kmh > 80 ORDER BY t.max_speed_kmh DESC; ``` ### After-hours trips (before 06:00 or after 20:00 EAT) ```sql SELECT d.device_name, d.driver_name, t.start_time AT TIME ZONE 'Africa/Nairobi' AS start_eat, ROUND(t.distance_km::numeric, 2) AS distance_km FROM tracksolid.trips t JOIN tracksolid.devices d ON d.imei = t.imei WHERE EXTRACT(HOUR FROM t.start_time AT TIME ZONE 'Africa/Nairobi') >= 20 OR EXTRACT(HOUR FROM t.start_time AT TIME ZONE 'Africa/Nairobi') < 6 ORDER BY t.start_time DESC; ``` ### Fleet utilisation rate per vehicle (today) ```sql SELECT d.device_name, d.driver_name, ROUND(SUM(t.driving_time_s) / 3600.0, 2) AS drive_hours, ROUND(SUM(t.idle_time_s) / 3600.0, 2) AS idle_hours, ROUND(SUM(t.driving_time_s) / (10.0 * 3600) * 100, 1) AS utilisation_pct FROM tracksolid.trips t JOIN tracksolid.devices d ON d.imei = t.imei WHERE t.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' GROUP BY d.device_name, d.driver_name ORDER BY utilisation_pct DESC; ``` ### Vehicles that did not move today ```sql SELECT d.device_name, d.mc_type, d.driver_name FROM tracksolid.devices d LEFT JOIN tracksolid.trips t ON t.imei = d.imei AND t.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' WHERE t.imei IS NULL ORDER BY d.device_name; ``` ### Distance per driver — last 30 days ```sql SELECT d.driver_name, COUNT(DISTINCT DATE(t.start_time AT TIME ZONE 'Africa/Nairobi')) AS active_days, COUNT(*) AS total_trips, ROUND(SUM(t.distance_km)::numeric, 0) AS total_km, ROUND(AVG(t.distance_km)::numeric, 1) AS avg_km_per_trip, MAX(t.max_speed_kmh) AS top_speed_ever FROM tracksolid.trips t JOIN tracksolid.devices d ON d.imei = t.imei WHERE t.start_time > now() - interval '30 days' AND d.driver_name IS NOT NULL AND d.driver_name != '' GROUP BY d.driver_name ORDER BY total_km DESC; ``` --- ## 6. Alarms ### All alarms — last 24 hours ```sql SELECT d.device_name, a.alarm_type, a.alarm_name, a.alarm_time AT TIME ZONE 'Africa/Nairobi' AS alarm_time_eat, ROUND(a.lat::numeric, 5) AS lat, ROUND(a.lng::numeric, 5) AS lng, a.speed, a.severity, a.acknowledged_at FROM tracksolid.alarms a JOIN tracksolid.devices d ON d.imei = a.imei WHERE a.alarm_time > now() - interval '24 hours' ORDER BY a.alarm_time DESC; ``` ### Alarm summary by type — last 7 days ```sql SELECT a.alarm_name, a.alarm_type, COUNT(*) AS occurrences, COUNT(DISTINCT a.imei) AS devices_affected, MAX(a.alarm_time AT TIME ZONE 'Africa/Nairobi') AS last_seen_eat FROM tracksolid.alarms a WHERE a.alarm_time > now() - interval '7 days' GROUP BY a.alarm_name, a.alarm_type ORDER BY occurrences DESC; ``` ### Unacknowledged alarms ```sql SELECT d.device_name, a.alarm_name, a.alarm_time AT TIME ZONE 'Africa/Nairobi' AS alarm_time_eat, ROUND(EXTRACT(EPOCH FROM (now() - a.alarm_time)) / 3600.0, 1) AS hours_open FROM tracksolid.alarms a JOIN tracksolid.devices d ON d.imei = a.imei WHERE a.acknowledged_at IS NULL ORDER BY a.alarm_time DESC; ``` ### Acknowledge an alarm ```sql UPDATE tracksolid.alarms SET acknowledged_at = now(), acknowledged_by = 'operator_name' WHERE id = ; ``` --- ## 7. Parking & Idle Time ### Parking events today ```sql SELECT d.device_name, pe.event_type, pe.start_time AT TIME ZONE 'Africa/Nairobi' AS start_eat, pe.end_time AT TIME ZONE 'Africa/Nairobi' AS end_eat, ROUND(pe.duration_seconds / 60.0, 0) AS duration_mins, pe.address FROM tracksolid.parking_events pe JOIN tracksolid.devices d ON d.imei = pe.imei WHERE pe.start_time >= CURRENT_DATE AT TIME ZONE 'Africa/Nairobi' ORDER BY pe.start_time DESC; ``` ### Longest idle periods — last 7 days ```sql SELECT d.device_name, d.driver_name, pe.start_time AT TIME ZONE 'Africa/Nairobi' AS idle_start, ROUND(pe.duration_seconds / 3600.0, 2) AS idle_hours, pe.address FROM tracksolid.parking_events pe JOIN tracksolid.devices d ON d.imei = pe.imei WHERE pe.start_time > now() - interval '7 days' ORDER BY pe.duration_seconds DESC LIMIT 20; ``` --- ## 8. Position History & Route Replay ### Position history by source — counts ```sql SELECT source, COUNT(*) AS fixes, MIN(gps_time AT TIME ZONE 'Africa/Nairobi') AS earliest, MAX(gps_time AT TIME ZONE 'Africa/Nairobi') AS latest FROM tracksolid.position_history GROUP BY source; ``` ### Route replay for a specific vehicle — last 24 hours ```sql -- Replace 'FRED KMGW 538W HULETI' with the device_name you want SELECT ph.gps_time AT TIME ZONE 'Africa/Nairobi' AS gps_time_eat, ROUND(ph.lat::numeric, 5) AS lat, ROUND(ph.lng::numeric, 5) AS lng, ph.speed, ph.direction, ph.acc_status, ph.source FROM tracksolid.position_history ph JOIN tracksolid.devices d ON d.imei = ph.imei WHERE d.device_name = 'FRED KMGW 538W HULETI' AND ph.gps_time > now() - interval '24 hours' ORDER BY ph.gps_time ASC; ``` ### Fix density per device — last 24 hours ```sql SELECT d.device_name, COUNT(*) AS total_fixes, COUNT(*) FILTER (WHERE ph.source = 'poll') AS poll_fixes, COUNT(*) FILTER (WHERE ph.source = 'track_list') AS track_list_fixes, MIN(ph.gps_time AT TIME ZONE 'Africa/Nairobi') AS first_fix, MAX(ph.gps_time AT TIME ZONE 'Africa/Nairobi') AS last_fix FROM tracksolid.position_history ph JOIN tracksolid.devices d ON d.imei = ph.imei WHERE ph.gps_time > now() - interval '24 hours' GROUP BY d.device_name ORDER BY total_fixes DESC; ``` --- ## 9. Ingestion Pipeline Health ### Pipeline health — last hour (key check) ```sql SELECT endpoint, COUNT(*) AS calls, SUM(rows_upserted) AS upserted, SUM(rows_inserted) AS inserted, ROUND(AVG(duration_ms)::numeric, 0) AS avg_ms, COUNT(*) FILTER (WHERE success = false) AS failures, MAX(run_at AT TIME ZONE 'Africa/Nairobi') AS last_call_eat FROM tracksolid.ingestion_log WHERE run_at > now() - interval '1 hour' GROUP BY endpoint ORDER BY calls DESC; ``` ### All-time ingestion summary ```sql SELECT endpoint, COUNT(*) AS total_calls, SUM(rows_upserted) AS total_upserted, SUM(rows_inserted) AS total_inserted, ROUND(AVG(duration_ms)::numeric, 0) AS avg_ms, COUNT(*) FILTER (WHERE success = false) AS failures, MIN(run_at AT TIME ZONE 'Africa/Nairobi') AS first_call, MAX(run_at AT TIME ZONE 'Africa/Nairobi') AS last_call FROM tracksolid.ingestion_log GROUP BY endpoint ORDER BY total_calls DESC; ``` ### Recent calls — last 20 entries ```sql SELECT run_at AT TIME ZONE 'Africa/Nairobi' AS run_eat, endpoint, imei_count, rows_upserted, rows_inserted, duration_ms, success, error_message FROM tracksolid.ingestion_log ORDER BY run_at DESC LIMIT 20; ``` ### Failed calls — all time ```sql SELECT run_at AT TIME ZONE 'Africa/Nairobi' AS run_eat, endpoint, error_code, error_message FROM tracksolid.ingestion_log WHERE success = false ORDER BY run_at DESC; ``` --- ## 10. Device & Fleet Registry ### Full fleet — all 63 devices ```sql SELECT device_name, mc_type, vehicle_number, driver_name, sim, ROUND(current_mileage_km::numeric, 0) AS odometer_km, expiration::date AS expires, enabled_flag FROM tracksolid.devices ORDER BY mc_type, device_name; ``` ### Fleet by model ```sql SELECT mc_type, COUNT(*) AS devices FROM tracksolid.devices GROUP BY mc_type ORDER BY devices DESC; ``` ### Odometer leaders — top 15 ```sql SELECT device_name, mc_type, sim, ROUND(current_mileage_km::numeric, 0) AS odometer_km, expiration::date AS expires FROM tracksolid.devices WHERE current_mileage_km IS NOT NULL AND current_mileage_km > 0 ORDER BY current_mileage_km DESC LIMIT 15; ``` ### Devices needing metadata (blank vehicle_number or driver_name) ```sql SELECT device_name, mc_type, sim FROM tracksolid.devices WHERE vehicle_number IS NULL OR vehicle_number = '' OR driver_name IS NULL OR driver_name = '' ORDER BY mc_type, device_name; ``` ### Subscription status breakdown ```sql SELECT COUNT(*) FILTER (WHERE expiration IS NULL) AS no_expiry_set, COUNT(*) FILTER (WHERE expiration < now()) AS already_expired, COUNT(*) FILTER (WHERE expiration BETWEEN now() AND now() + interval '90 days') AS expiring_90days, COUNT(*) FILTER (WHERE expiration > now() + interval '90 days') AS valid_long_term FROM tracksolid.devices; ``` ### Update vehicle metadata for a device ```sql UPDATE tracksolid.devices SET vehicle_number = 'KDA 123B', driver_name = 'John Kamau', vehicle_category = 'van' WHERE device_name = 'FRED KMGW 538W HULETI'; ``` --- ## 11. Schema & Migrations ### Check applied migrations ```sql SELECT filename, applied_at AT TIME ZONE 'Africa/Nairobi' AS applied_eat FROM tracksolid.schema_migrations ORDER BY applied_at; ``` ### List all tables in tracksolid schema ```sql SELECT table_name FROM information_schema.tables WHERE table_schema = 'tracksolid' ORDER BY table_name; ``` ### Row counts across all key tables ```sql SELECT (SELECT COUNT(*) FROM tracksolid.devices) AS devices, (SELECT COUNT(*) FROM tracksolid.live_positions) AS live_positions, (SELECT COUNT(*) FROM tracksolid.position_history) AS position_history, (SELECT COUNT(*) FROM tracksolid.trips) AS trips, (SELECT COUNT(*) FROM tracksolid.alarms) AS alarms, (SELECT COUNT(*) FROM tracksolid.parking_events) AS parking_events, (SELECT COUNT(*) FROM tracksolid.obd_readings) AS obd_readings, (SELECT COUNT(*) FROM tracksolid.device_events) AS device_events, (SELECT COUNT(*) FROM tracksolid.fuel_readings) AS fuel_readings, (SELECT COUNT(*) FROM tracksolid.temperature_readings) AS temperature_readings, (SELECT COUNT(*) FROM tracksolid.lbs_readings) AS lbs_readings, (SELECT COUNT(*) FROM tracksolid.ingestion_log) AS ingestion_log; ``` ### Describe a table ```bash # On the server tsdb -c "\d tracksolid.trips" tsdb -c "\d tracksolid.live_positions" ``` ### Run DWH gold ETL for yesterday ```sql SELECT dwh_gold.refresh_daily_metrics(CURRENT_DATE - 1); ``` ### Apply a migration manually (from your Mac) ```bash ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -i "$TS" psql -U postgres -d tracksolid_db' \ < 05_enhancement_migration.sql ``` --- ## 12. Container & Service Operations ### Find the current TimescaleDB container name ```bash ssh kianiadee@rahamafresh.com \ 'docker ps --filter "name=timescale_db" --format "{{.Names}}"' ``` ### List all running containers ```bash ssh kianiadee@rahamafresh.com 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"' ``` ### Check container logs (last 100 lines) ```bash # TimescaleDB ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker logs "$TS" --tail 100' # Ingest movement service ssh kianiadee@rahamafresh.com \ 'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) && docker logs "$SVC" --tail 100' # Ingest events service ssh kianiadee@rahamafresh.com \ 'SVC=$(docker ps --filter "name=ingest_events" --format "{{.Names}}" | head -1) && docker logs "$SVC" --tail 100' # Webhook receiver ssh kianiadee@rahamafresh.com \ 'SVC=$(docker ps --filter "name=webhook_receiver" --format "{{.Names}}" | head -1) && docker logs "$SVC" --tail 100' ``` ### Follow logs live (stream) ```bash ssh kianiadee@rahamafresh.com \ 'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) && docker logs "$SVC" --follow --tail 50' ``` ### Restart a service ```bash # Replace with: ingest_movement, ingest_events, webhook_receiver ssh kianiadee@rahamafresh.com \ 'SVC=$(docker ps --filter "name=ingest_movement" --format "{{.Names}}" | head -1) && docker restart "$SVC"' ``` ### Check disk space on the server ```bash ssh kianiadee@rahamafresh.com 'df -h /' ``` ### Check TimescaleDB chunk sizes (storage usage) ```sql SELECT hypertable_name, pg_size_pretty(SUM(total_bytes)) AS total_size FROM timescaledb_information.chunks GROUP BY hypertable_name ORDER BY SUM(total_bytes) DESC; ``` ### Vacuum / analyse a table (maintenance) ```bash ssh kianiadee@rahamafresh.com \ 'TS=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1) && docker exec -i "$TS" psql -U postgres -d tracksolid_db -c " VACUUM ANALYSE tracksolid.position_history;"' ``` --- ## 13. Quick Reference — Flag Meanings | Flag | Context | Meaning | |---|---|---| | `--filter "name=X"` | `docker ps` | Match containers whose name contains `X` | | `--format "{{.Names}}"` | `docker ps` | Output only the container name column | | `head -1` | shell | Take only the first result (guard against duplicates) | | `exec -i` | `docker exec` | Keep stdin open — required for piping SQL or running non-interactively | | `exec -it` | `docker exec` | Add a TTY — use only for interactive sessions, not when piping | | `-U postgres` | `psql` | Connect as the `postgres` superuser | | `-d tracksolid_db` | `psql` | Target this database | | `-c "..."` | `psql` | Run a single SQL statement and exit | | `-f file.sql` | `psql` | Execute all SQL in a file | | `AT TIME ZONE 'Africa/Nairobi'` | SQL | Convert UTC timestamp to EAT (UTC+3) for display | --- *Last updated: 2026-04-11*