chore: CR/QA polish for v1.0.0 release

Fix documentation style (no em dashes). Update hijri-core devDep
from file: path to ^1.0.0. Add wiki-sync workflow permissions.
Tighten CalendarEngine interface usage; correct month range docs
to 1318-1500 AH throughout.
This commit is contained in:
Aric Camarata 2026-02-25 15:11:28 -05:00
parent 08793ca153
commit a20f4d62c4
10 changed files with 39 additions and 30 deletions

View file

@ -6,6 +6,9 @@ on:
paths:
- '.wiki/**'
permissions:
contents: write
jobs:
sync:
name: Sync wiki to GitHub Wiki

View file

@ -4,7 +4,7 @@
The TC39 Temporal proposal (Stage 3, actively shipping in browsers) replaces the legacy `Date` object with a family of types: `PlainDate`, `PlainTime`, `PlainDateTime`, `ZonedDateTime`, `Instant`, and more. Central to its design is an extensible calendar system.
A `Temporal.PlainDate` always stores its fields (year, month, day) in terms of a specific calendar. The ISO 8601 (Gregorian) calendar is the default. Custom calendars implement an interface — informally called the Temporal Calendar Protocol — that lets the `PlainDate` compute calendar-specific values from those fields, perform arithmetic, and convert between calendar systems.
A `Temporal.PlainDate` always stores its fields (year, month, day) in terms of a specific calendar. The ISO 8601 (Gregorian) calendar is the default. Custom calendars implement an interface (informally called the Temporal Calendar Protocol) that lets the `PlainDate` compute calendar-specific values from those fields, perform arithmetic, and convert between calendar systems.
`temporal-hijri` implements this protocol for the Hijri (Islamic) calendar.
@ -26,7 +26,7 @@ mergeFields(fields, additionalFields)
toString()
```
`HijriCalendar` implements all of these. The key challenge is that `Temporal.PlainDate` stores ISO coordinates (Gregorian year/month/day), while the methods must return Hijri coordinates — and `dateFromFields` must do the reverse.
`HijriCalendar` implements all of these. The key challenge is that `Temporal.PlainDate` stores ISO coordinates (Gregorian year/month/day), while the methods must return Hijri coordinates. `dateFromFields` does the reverse.
## Coordinate Bridging
@ -49,7 +49,7 @@ The inverse path:
### Date object construction
The UAQ engine reads local date components (`getFullYear`, `getMonth`, `getDate`). To ensure the local date always matches the intended calendar date — regardless of the host's timezone — `toHijri()` uses the local `Date` constructor: `new Date(year, month - 1, day)`. This avoids the UTC-to-local shift that would occur with `Date.UTC`.
The UAQ engine reads local date components (`getFullYear`, `getMonth`, `getDate`). To ensure the local date always matches the intended calendar date regardless of the host's timezone, `toHijri()` uses the local `Date` constructor: `new Date(year, month - 1, day)`. This avoids the UTC-to-local shift that would occur with `Date.UTC`.
The FCNA engine reads UTC components for its astronomical calculations. The UTC-local discrepancy is at most one day, which falls within the tolerance of FCNA's calculation window.
@ -57,7 +57,7 @@ The FCNA engine reads UTC components for its astronomical calculations. The UTC-
Adding a duration to a Hijri date requires different handling for different duration components:
- **Years and months** must be applied in Hijri space. Adding "1 month" to 1 Ramadan should yield 1 Shawwal not a fixed 30-day offset. The Hijri calendar has months of 29 and 30 days in no fixed pattern, so month arithmetic must account for actual month lengths.
- **Years and months** must be applied in Hijri space. Adding "1 month" to 1 Ramadan should yield 1 Shawwal, not a fixed 30-day offset. The Hijri calendar has months of 29 and 30 days in no fixed pattern, so month arithmetic must account for actual month lengths.
- **Days and weeks** can be applied in ISO (Gregorian) space after the Hijri-space year/month addition. Adding 7 days means exactly 7 days, and ISO arithmetic handles that correctly.
@ -72,7 +72,7 @@ The implementation:
6. Apply the day and week delta with ISO PlainDate.add().
```
Clamping (step 4) follows the Temporal specification's "constrain" overflow behavior. Adding 1 month to 30 Rajab (a 30-day month) where Shaban is 29 days would yield 30 Shaban — instead, the result is clamped to 29 Shaban.
Clamping (step 4) follows the Temporal specification's "constrain" overflow behavior. Adding 1 month to 30 Rajab (a 30-day month) where Shaban is 29 days would yield 30 Shaban. The result is clamped to 29 Shaban.
## dateUntil: Difference Strategy
@ -107,7 +107,7 @@ hijri-core provides:
- Built-in UAQ engine (table-driven, Hijri years 1318-1500)
- Built-in FCNA engine (Meeus Chapter 49 astronomical calculations)
`temporal-hijri` is a pure adapter layer. It does not implement any calendar arithmetic itself — it translates between the Temporal protocol and hijri-core's engine interface.
`temporal-hijri` is a pure adapter layer. It does not implement any calendar arithmetic itself. It translates between the Temporal protocol and hijri-core's engine interface.
## Build Output

View file

@ -6,15 +6,15 @@ This package provides `UaqCalendar` and `FcnaCalendar` as plug-in calendar objec
## Pages
- [Home](Home) you are here
- [API Reference](API-Reference) full method signatures and return types
- [Architecture](Architecture) design decisions, protocol internals, arithmetic strategy
- [Home](Home): you are here
- [API Reference](API-Reference): full method signatures and return types
- [Architecture](Architecture): design decisions, protocol internals, arithmetic strategy
## Quick links
- [GitHub repository](https://github.com/acamarata/temporal-hijri)
- [npm package](https://www.npmjs.com/package/temporal-hijri)
- [hijri-core](https://github.com/acamarata/hijri-core) the underlying calendar engine
- [hijri-core](https://github.com/acamarata/hijri-core): the underlying calendar engine
## Calendar systems
@ -27,7 +27,7 @@ This package provides `UaqCalendar` and `FcnaCalendar` as plug-in calendar objec
- Node.js 20+
- `hijri-core ^1.0.0` (peer dependency)
- `@js-temporal/polyfill ^0.4.0` (optional peer dependency needed if native `Temporal` is unavailable)
- `@js-temporal/polyfill ^0.4.0` (optional peer dependency, needed if native `Temporal` is unavailable)
---

View file

@ -10,8 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- `HijriCalendar` base class implementing the TC39 Temporal Calendar Protocol
- `UaqCalendar` Umm al-Qura calendar (table-driven, 1318-1500 AH coverage)
- `FcnaCalendar` FCNA/ISNA calendar (astronomical new moon calculation via Meeus)
- `UaqCalendar`: Umm al-Qura calendar (table-driven, 1318-1500 AH coverage)
- `FcnaCalendar`: FCNA/ISNA calendar (astronomical new moon calculation via Meeus)
- `uaqCalendar` and `fcnaCalendar` convenience singletons
- Full Temporal protocol: `year`, `month`, `monthCode`, `day`, `daysInMonth`, `daysInYear`, `monthsInYear`, `inLeapYear`, `dayOfWeek`, `dayOfYear`, `weekOfYear`, `daysInWeek`, `dateFromFields`, `yearMonthFromFields`, `monthDayFromFields`, `dateAdd`, `dateUntil`, `mergeFields`, `toString`
- Dual CJS and ESM builds with TypeScript declarations

View file

@ -175,7 +175,7 @@ The package ships dual CJS/ESM builds with full `.d.ts` and `.d.mts` declaration
- Node.js 20, 22, 24
- Any bundler supporting `exports` field (`Vite`, `Webpack 5`, `Rollup`, `esbuild`)
- ESM (`import`) and CommonJS (`require`) both provided
- ESM (`import`) and CommonJS (`require`): both provided
- No native `Temporal` required: works entirely with `@js-temporal/polyfill`
---
@ -188,9 +188,9 @@ Full reference, architecture notes, and algorithmic detail in the [wiki](https:/
## Related
- [hijri-core](https://github.com/acamarata/hijri-core) — the zero-dependency Hijri engine powering this package
- [luxon-hijri](https://github.com/acamarata/luxon-hijri) Hijri/Gregorian conversion for Luxon
- [pray-calc](https://github.com/acamarata/pray-calc) Islamic prayer times
- [hijri-core](https://github.com/acamarata/hijri-core): zero-dependency Hijri engine powering this package
- [luxon-hijri](https://github.com/acamarata/luxon-hijri): Hijri/Gregorian conversion for Luxon
- [pray-calc](https://github.com/acamarata/pray-calc): Islamic prayer times
---

View file

@ -56,7 +56,7 @@
"devDependencies": {
"@js-temporal/polyfill": "^0.4.4",
"@types/node": "^22.0.0",
"hijri-core": "file:../hijri-core",
"hijri-core": "^1.0.0",
"tsup": "^8.0.0",
"typescript": "^5.5.0"
},

View file

@ -15,8 +15,8 @@ importers:
specifier: ^22.0.0
version: 22.19.11
hijri-core:
specifier: file:../hijri-core
version: file:../hijri-core
specifier: ^1.0.0
version: 1.0.0
tsup:
specifier: ^8.0.0
version: 8.5.1(typescript@5.9.3)
@ -407,8 +407,8 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
hijri-core@file:../hijri-core:
resolution: {directory: ../hijri-core, type: directory}
hijri-core@1.0.0:
resolution: {integrity: sha512-wImBZLBKbEWEEUE1nrc1CFY/uvx4XjGNWYChImJZlswXIVhrBCzSVaj6DP1AU2gUMJ6KDh2ygXo/u/Qx232CXA==}
engines: {node: '>=20'}
joycon@3.1.1:
@ -803,7 +803,7 @@ snapshots:
fsevents@2.3.3:
optional: true
hijri-core@file:../hijri-core: {}
hijri-core@1.0.0: {}
joycon@3.1.1: {}

View file

@ -1,7 +1,7 @@
import { Temporal } from '@js-temporal/polyfill';
import type { CalendarEngine } from 'hijri-core';
type DateUnit = 'year' | 'years' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days' | 'auto';
type DateUnit = 'year' | 'years' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days';
/**
* Base class implementing the TC39 Temporal Calendar Protocol for Hijri calendars.
@ -146,6 +146,7 @@ export class HijriCalendar {
return total;
}
/** Hijri week number counted from day 1 of Muharram (day 1-7 = week 1). No ISO week alignment. */
weekOfYear(date: Temporal.PlainDate): number {
return Math.ceil(this.dayOfYear(date) / 7);
}
@ -163,6 +164,11 @@ export class HijriCalendar {
return this.fromHijri(fields.year, fields.month, fields.day);
}
/**
* ISO-anchored PlainYearMonth per the Temporal Calendar Protocol.
* The resulting PlainYearMonth stores ISO coordinates internally, representing
* the Hijri month that starts on that ISO year/month.
*/
yearMonthFromFields(
fields: { year: number; month: number },
_options?: { overflow?: 'constrain' | 'reject' }
@ -174,6 +180,11 @@ export class HijriCalendar {
});
}
/**
* ISO-anchored PlainMonthDay per the Temporal Calendar Protocol.
* Reference year 1444 is intentional: it is a recent, well-covered UAQ year
* used to anchor the ISO coordinates when no year is supplied.
*/
monthDayFromFields(
fields: { month: number; day: number; year?: number },
_options?: { overflow?: 'constrain' | 'reject' }

View file

@ -3,9 +3,8 @@ export { UaqCalendar } from './calendars/UaqCalendar';
export { FcnaCalendar } from './calendars/FcnaCalendar';
export type { HijriDate, CalendarEngine, ConversionOptions } from 'hijri-core';
export type { HijriCalendarOptions } from './types';
// Convenience singletons — ready to use without instantiation.
// Pre-built singletons. Import and use directly; no need to instantiate.
import { UaqCalendar } from './calendars/UaqCalendar';
import { FcnaCalendar } from './calendars/FcnaCalendar';

View file

@ -1,5 +1 @@
export type { HijriDate, CalendarEngine, ConversionOptions } from 'hijri-core';
export interface HijriCalendarOptions {
calendar?: string;
}