mirror of
https://github.com/acamarata/temporal-hijri.git
synced 2026-06-30 19:04:29 +00:00
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:
parent
08793ca153
commit
a20f4d62c4
10 changed files with 39 additions and 30 deletions
3
.github/workflows/wiki-sync.yml
vendored
3
.github/workflows/wiki-sync.yml
vendored
|
|
@ -6,6 +6,9 @@ on:
|
|||
paths:
|
||||
- '.wiki/**'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync wiki to GitHub Wiki
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1 @@
|
|||
export type { HijriDate, CalendarEngine, ConversionOptions } from 'hijri-core';
|
||||
|
||||
export interface HijriCalendarOptions {
|
||||
calendar?: string;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue