Skip to content

koke1997/timenow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

114 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

TimeNow

CI

🌐 Homepage: https://koke1997.github.io/timenow/

GitHub Pages setup: Go to Settings → Pages → Source: GitHub Actions to enable deployment.

The 1884 problem: Railroads needed trains to run on schedule. To prevent collisions on single-track lines, 80+ local times were collapsed into 4 zones in one afternoon — November 18, 1883. That political fix is still running your clock.

What we are building: Every point on Earth can now calculate its own true solar time from GPS coordinates and an accurate clock. No zones. No DST. 12:00:00 means the sun is at its highest — everywhere, always, honestly. The API also quantifies social jetlag (the gap between biological and civil time) and derives prayer times (Islamic, Jewish, Christian) from solar geometry — not lookup tables.

Two deployment targets:

  • IoT device — runs at a fixed location, reads GPS, calculates solar time, shares it with nearby devices. A network of these makes time zones obsolete.
  • Web — any browser, any coordinate, any historical or future instant. Explore, compare, understand how far your civil clock diverges from your sun.

The problem in numbers

For any location, the structural offset between civil time and solar time is:

mean_offset_min = UTC_offset_hours × 60 − longitude_deg × 4

This is stable year-round. The Equation of Time (Earth's orbital eccentricity + axial tilt) adds ±16 minutes of seasonal variation on top. The API field civil_solar_offset_min returns the full value at any instant.

Selected deviations (verifiable with GET /solar):

Location Longitude Civil UTC Mean solar UTC Mean offset
Kashgar, Xinjiang 76.0°E +8 +5.07 +2 h 56 m
Urumqi, Xinjiang 87.6°E +8 +5.84 +2 h 09 m
Shanghai 121.5°E +8 +8.10 −6 m (aligned)
Vladivostok 131.9°E +10 +8.79 +1 h 12 m
Madrid (winter) 3.7°W +1 −0.25 +1 h 14 m
Madrid (summer) 3.7°W +2 −0.25 +2 h 14 m
Trans-Siberian (Vladivostok end) 131.9°E Moscow +7 +8.79 solar noon ≈ 02:30 Moscow time on ticket

The Kashgar–Shanghai pair is the starkest: both cities share UTC+8, yet their mean solar offsets differ by 3 hours and 2 minutes. The civil clock cannot represent this difference — not approximately, not with DST, not ever.


Why this matters

Health

Civil-solar misalignment is the structural component of "social jetlag" — the difference between your biological clock and your civil clock. Peer-reviewed findings:

Study Finding
Giuntella & Mazzonna, J. Health Economics (2019) US counties on western timezone edges: higher obesity, diabetes, shorter sleep — using timezone borders as natural experiment
Roenneberg et al., Nature Reviews Endocrinology (2023) Social jetlag associated with obesity, type 2 diabetes, tobacco/alcohol consumption
Holz et al., Sleep 46(12) (2023), n=6,335 adolescents Higher social jetlag → lower crystallized intelligence, worse school grades, altered brain connectivity
Qian et al., European J. Medical Research (2026) Social jetlag associated with higher 10-year Framingham cardiovascular risk

Transport

Civil timezone boundaries change the clock in integer-hour steps at political lines. Solar time changes continuously at 4 minutes per degree of longitude. Any vehicle moving east or west accumulates solar displacement that no civil clock tracks until the next zone boundary.

Eastbound travel shortens the solar day (moving against the Sun). Westbound lengthens it. Aviation already accounts for this: FAA 14 CFR Part 117 requires stricter crew rest for eastbound flights due to circadian desynchronisation. Railways, ships, and long-haul trucking have the same physics.

Religious practice

~2 billion people observe prayer times defined entirely by solar position: Islamic Fajr–Isha, Jewish Shacharit–Ma'ariv, and Christian canonical hours. Civil zones shift when these obligations fall — sometimes by hours. GET /solar/prayer returns prayer windows from solar geometry, not lookup tables.

The coordination problem

Solar time does not replace UTC. UTC is the coordination layer — trains, flights, international calls. Solar time is the personal biological layer. A device shows both: "Solar noon (UTC 11:47)". Every person on Earth knows when UTC 11:47 is for them. No zone conversion needed.


Quick start

Option A — Podman / Docker (no Scala toolchain needed)

# Clone and start
git clone https://github.com/koke1997/timenow.git && cd timenow
podman-compose up --build      # or: docker compose up --build

Open http://localhost:8090 — the web UI, deviation map, and all API endpoints are ready.

The first build downloads dependencies and compiles (~3–5 min). Subsequent starts use the image cache.

IoT mode with fixed GPS coordinates (no serial GPS device required):

TIMENOW_PROFILE=iot TIMENOW_GPS_COORDS="51.5074,-0.1278" podman-compose up

IoT mode with a real NMEA serial device — add to podman-compose.yml:

devices:
  - /dev/ttyUSB0:/dev/ttyUSB0
environment:
  - TIMENOW_GPS_PORT=/dev/ttyUSB0

IERS / Orekit EOP data is persisted in a named volume (orekit-data) and refreshed automatically every 6 hours.


Option B — Run from source

Requires: JDK 17+, sbt 1.x on PATH.

./start.sh          # auto-detects a free port starting at 8090

Or:

cd backend && sbt run

Open http://localhost:8090 for the web UI, /globe.html for the world deviation map, /docs for the API schema.

Developer commands (Makefile)

make dev        # hot-reload backend on every save (~2 s cycle)
make test       # all backend tests
make fmt        # format all Scala sources
make check      # format-check (CI-safe)
make playwright # E2E smoke tests
make health     # curl /health pretty-printed

Offline geo data

The server starts with 198 bundled cities. Additional data loads on demand:

curl "http://localhost:8090/geo/fetch?cc=DE"        # Germany cities (~1–2 MB)
curl "http://localhost:8090/geo/fetch?type=geoip"   # IP geolocation (~7 MB)
curl "http://localhost:8090/geo/status"             # what is loaded

All data is cached in ~/.timenow/data/ and survives restarts.


API

GET /docs returns the full OpenAPI 3.0 schema.

Core

Endpoint Params Description
GET /solar lat, lng[, dt] Solar time, sun position, day boundaries, civil offset
GET /world/deviation-map 2° global grid: [lat, lng, deviation_hours]
GET /health Server status, IERS EOP freshness
GET /geocode q= City search
GET /locate IP geolocation
GET /geo/status · GET /geo/fetch cc= Offline geo data status / load

Extended solar

Endpoint Params Description
GET /reform lat, lng Honest UTC offset; clock reform calculator
GET /solar/prayer lat, lng, date Prayer times (Islamic/Jewish/Christian) from solar position
GET /solar/jetlag lat, lng Quantified social jetlag score and health risk level
GET /solar/almanac lat, lng, year Solstices, equinoxes, EoT extremes for the year
GET /solar/timetable lat1, lng1, lat2, lng2, depart_utc Solar time journey for travel
GET /solar/ical lat, lng, year RFC 5545 iCalendar feed
GET /share lat, lng Shareable social card with OG meta tags
GET /analemma lat, lng, year 365 sun positions (figure-8 visualization)

IoT / mesh / discovery

Endpoint Params Description
GET /iot/stream SSE live feed, 1 s cadence
GET /mesh/solar lat, lng Peer mesh solar comparison
GET /mesh/coverage Peer coverage map
GET /.well-known/solar-time RFC 9557 IXDTF discovery — dut1_sec, tai_utc_offset_sec, node_public_key, signature

Example — GET /solar

{
  "solar_time": {
    "local_solar_time": "13:51:04",
    "solar_noon_utc": "2026-04-13T12:00:52Z",
    "equation_of_time_min": -0.47
  },
  "sun_position": {
    "elevation_deg": 41.97,
    "azimuth_deg": 218.5,
    "distance_au": 1.0027,
    "declination_deg": 9.18
  },
  "day": {
    "sunrise_utc": "...", "sunset_utc": "...",
    "day_length_hours": 13.75,
    "is_polar_day": false, "is_polar_night": false
  },
  "civil_time": {
    "timezone": "Europe/London",
    "utc_offset_hours": 1.0,
    "civil_time_local": "14:51:04",
    "civil_solar_offset_min": 60.87
  }
}

Precision

Layer Method Accuracy
Sun position NREL SPA — VSOP87 truncated series (Reda & Andreas 2004) ±0.0003°
ΔT (TT − UT1) Orekit EOP from IERS finals2000A.all < 0.1 s
Earth rotation Orekit IAU 2006 precession + IAU 2000B nutation sub-arcsecond
IERS EOP finals2000A.all from USNO, refreshed weekly published values
Timezone offset timeshape polygon lookup, tzdata 2025b exact IANA tz
Atmospheric refraction Bennett (1982) < 0.1 arcmin

Architecture

timenow/
├── backend/              Scala 3 — calculation engine + HTTP API
│   └── src/main/scala/solar/
│       ├── calc/         Pure math (no IO): NREL SPA, VSOP87, EoT, refraction
│       ├── domain/       Opaque units: Degrees, AU, Minutes — zero runtime cost
│       ├── service/      IO orchestration: SolarService, DeviationMapService
│       ├── infra/        Orekit init, IERS download, timezone lookup, port scan
│       └── api/          HTTP routes + Circe encoders — thin, no logic
├── frontend/             HTML/JS — no framework
│   ├── index.html        Solar clock, city search, EoT badge, civil offset
│   ├── globe.html        World deviation map (Leaflet + OpenStreetMap tiles)
│   ├── stories.html      Case studies: Spain, Xinjiang, Trans-Siberian, health
│   ├── prayer.html       Prayer times across Islamic/Jewish/Christian traditions
│   └── timetable.html    Solar time journey for trains/flights
├── agent/                A2A device-to-device protocol (in progress)
├── llm/                  Solar-time knowledge distillation (research)
└── tests/                Playwright E2E smoke tests

The backend is a self-contained JAR (sbt assembly). It runs on a Raspberry Pi. It works offline after the first IERS data sync. This is intentional — IoT deployment requires no cloud dependency.

Container image: A Containerfile (OCI/Podman-compatible) and podman-compose.yml are included for zero-toolchain deployment. See Quick start above.


Tests

cd backend && sbt test       # 54 tests
npm test                     # Playwright E2E tests (requires backend running)

Backend test suites:

  • JulianCalendarSuite — JD/century conversions against NOAA reference
  • SunParamsSuite — NOAA orbital geometry
  • SolarTimeSuite — Equation of Time, true solar time
  • DayBoundariesSuite — sunrise/sunset/polar, Boulder CO, Svalbard
  • SolarPositionSuite — elevation, azimuth, refraction
  • SpaSuite — NREL SPA vs NREL TP-560-34302 reference (Oct 17 2003, ΔT=67s)
  • RoutesSuite — HTTP integration tests (health, solar, deviation-map, docs)

Stack

Component Library Version
Language Scala 3 3.7.4
HTTP http4s Ember 0.23.30
Effects Cats Effect 3.5.7
JSON Circe 0.14.10
Astrodynamics Orekit 13.1.4
Timezone DB timeshape 2025b.26
Geo data GeoIP2 Java 4.2.0
Tests munit + munit-cats-effect 1.0.4 / 2.0.0
Map Leaflet.js 1.9.4
E2E Playwright latest
Container Podman / Docker (OCI) Containerfile

Standards and the gap

See docs/rfcs.md for a full analysis of the existing IETF standards for time (RFC 3339, RFC 9557/IXDTF, RFC 7808/TZDIST, RFC 9636/TZif, RFC 5905/NTPv4) and the gap that TimeNow fills: no RFC exists for solar time representation, calculation, or distribution.

The docs/draft-timenow-solar-time-ixdtf-00.md document is an Internet-Draft proposing registration of solar as an IXDTF extension key (RFC 9557 §3.2). A full RFC 7991 xml2rfc XML version is at docs/draft-timenow-solar-time-ixdtf-00.xml, ready for IETF Datatracker submission (see docs/IETF_SUBMISSION.md). The format:

2026-04-13T12:00:52Z[Europe/London][solar/51.5000,-0.1000]

UTC is authoritative. The [solar/...] suffix is an elective annotation that any receiver not implementing this spec MUST ignore (per RFC 9557 §3.3).

The GET /.well-known/solar-time endpoint is the IoT discovery URL that the draft normalises. It exposes dut1_sec, tai_utc_offset_sec, node_public_key, and a signature so constrained devices can apply IERS corrections and verify peer identity without downloading finals2000A.all.


References

Solar algorithms

Source Used for
NOAA Solar Calculation Details GMST polynomial: mean longitude, anomaly, EoT, sunrise/sunset hour angle
NREL SPA — Reda & Andreas (2004) VSOP87 truncated series; ±0.0003° — current Sun position engine
VSOP87 — Bretagnon & Francou (1988) Earth heliocentric position theory
Jean Meeus — Astronomical Algorithms, 2nd ed. (1998) JD/JC, nutation, orbital terms
Bennett (1982) — atmospheric refraction Refraction correction; < 0.1 arcmin for elevations > −0.575°
IAU SOFA Reference for IAU 2006 precession and IAU 2000B nutation
JPL Horizons Independent validation: sub-arcsecond Sun ephemeris

Time scales & Earth orientation

Source Used for
IERS Conventions 2010 (TN 36) IAU 2006 precession + IAU 2000B nutation; UT1–UTC definition
IERS finals2000A.all — USNO Published UT1–UTC, polar motion; fetched weekly
USNO tai-utc.dat Leap-second history

Libraries

Library Role
Orekit 13.1.4 IAU 2006/2000B, IERS EOP, UTC/UT1/TT time scales, ITRF→TOD
timeshape 2025b Timezone polygon lookup
OpenStreetMap Map basemap + country borders (© contributors, ODbL)
ColorBrewer RdBu Diverging colormap for deviation overlay
GeoNames City database, CC BY 4.0
MaxMind GeoLite2 IP geolocation, CC BY-SA 4.0

Health & social jetlag

Transport & timekeeping history

About

True local solar time for any place on Earth. Solves the 1884 railroad time-zone problem. IoT (Raspberry Pi + GPS) + web. NREL SPA · IERS EOP · RFC 9557 IXDTF draft.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors