{"id":48754171,"url":"https://github.com/brickfrog/tempo","last_synced_at":"2026-04-29T21:01:44.588Z","repository":{"id":347564672,"uuid":"1194421869","full_name":"brickfrog/tempo","owner":"brickfrog","description":"MoonBit date/time library — UTC-first, RFC 3339","archived":false,"fork":false,"pushed_at":"2026-04-12T21:30:10.000Z","size":112,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T00:23:51.556Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"MoonBit","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brickfrog.png","metadata":{"files":{"readme":"README.mbt.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-28T10:36:01.000Z","updated_at":"2026-04-12T21:30:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brickfrog/tempo","commit_stats":null,"previous_names":["brickfrog/tempo"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/brickfrog/tempo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Ftempo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Ftempo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Ftempo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Ftempo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brickfrog","download_url":"https://codeload.github.com/brickfrog/tempo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brickfrog%2Ftempo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32443576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T20:22:27.477Z","status":"ssl_error","status_checked_at":"2026-04-29T20:22:26.507Z","response_time":110,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-13T00:00:36.002Z","updated_at":"2026-04-29T21:01:44.583Z","avatar_url":"https://github.com/brickfrog.png","language":"MoonBit","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tempo\n\nUTC date/time library for MoonBit. RFC 3339 parsing, Unix timestamp conversion,\nbasic arithmetic. No external dependencies.\n\nIn your `moon.pkg`:\n\n```\nimport {\n  \"brickfrog/tempo/src\" @tempo,\n}\n```\n\n## Quick start\n\n```moonbit nocheck\n///|\ntest {\n  let dt = @tempo.DateTime::parse(\"2026-03-28T14:31:43Z\")\n  inspect(dt.date.year, content=\"2026\")\n  inspect(dt.date.month, content=\"3\")\n  inspect(dt.time.hour, content=\"14\")\n  inspect(dt.format(), content=\"2026-03-28T14:31:43Z\")\n}\n```\n\n## Types\n\n| Type | Description |\n|---|---|\n| `Date` | `year`, `month` (1–12), `day` (1–31) |\n| `Time` | `hour`, `minute`, `second`, `nanosecond` |\n| `DateTime` | Combined UTC date and time |\n| `Duration` | Signed duration, stored as nanoseconds |\n\nAll types implement `Eq`, `Compare`, and `Show`.\n\n## Constructing values\n\n```moonbit nocheck\n///|\ntest {\n  let d = @tempo.Date::new(2026, 3, 28)\n  let t = @tempo.Time::new(14, 31, 43, 0)\n  let dt = @tempo.DateTime::new(d, t)\n  inspect(dt.format(), content=\"2026-03-28T14:31:43Z\")\n}\n```\n\n```moonbit nocheck\n///|\ntest {\n  let dt = @tempo.DateTime::from_unix_seconds(0L)\n  inspect(dt.format(), content=\"1970-01-01T00:00:00Z\")\n\n  let dt2 = @tempo.DateTime::from_unix_nanos(1_000_000_000L)\n  inspect(dt2.format(), content=\"1970-01-01T00:00:01Z\")\n}\n```\n\n## Parsing\n\nAccepts RFC 3339 / ISO 8601. Only UTC offsets (`Z`, `+00:00`, `-00:00`) are\naccepted — others raise `TempoError`.\n\n```moonbit nocheck\n///|\ntest {\n  let dt = @tempo.DateTime::parse(\"2026-03-28T14:31:43.125Z\")\n  inspect(dt.time.nanosecond, content=\"125000000\")\n}\n```\n\n```moonbit nocheck\n///|\ntest {\n  let result = try {\n    @tempo.DateTime::parse(\"2026-03-28T14:31:43+09:00\") |\u003e ignore\n    \"ok\"\n  } catch {\n    @tempo.TempoError(_) =\u003e \"error\"\n  }\n  assert_eq(result, \"error\")\n}\n```\n\n## Formatting\n\n`DateTime::format` produces RFC 3339 with a `Z` suffix. Fractional seconds are\nincluded only when `nanosecond ≠ 0`, trailing zeros trimmed.\n\n```moonbit nocheck\n///|\ntest {\n  let dt = @tempo.DateTime::from_unix_nanos(1_711_630_303_100_000_000L)\n  inspect(dt.format(), content=\"2024-03-28T14:31:43.1Z\")\n}\n```\n\n## Arithmetic\n\n```moonbit nocheck\n///|\ntest {\n  let dt = @tempo.DateTime::parse(\"2026-03-28T12:00:00Z\")\n  let dt2 = dt.add(@tempo.Duration::hours(2L))\n  inspect(dt2.time.hour, content=\"14\")\n\n  let dt3 = dt.sub(@tempo.Duration::minutes(30L))\n  inspect(dt3.time.minute, content=\"30\")\n\n  let gap = dt2.diff(dt)\n  inspect(gap.as_hours(), content=\"2\")\n}\n```\n\n```moonbit nocheck\n///|\ntest {\n  let a = @tempo.Duration::hours(1L)\n  let b = @tempo.Duration::minutes(30L)\n  inspect((a + b).as_minutes(), content=\"90\")\n  inspect((-a).as_nanoseconds(), content=\"-3600000000000\")\n}\n```\n\n## Duration constructors\n\n```moonbit nocheck\n///|\ntest {\n  inspect(@tempo.Duration::weeks(1L).as_days(), content=\"7\")\n  inspect(@tempo.Duration::days(1L).as_hours(), content=\"24\")\n  inspect(@tempo.Duration::hours(1L).as_minutes(), content=\"60\")\n  inspect(@tempo.Duration::minutes(1L).as_seconds(), content=\"60\")\n  inspect(@tempo.Duration::seconds(1L).as_milliseconds(), content=\"1000\")\n  inspect(@tempo.Duration::milliseconds(1L).as_microseconds(), content=\"1000\")\n  inspect(@tempo.Duration::microseconds(1L).as_nanoseconds(), content=\"1000\")\n}\n```\n\n## Duration accessors\n\nAll accessors truncate toward zero (e.g. 90 minutes → 1 hour).\n\n```moonbit nocheck\n///|\ntest {\n  inspect(@tempo.Duration::days(3L).as_days(), content=\"3\")\n  inspect(@tempo.Duration::weeks(1L).as_weeks(), content=\"1\")\n  inspect(@tempo.Duration::days(7L).as_weeks(), content=\"1\")\n}\n```\n\n## Duration predicates\n\n```moonbit nocheck\n///|\ntest {\n  assert_eq(@tempo.Duration::seconds(0L).is_zero(), true)\n  assert_eq(@tempo.Duration::seconds(1L).is_zero(), false)\n  assert_eq(@tempo.Duration::seconds(-1L).is_negative(), true)\n  assert_eq(@tempo.Duration::seconds(1L).is_negative(), false)\n}\n```\n\n## Date arithmetic\n\n```moonbit nocheck\n///|\ntest {\n  let d = @tempo.Date::new(2024, 3, 15)\n\n  // Add/remove days\n  let d2 = d.add_days(10)\n  inspect(d2.day, content=\"25\")\n\n  // ISO weekday: Monday = 1 … Sunday = 7\n  assert_eq(d.day_of_week(), 5) // Friday\n\n  // Day of year (1-based)\n  assert_eq(d.day_of_year(), 75)\n\n  // Days between two dates\n  let other = @tempo.Date::new(2024, 3, 1)\n  assert_eq(d.days_until(other), -14)\n}\n```\n\n## Date and Time parsing/formatting\n\n```moonbit nocheck\n///|\ntest {\n  // Date-only parse/format\n  let d = @tempo.Date::parse(\"2024-03-15\")\n  inspect(d.format(), content=\"2024-03-15\")\n\n  // Time-only parse/format\n  let t = @tempo.Time::parse(\"14:31:43.500\")\n  inspect(t.format(), content=\"14:31:43.5\")\n}\n```\n\n## Current time\n\n```moonbit nocheck\n///|\ntest {\n  // Millisecond precision on js/wasm-gc, whole seconds on native.\n  let now = @tempo.DateTime::now()\n  assert_eq(now \u003e @tempo.DateTime::epoch(), true)\n}\n```\n\n## Calendar helpers\n\n```moonbit nocheck\n///|\ntest {\n  assert_eq(@tempo.is_leap_year(2000), true)\n  assert_eq(@tempo.is_leap_year(1900), false)\n  assert_eq(@tempo.is_leap_year(2024), true)\n  assert_eq(@tempo.days_in_month(2024, 2), 29)\n  assert_eq(@tempo.days_in_month(2023, 2), 28)\n}\n```\n\n## Not included\n\n- **Timezones / DST** — planned as a separate `tempo-tz` package\n- **Locale-aware formatting** — `strftime` patterns, localized names\n- **Leap seconds** — POSIX ignores them, so does tempo\n\nSee [ROADMAP.md](ROADMAP.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrickfrog%2Ftempo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrickfrog%2Ftempo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrickfrog%2Ftempo/lists"}