mirror of
https://github.com/acamarata/temporal-hijri.git
synced 2026-07-02 03:40:42 +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:
|
paths:
|
||||||
- '.wiki/**'
|
- '.wiki/**'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync:
|
sync:
|
||||||
name: Sync wiki to GitHub Wiki
|
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.
|
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.
|
`temporal-hijri` implements this protocol for the Hijri (Islamic) calendar.
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ mergeFields(fields, additionalFields)
|
||||||
toString()
|
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
|
## Coordinate Bridging
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ The inverse path:
|
||||||
|
|
||||||
### Date object construction
|
### 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.
|
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:
|
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.
|
- **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().
|
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
|
## dateUntil: Difference Strategy
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ hijri-core provides:
|
||||||
- Built-in UAQ engine (table-driven, Hijri years 1318-1500)
|
- Built-in UAQ engine (table-driven, Hijri years 1318-1500)
|
||||||
- Built-in FCNA engine (Meeus Chapter 49 astronomical calculations)
|
- 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
|
## Build Output
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,15 @@ This package provides `UaqCalendar` and `FcnaCalendar` as plug-in calendar objec
|
||||||
|
|
||||||
## Pages
|
## Pages
|
||||||
|
|
||||||
- [Home](Home) — you are here
|
- [Home](Home): you are here
|
||||||
- [API Reference](API-Reference) — full method signatures and return types
|
- [API Reference](API-Reference): full method signatures and return types
|
||||||
- [Architecture](Architecture) — design decisions, protocol internals, arithmetic strategy
|
- [Architecture](Architecture): design decisions, protocol internals, arithmetic strategy
|
||||||
|
|
||||||
## Quick links
|
## Quick links
|
||||||
|
|
||||||
- [GitHub repository](https://github.com/acamarata/temporal-hijri)
|
- [GitHub repository](https://github.com/acamarata/temporal-hijri)
|
||||||
- [npm package](https://www.npmjs.com/package/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
|
## Calendar systems
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ This package provides `UaqCalendar` and `FcnaCalendar` as plug-in calendar objec
|
||||||
|
|
||||||
- Node.js 20+
|
- Node.js 20+
|
||||||
- `hijri-core ^1.0.0` (peer dependency)
|
- `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
|
### Added
|
||||||
|
|
||||||
- `HijriCalendar` base class implementing the TC39 Temporal Calendar Protocol
|
- `HijriCalendar` base class implementing the TC39 Temporal Calendar Protocol
|
||||||
- `UaqCalendar` — Umm al-Qura calendar (table-driven, 1318-1500 AH coverage)
|
- `UaqCalendar`: Umm al-Qura calendar (table-driven, 1318-1500 AH coverage)
|
||||||
- `FcnaCalendar` — FCNA/ISNA calendar (astronomical new moon calculation via Meeus)
|
- `FcnaCalendar`: FCNA/ISNA calendar (astronomical new moon calculation via Meeus)
|
||||||
- `uaqCalendar` and `fcnaCalendar` convenience singletons
|
- `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`
|
- 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
|
- 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
|
- Node.js 20, 22, 24
|
||||||
- Any bundler supporting `exports` field (`Vite`, `Webpack 5`, `Rollup`, `esbuild`)
|
- 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`
|
- 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
|
## Related
|
||||||
|
|
||||||
- [hijri-core](https://github.com/acamarata/hijri-core) — the zero-dependency Hijri engine powering this package
|
- [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
|
- [luxon-hijri](https://github.com/acamarata/luxon-hijri): Hijri/Gregorian conversion for Luxon
|
||||||
- [pray-calc](https://github.com/acamarata/pray-calc) — Islamic prayer times
|
- [pray-calc](https://github.com/acamarata/pray-calc): Islamic prayer times
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@js-temporal/polyfill": "^0.4.4",
|
"@js-temporal/polyfill": "^0.4.4",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"hijri-core": "file:../hijri-core",
|
"hijri-core": "^1.0.0",
|
||||||
"tsup": "^8.0.0",
|
"tsup": "^8.0.0",
|
||||||
"typescript": "^5.5.0"
|
"typescript": "^5.5.0"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ importers:
|
||||||
specifier: ^22.0.0
|
specifier: ^22.0.0
|
||||||
version: 22.19.11
|
version: 22.19.11
|
||||||
hijri-core:
|
hijri-core:
|
||||||
specifier: file:../hijri-core
|
specifier: ^1.0.0
|
||||||
version: file:../hijri-core
|
version: 1.0.0
|
||||||
tsup:
|
tsup:
|
||||||
specifier: ^8.0.0
|
specifier: ^8.0.0
|
||||||
version: 8.5.1(typescript@5.9.3)
|
version: 8.5.1(typescript@5.9.3)
|
||||||
|
|
@ -407,8 +407,8 @@ packages:
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
hijri-core@file:../hijri-core:
|
hijri-core@1.0.0:
|
||||||
resolution: {directory: ../hijri-core, type: directory}
|
resolution: {integrity: sha512-wImBZLBKbEWEEUE1nrc1CFY/uvx4XjGNWYChImJZlswXIVhrBCzSVaj6DP1AU2gUMJ6KDh2ygXo/u/Qx232CXA==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
joycon@3.1.1:
|
joycon@3.1.1:
|
||||||
|
|
@ -803,7 +803,7 @@ snapshots:
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
hijri-core@file:../hijri-core: {}
|
hijri-core@1.0.0: {}
|
||||||
|
|
||||||
joycon@3.1.1: {}
|
joycon@3.1.1: {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Temporal } from '@js-temporal/polyfill';
|
import { Temporal } from '@js-temporal/polyfill';
|
||||||
import type { CalendarEngine } from 'hijri-core';
|
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.
|
* Base class implementing the TC39 Temporal Calendar Protocol for Hijri calendars.
|
||||||
|
|
@ -146,6 +146,7 @@ export class HijriCalendar {
|
||||||
return total;
|
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 {
|
weekOfYear(date: Temporal.PlainDate): number {
|
||||||
return Math.ceil(this.dayOfYear(date) / 7);
|
return Math.ceil(this.dayOfYear(date) / 7);
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +164,11 @@ export class HijriCalendar {
|
||||||
return this.fromHijri(fields.year, fields.month, fields.day);
|
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(
|
yearMonthFromFields(
|
||||||
fields: { year: number; month: number },
|
fields: { year: number; month: number },
|
||||||
_options?: { overflow?: 'constrain' | 'reject' }
|
_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(
|
monthDayFromFields(
|
||||||
fields: { month: number; day: number; year?: number },
|
fields: { month: number; day: number; year?: number },
|
||||||
_options?: { overflow?: 'constrain' | 'reject' }
|
_options?: { overflow?: 'constrain' | 'reject' }
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ export { UaqCalendar } from './calendars/UaqCalendar';
|
||||||
export { FcnaCalendar } from './calendars/FcnaCalendar';
|
export { FcnaCalendar } from './calendars/FcnaCalendar';
|
||||||
|
|
||||||
export type { HijriDate, CalendarEngine, ConversionOptions } from 'hijri-core';
|
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 { UaqCalendar } from './calendars/UaqCalendar';
|
||||||
import { FcnaCalendar } from './calendars/FcnaCalendar';
|
import { FcnaCalendar } from './calendars/FcnaCalendar';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
export type { HijriDate, CalendarEngine, ConversionOptions } from 'hijri-core';
|
export type { HijriDate, CalendarEngine, ConversionOptions } from 'hijri-core';
|
||||||
|
|
||||||
export interface HijriCalendarOptions {
|
|
||||||
calendar?: string;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue