https://github.com/burglekitt/gmt
Give Me Temporal! A long-due library to squash date and time bugs for good
https://github.com/burglekitt/gmt
biome eslint iso-8601 no-date oxlint tanstack-intent temporal
Last synced: about 1 month ago
JSON representation
Give Me Temporal! A long-due library to squash date and time bugs for good
- Host: GitHub
- URL: https://github.com/burglekitt/gmt
- Owner: burglekitt
- License: mit
- Created: 2026-03-10T07:54:12.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-05-10T11:50:02.000Z (about 2 months ago)
- Last Synced: 2026-05-10T13:30:15.620Z (about 2 months ago)
- Topics: biome, eslint, iso-8601, no-date, oxlint, tanstack-intent, temporal
- Language: TypeScript
- Homepage:
- Size: 634 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# GMT: Give Me Temporal!
Home of [@burglekitt/gmt](./packages/gmt) — **Give Me Temporal!**
A monorepo for Burglekitt community libraries, built with Nx, powered by pnpm, and dead serious about making JavaScript date handling not terrible.
## Install
Install the runtime package:
```bash
pnpm add @burglekitt/gmt
```
Quick example:
```js
import { getNow } from "@burglekitt/gmt";
console.log(getNow()); // ISO 8601 string
```
## Aint Nobody Got Time For JavaScript Date objects

We do not use JavaScript `Date` APIs in this monorepo.
- Aint nobody got time for `new Date()` mutability and environment drift.
- Aint nobody got time for `Date.parse()` guessing what you meant.
- Aint nobody got time for `Date.UTC()` argument gymnastics.
- Aint nobody got time for `Date.now()` scattered magic numbers.
Use GMT instead:
- `getNow()`, `getUnixNow()`, and `getUtcNow()` for current time values.
- `convertUtcDateTimeToUnix()` and `convertUtcToUnix()` for explicit unix conversion.
- `convertTimezoneToUtc()` and `convertUtcToTimezone()` for timezone-safe conversion.
- String-in/string-out APIs with Temporal under the hood for safer behavior.
If you see a Date API in code, replace it with a GMT helper.
## Packages
| Package | npm | Description |
|---|---|---|
| [`@burglekitt/gmt`](./packages/gmt) | `npm install @burglekitt/gmt` | Give Me Temporal — string-in/string-out date library |
`@burglekitt/gmt` currently exports top-level `Temporal`, `plain`, `zoned`, `unix`, `utc`, and `regex` namespaces, with direct subpath imports available under `@burglekitt/gmt/*`.
## Optional: Add Linting for Date API Bans
Want to ban `Date` APIs in your own project? GMT provides linting packages:
| Package | npm | Description |
|---|---|---|
| [`@burglekitt/gmt-biome`](./packages/gmt-biome) | `npm install -D @burglekitt/gmt-biome` | Shared Biome config — bans `Date` APIs via Grit plugins ([docs](./packages/gmt-biome/README.md)) |
| [`@burglekitt/gmt-eslint`](./packages/gmt-eslint) | `npm install -D @burglekitt/gmt-eslint` | Shared ESLint flat config — bans `Date` APIs ([docs](./packages/gmt-eslint/README.md)) |
| [`@burglekitt/gmt-oxlint`](./packages/gmt-oxlint) | `npm install -D @burglekitt/gmt-oxlint oxlint` | Shared Oxlint JS plugin — bans `Date` APIs ([docs](./packages/gmt-oxlint/README.md)) |
---
## Contributing
Quick start for contributors:
```bash
# Install all workspace dependencies
pnpm install
# Run tests across all packages
pnpm -w exec nx run-many -t test
# Build all packages
pnpm -w exec nx run-many -t build
# Lint and format
pnpm run lint
pnpm run format
```
### Project Structure
```
.
├── packages/
│ ├── gmt/ # @burglekitt/gmt — Give Me Temporal!
│ │ ├── src/
│ │ │ ├── plain/ # Timezone-free operations
│ │ │ │ ├── calculate/ # addDate, diffDateTime, subtractTime, ...
│ │ │ │ ├── compare/ # isAfterDate, isBeforeDate, areDatesEqual, ...
│ │ │ │ ├── format/ # formatDate, formatTime, formatDateTime, formatRelativeDate, ...
│ │ │ │ ├── get/ # getNow, getToday, getUnixNow, ...
│ │ │ │ ├── map/ # mapDaysInMonth, mapDatesInRange, ...
│ │ │ │ ├── parse/ # parseDateUnit, parseTimeUnit, ...
│ │ │ │ └── validate/ # isValidDate, unix validators, ...
│ │ │ ├── zoned/ # IANA timezone-aware operations
│ │ │ │ ├── calculate/ # addZoned, subtractZoned
│ │ │ │ ├── compare/ # isAfterZoned, isBeforeZoned, areZonedEqual
│ │ │ │ ├── convert/ # unix/utc/timezone conversion helpers
│ │ │ │ ├── format/ # formatZonedDateTime, formatZonedRange, formatRelativeZoned
│ │ │ │ ├── get/ # getZonedNow, getZonedToday, ...
│ │ │ │ ├── map/ # mapZonedHoursInDay, mapZonedDatesInRange
│ │ │ │ ├── parse/ # parseZonedDate, parseTimeFromZoned, ...
│ │ │ │ └── validate/ # isValidZonedDateTime, isValidTimezone
│ │ │ ├── unix/ # Unix epoch utilities
│ │ │ │ ├── calculate/ # addUnix, subtractUnix, diffUnix, ...
│ │ │ │ ├── convert/ # convertUnixToPlainDate, convertUnixToZoned, ...
│ │ │ │ ├── format/ # formatUnix, formatRelativeUnix
│ │ │ │ ├── get/ # getUnixNow, getUnixYear, ...
│ │ │ │ ├── parse/ # parseDateFromUnix, parseTimeFromUnix, ...
│ │ │ │ └── validate/ # isValidUnixSeconds, isValidUnixMilliseconds
│ │ │ ├── utc/ # UTC instant utilities
│ │ │ │ ├── calculate/ # addUtc, subtractUtc, diffUtc, ...
│ │ │ │ ├── chop/ # chopUtc
│ │ │ │ ├── convert/ # convertUtcToPlainDate, convertUtcToZoned, ...
│ │ │ │ ├── format/ # formatUtc, formatRelativeUtc
│ │ │ │ ├── get/ # getUtcNow, getUtcYear, ...
│ │ │ │ ├── parse/ # parseDateFromUtc, parseTimeFromUtc, ...
│ │ │ │ └── validate/ # isValidUtc
│ │ │ ├── regex/ # Composable regex patterns for date/time strings
│ │ │ └── package.json
│ ├── gmt-biome/ # @burglekitt/gmt-biome — Shared Biome config
│ │ ├── biome.json # Consumer-facing config (uses ./plugins/ paths)
│ │ └── plugins/ # Grit plugins banning Date APIs
│ └── gmt-eslint/ # @burglekitt/gmt-eslint — Shared ESLint flat config
│ └── eslint/
│ └── index.mjs # Flat config banning Date APIs
├── burglekitt/ # Nx workspace configuration (internal, do not publish)
├── biome.json # Root Biome config — references gmt-biome plugins directly
├── eslint.config.mjs # Root ESLint config — imports gmt-eslint
├── tsconfig.base.json # Shared TypeScript base config
└── package.json # Workspace root
```
### Workspace Scripts
Run from the root:
```bash
# Test, build, typecheck
pnpm -w exec nx run-many -t test
pnpm -w exec nx run-many -t build
pnpm -w exec nx run-many -t typecheck
# Code quality
pnpm run check
pnpm run lint
pnpm run format
# Nx utilities
pnpm -w exec nx graph # Visual dependency graph
pnpm -w exec nx sync # Sync TypeScript project references
```
### Nx Commands You Will Actually Use
As more packages are added under `packages/*`, these commands become the default workflow:
```bash
# Run targets for every package
pnpm run build
pnpm run test:nx
pnpm run lint:nx
pnpm run typecheck
# Full local gate before PR
pnpm run validate
# Only run on projects affected by your branch changes
pnpm run affected:build
pnpm run affected:test
pnpm run affected:lint
pnpm run affected:typecheck
# Workspace maintenance
pnpm run graph
pnpm run sync
pnpm run sync:check
pnpm run reset
```
Recommended PR flow:
```bash
pnpm run affected:lint
pnpm run affected:test
pnpm run affected:typecheck
pnpm run affected:build
```
CI strategy:
- Pull requests run `nx affected` targets (`lint`, `test`, `typecheck`, `build`) using `NX_BASE` and `NX_HEAD`.
- Pushes to `main` run full `nx run-many` across all projects.
- `defaultBase` is set to `main` in Nx config so local affected commands behave consistently.
Run within a specific package:
```bash
cd packages/gmt
pnpm run test
pnpm run build
pnpm run lint
```
### Code Quality
| Tool | Purpose |
|---|---|
| [Biome](https://biomejs.dev/) | Formatting and linting (+ Grit plugins for Date ban) |
| [TypeScript](https://www.typescriptlang.org/) | Type safety |
| [Vitest](https://vitest.dev/) | Testing |
| [Nx](https://nx.dev/) | Task orchestration and caching |
All Biome rules are in [biome.json](./biome.json) (Grit plugins live in [packages/gmt-biome/plugins/](./packages/gmt-biome/plugins/)).
### Releases
Pre-alpha. Each package follows semantic versioning and is published independently to npm.
#### Publishing (manual)
Publishing is manual only. We use [Changesets](https://github.com/changesets/changesets) to manage per-package versioning. Nothing publishes automatically — releases are triggered by maintainers.
Two supported publish paths:
- **Local publish (recommended):** run Changesets locally with your npm credentials (passkey). This gives maintainers direct control and creates git tags when publishing.
- **GitHub Actions (optional):** run the manual `Publish Package` workflow at `.github/workflows/publish.yml` via Actions → Run workflow. The workflow reads `NPM_TOKEN` from secrets and is gated by the `release` environment.
Prerequisites for Actions-based publishing (optional):
1. Create an npm access token with `Publish` permission for the `@burglekitt` org at https://www.npmjs.com/.
2. Add it as a repository secret named `NPM_TOKEN` (or add it to the `release` environment) in GitHub (`Settings → Secrets` / `Settings → Environments`).
Basic Changesets workflow:
- On your feature branch, run `pnpm run changeset:add` to record the change and desired bump.
- Merge the PR. If no `.changeset/*` files were merged, create changesets before versioning — Changesets only acts on files in `.changeset/`.
- On `main`, run `pnpm run changeset:version` to apply version bumps and update changelogs; commit and push those changes.
- To publish locally, run `pnpm run changeset:publish` from the repo root — this will publish packages and create package-scoped git tags.
- If you use the Actions workflow to publish, run the workflow, then run `pnpm exec changeset tag` locally and `git push --follow-tags` to synchronize tags (Actions publish does not create tags).
Notes:
- Prefer using Changesets rather than manually bumping `package.json`; manual bumps can be used but they bypass the Changesets workflow.
- Verify packages with `npm pack --dry-run` before publishing. For `@burglekitt/gmt`, run the dry-run after building.
- The `Publish Package` workflow will build `@burglekitt/gmt` automatically when publishing that package.
See [PUBLISHING.md](./PUBLISHING.md) for the full, step-by-step guide and examples.
---
## License
MIT — See [LICENSE](./LICENSE) for details.