ponomar-ts 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/NOTICE.md +27 -0
- package/README.md +105 -0
- package/dist/core/calendar/jdate.d.ts +52 -0
- package/dist/core/calendar/jdate.d.ts.map +1 -0
- package/dist/core/calendar/jdate.js +151 -0
- package/dist/core/calendar/jdate.js.map +1 -0
- package/dist/core/calendar/pcalendar.d.ts +21 -0
- package/dist/core/calendar/pcalendar.d.ts.map +1 -0
- package/dist/core/calendar/pcalendar.js +33 -0
- package/dist/core/calendar/pcalendar.js.map +1 -0
- package/dist/core/dsl/eval.d.ts +10 -0
- package/dist/core/dsl/eval.d.ts.map +1 -0
- package/dist/core/dsl/eval.js +83 -0
- package/dist/core/dsl/eval.js.map +1 -0
- package/dist/core/dsl/index.d.ts +4 -0
- package/dist/core/dsl/index.d.ts.map +1 -0
- package/dist/core/dsl/index.js +4 -0
- package/dist/core/dsl/index.js.map +1 -0
- package/dist/core/dsl/lexer.d.ts +14 -0
- package/dist/core/dsl/lexer.d.ts.map +1 -0
- package/dist/core/dsl/lexer.js +121 -0
- package/dist/core/dsl/lexer.js.map +1 -0
- package/dist/core/dsl/parser.d.ts +25 -0
- package/dist/core/dsl/parser.d.ts.map +1 -0
- package/dist/core/dsl/parser.js +103 -0
- package/dist/core/dsl/parser.js.map +1 -0
- package/dist/data/divineLiturgy.d.ts +3 -0
- package/dist/data/divineLiturgy.d.ts.map +1 -0
- package/dist/data/divineLiturgy.js +80 -0
- package/dist/data/divineLiturgy.js.map +1 -0
- package/dist/data/index.d.ts +6 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +8 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/menaion.d.ts +3 -0
- package/dist/data/menaion.d.ts.map +1 -0
- package/dist/data/menaion.js +18363 -0
- package/dist/data/menaion.js.map +1 -0
- package/dist/data/pentecostarion.d.ts +3 -0
- package/dist/data/pentecostarion.d.ts.map +1 -0
- package/dist/data/pentecostarion.js +4096 -0
- package/dist/data/pentecostarion.js.map +1 -0
- package/dist/data/triodion.d.ts +3 -0
- package/dist/data/triodion.d.ts.map +1 -0
- package/dist/data/triodion.js +841 -0
- package/dist/data/triodion.js.map +1 -0
- package/dist/data/types.d.ts +30 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/data/types.js +5 -0
- package/dist/data/types.js.map +1 -0
- package/dist/engine/day.d.ts +23 -0
- package/dist/engine/day.d.ts.map +1 -0
- package/dist/engine/day.js +41 -0
- package/dist/engine/day.js.map +1 -0
- package/dist/engine/index.d.ts +20 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +26 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/lookup.d.ts +7 -0
- package/dist/engine/lookup.d.ts.map +1 -0
- package/dist/engine/lookup.js +30 -0
- package/dist/engine/lookup.js.map +1 -0
- package/dist/engine/resolve.d.ts +15 -0
- package/dist/engine/resolve.d.ts.map +1 -0
- package/dist/engine/resolve.js +30 -0
- package/dist/engine/resolve.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/paschalion.d.ts +31 -0
- package/dist/paschalion.d.ts.map +1 -0
- package/dist/paschalion.js +60 -0
- package/dist/paschalion.js.map +1 -0
- package/package.json +89 -0
- package/src/core/calendar/jdate.ts +192 -0
- package/src/core/calendar/pcalendar.ts +47 -0
- package/src/core/dsl/eval.ts +94 -0
- package/src/core/dsl/index.ts +15 -0
- package/src/core/dsl/lexer.ts +121 -0
- package/src/core/dsl/parser.ts +148 -0
- package/src/data/divineLiturgy.ts +82 -0
- package/src/data/index.ts +8 -0
- package/src/data/menaion.ts +18365 -0
- package/src/data/pentecostarion.ts +4098 -0
- package/src/data/triodion.ts +843 -0
- package/src/data/types.ts +35 -0
- package/src/engine/day.ts +67 -0
- package/src/engine/index.ts +44 -0
- package/src/engine/lookup.ts +34 -0
- package/src/engine/resolve.ts +49 -0
- package/src/index.ts +17 -0
- package/src/paschalion.ts +80 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Shared, hand-written type definitions for generated data under src/data/.
|
|
2
|
+
// The codegen scripts import these; generated files reference them via
|
|
3
|
+
// import type. Do not edit generated files by hand.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One SAINT record from a `<DAY>` XML file (pentecostarion/triodion/menaion).
|
|
7
|
+
* `sIds` is a list because upstream sometimes packs multiple ids into one
|
|
8
|
+
* `SId="a,b,c"` attribute.
|
|
9
|
+
*/
|
|
10
|
+
export interface Saint {
|
|
11
|
+
readonly sIds: readonly string[];
|
|
12
|
+
readonly cId: string;
|
|
13
|
+
/** Raw StringOp DSL guard, evaluated at runtime; missing means "always". */
|
|
14
|
+
readonly cmd?: string;
|
|
15
|
+
/** Raw DSL expression yielding a numeric tone, or a bare literal. */
|
|
16
|
+
readonly tone?: string;
|
|
17
|
+
/** Source tag: G (Greek), R (Russian), U (Unsourced), etc. */
|
|
18
|
+
readonly src?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** All saints registered for a single day of a cycle. */
|
|
22
|
+
export interface DayEntry {
|
|
23
|
+
readonly saints: readonly Saint[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One `<COMMAND>` record from Commands/DivineLiturgy.xml (or similar).
|
|
28
|
+
* `value` and `cmd` are DSL expressions.
|
|
29
|
+
*/
|
|
30
|
+
export interface Command {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly value: string;
|
|
33
|
+
readonly cmd?: string;
|
|
34
|
+
readonly comment?: string;
|
|
35
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// The day-context calculator: given a Gregorian date, produce the numeric
|
|
2
|
+
// variables that the upstream StringOp DSL expects (`doy`, `dow`, `nday`,
|
|
3
|
+
// `ndayP`, `ndayF`). Follows upstream conventions: all values derive from
|
|
4
|
+
// the *Julian* civil-date equivalent (that is what the Ponomar engine and
|
|
5
|
+
// its rule XML use).
|
|
6
|
+
|
|
7
|
+
import type { JulianDate } from "../core/calendar/jdate.ts";
|
|
8
|
+
import {
|
|
9
|
+
dayOfWeek,
|
|
10
|
+
dayOfYear,
|
|
11
|
+
difference,
|
|
12
|
+
fromGregorian,
|
|
13
|
+
} from "../core/calendar/jdate.ts";
|
|
14
|
+
import type { CalendarDate } from "../core/calendar/pcalendar.ts";
|
|
15
|
+
import { getJulianPaschaRich } from "../paschalion.ts";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Numeric context variables for a single day, in the shape the StringOp DSL
|
|
19
|
+
* expects. Sunday is `dow === 0`; `doy` is 0-indexed to match upstream.
|
|
20
|
+
*/
|
|
21
|
+
export interface DayContext {
|
|
22
|
+
readonly gregorian: CalendarDate;
|
|
23
|
+
readonly julian: JulianDate;
|
|
24
|
+
readonly doy: number;
|
|
25
|
+
readonly dow: number;
|
|
26
|
+
readonly nday: number;
|
|
27
|
+
readonly ndayP: number;
|
|
28
|
+
readonly ndayF: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function computeDayContext(gregorian: CalendarDate): DayContext {
|
|
32
|
+
const julian = fromGregorian(gregorian);
|
|
33
|
+
const year = julian.year;
|
|
34
|
+
const paschaThis = getJulianPaschaRich(year);
|
|
35
|
+
const paschaPrev = getJulianPaschaRich(year - 1);
|
|
36
|
+
const paschaNext = getJulianPaschaRich(year + 1);
|
|
37
|
+
return {
|
|
38
|
+
gregorian,
|
|
39
|
+
julian,
|
|
40
|
+
doy: dayOfYear(julian),
|
|
41
|
+
dow: dayOfWeek(julian),
|
|
42
|
+
nday: difference(julian, paschaThis),
|
|
43
|
+
ndayP: difference(julian, paschaPrev),
|
|
44
|
+
ndayF: difference(julian, paschaNext),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format a `DayContext` as a plain lookup table for the DSL evaluator.
|
|
50
|
+
* `dRank` defaults to 10 (upstream's "no rank found" fallback), matching
|
|
51
|
+
* initial state before Commemorations are loaded.
|
|
52
|
+
*/
|
|
53
|
+
export function dslContext(
|
|
54
|
+
ctx: DayContext,
|
|
55
|
+
extra?: Readonly<Record<string, number>>,
|
|
56
|
+
): Readonly<Record<string, number>> {
|
|
57
|
+
return {
|
|
58
|
+
doy: ctx.doy,
|
|
59
|
+
dow: ctx.dow,
|
|
60
|
+
nday: ctx.nday,
|
|
61
|
+
ndayP: ctx.ndayP,
|
|
62
|
+
ndayF: ctx.ndayF,
|
|
63
|
+
dRank: 10,
|
|
64
|
+
GS: 1,
|
|
65
|
+
...extra,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Top-level public API for the day-info engine.
|
|
2
|
+
//
|
|
3
|
+
// This composes the day-context calculator, the DayEntry lookups
|
|
4
|
+
// (pentecostarion / triodion / menaion) and the DSL-guard filter into one
|
|
5
|
+
// call. It answers: "for this Gregorian date, which saints are commemorated,
|
|
6
|
+
// and what is the paschal-cycle context?"
|
|
7
|
+
|
|
8
|
+
import type { CalendarDate } from "../core/calendar/pcalendar.ts";
|
|
9
|
+
import type { DayContext } from "./day.ts";
|
|
10
|
+
import { computeDayContext } from "./day.ts";
|
|
11
|
+
import { selectMenaionEntry, selectPaschalCycleEntry } from "./lookup.ts";
|
|
12
|
+
import type { ResolvedSaint } from "./resolve.ts";
|
|
13
|
+
import { resolveSaints } from "./resolve.ts";
|
|
14
|
+
|
|
15
|
+
/** Result of `getLiturgicalDay`. */
|
|
16
|
+
export interface LiturgicalDay {
|
|
17
|
+
readonly context: DayContext;
|
|
18
|
+
/** Saints from the paschal cycle (pentecostarion or triodion), Cmd-filtered. */
|
|
19
|
+
readonly paschalSaints: readonly ResolvedSaint[];
|
|
20
|
+
/** Saints from the fixed Menaion at Julian MM-DD, Cmd-filtered. */
|
|
21
|
+
readonly menaionSaints: readonly ResolvedSaint[];
|
|
22
|
+
/** Union of paschalSaints then menaionSaints, in that order. */
|
|
23
|
+
readonly allSaints: readonly ResolvedSaint[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getLiturgicalDay(gregorian: CalendarDate): LiturgicalDay {
|
|
27
|
+
const context = computeDayContext(gregorian);
|
|
28
|
+
const paschal = selectPaschalCycleEntry(context);
|
|
29
|
+
const menaion = selectMenaionEntry(context);
|
|
30
|
+
const paschalSaints = paschal ? resolveSaints(paschal, context) : [];
|
|
31
|
+
const menaionSaints = menaion ? resolveSaints(menaion, context) : [];
|
|
32
|
+
return {
|
|
33
|
+
context,
|
|
34
|
+
paschalSaints,
|
|
35
|
+
menaionSaints,
|
|
36
|
+
allSaints: [...paschalSaints, ...menaionSaints],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type { DayContext } from "./day.ts";
|
|
41
|
+
export { computeDayContext, dslContext } from "./day.ts";
|
|
42
|
+
export { selectMenaionEntry, selectPaschalCycleEntry } from "./lookup.ts";
|
|
43
|
+
export type { ResolvedSaint } from "./resolve.ts";
|
|
44
|
+
export { resolveSaints } from "./resolve.ts";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Given a DayContext, pull the raw `DayEntry` rows from the generated data
|
|
2
|
+
// modules for the Paschal cycle (pentecostarion or triodion) and the fixed
|
|
3
|
+
// Menaion, if present. Both entries are optional: the vendor may not have
|
|
4
|
+
// data for every calendar slot.
|
|
5
|
+
|
|
6
|
+
import type { DayEntry } from "../data/index.ts";
|
|
7
|
+
import { MENAION, PENTECOSTARION, TRIODION } from "../data/index.ts";
|
|
8
|
+
import type { DayContext } from "./day.ts";
|
|
9
|
+
|
|
10
|
+
/** Selection of Paschal-cycle DayEntry for a given `nday` / `ndayP`. */
|
|
11
|
+
export function selectPaschalCycleEntry(ctx: DayContext): DayEntry | null {
|
|
12
|
+
// After Pascha, through Pentecost and beyond, up to ~245 days out.
|
|
13
|
+
if (ctx.nday >= 0 && ctx.nday < PENTECOSTARION.length) {
|
|
14
|
+
return PENTECOSTARION[ctx.nday] ?? null;
|
|
15
|
+
}
|
|
16
|
+
// Pre-Lent through Holy Saturday: negative nday, indexed by absolute value.
|
|
17
|
+
if (ctx.nday >= -70 && ctx.nday < 0) {
|
|
18
|
+
const idx = Math.abs(ctx.nday) - 1;
|
|
19
|
+
return TRIODION[idx] ?? null;
|
|
20
|
+
}
|
|
21
|
+
// After Pentecost / before Triodion opens: fall back to previous year's
|
|
22
|
+
// Pascha cycle. `ndayP` is days since previous-year Pascha.
|
|
23
|
+
if (ctx.nday < -70 && ctx.ndayP >= 0 && ctx.ndayP < PENTECOSTARION.length) {
|
|
24
|
+
return PENTECOSTARION[ctx.ndayP] ?? null;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Menaion entry keyed by Julian MM-DD; may be absent for days without data. */
|
|
30
|
+
export function selectMenaionEntry(ctx: DayContext): DayEntry | null {
|
|
31
|
+
const mm = ctx.julian.month.toString().padStart(2, "0");
|
|
32
|
+
const dd = ctx.julian.day.toString().padStart(2, "0");
|
|
33
|
+
return MENAION[`${mm}-${dd}`] ?? null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Apply the DSL `Cmd` guards on `Saint` records to a DayContext, and resolve
|
|
2
|
+
// the optional numeric `Tone` expression. Saints whose guard evaluates to
|
|
3
|
+
// false are dropped.
|
|
4
|
+
|
|
5
|
+
import { evaluate, evaluateBool } from "../core/dsl/index.ts";
|
|
6
|
+
import type { DayEntry, Saint } from "../data/index.ts";
|
|
7
|
+
import type { DayContext } from "./day.ts";
|
|
8
|
+
import { dslContext } from "./day.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A `Saint` with its `Tone` expression resolved to a number (or null if
|
|
12
|
+
* absent). The `cmd` guard, if any, has already been evaluated to `true`.
|
|
13
|
+
*/
|
|
14
|
+
export interface ResolvedSaint {
|
|
15
|
+
readonly sIds: readonly string[];
|
|
16
|
+
readonly cId: string;
|
|
17
|
+
readonly src?: string;
|
|
18
|
+
readonly tone: number | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Evaluate every saint's `Cmd`, then resolve tone; drop saints where Cmd is false. */
|
|
22
|
+
export function resolveSaints(
|
|
23
|
+
entry: DayEntry,
|
|
24
|
+
ctx: DayContext,
|
|
25
|
+
extraVars?: Readonly<Record<string, number>>,
|
|
26
|
+
): ResolvedSaint[] {
|
|
27
|
+
const vars = dslContext(ctx, extraVars);
|
|
28
|
+
const out: ResolvedSaint[] = [];
|
|
29
|
+
for (const s of entry.saints) {
|
|
30
|
+
if (s.cmd !== undefined && !evaluateBool(s.cmd, vars)) continue;
|
|
31
|
+
out.push({
|
|
32
|
+
sIds: s.sIds,
|
|
33
|
+
cId: s.cId,
|
|
34
|
+
...(s.src !== undefined ? { src: s.src } : {}),
|
|
35
|
+
tone: resolveTone(s, vars),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveTone(
|
|
42
|
+
s: Saint,
|
|
43
|
+
vars: Readonly<Record<string, number>>,
|
|
44
|
+
): number | null {
|
|
45
|
+
if (s.tone === undefined) return null;
|
|
46
|
+
const n = evaluate(s.tone, vars);
|
|
47
|
+
if (!Number.isFinite(n)) return null;
|
|
48
|
+
return Math.trunc(n);
|
|
49
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ponomar-ts — TypeScript port of the Ponomar Orthodox liturgics engine.
|
|
3
|
+
*
|
|
4
|
+
* This top-level entry re-exports every stable public module. Prefer deep
|
|
5
|
+
* imports (`ponomar-ts/paschalion`) for smaller bundles when possible.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from "./paschalion.ts";
|
|
11
|
+
export * as jdate from "./core/calendar/jdate.ts";
|
|
12
|
+
export * as pcalendar from "./core/calendar/pcalendar.ts";
|
|
13
|
+
export * as dsl from "./core/dsl/index.ts";
|
|
14
|
+
export * as data from "./data/index.ts";
|
|
15
|
+
export * as engine from "./engine/index.ts";
|
|
16
|
+
export { getLiturgicalDay } from "./engine/index.ts";
|
|
17
|
+
export type { LiturgicalDay } from "./engine/index.ts";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Ported from Ponomar/Paschalion.java (typiconman/ponomar).
|
|
2
|
+
// Uses Gauss' formula for the Julian Pascha date, then converts to the
|
|
3
|
+
// Gregorian civil calendar via Julian Day Number (see src/core/calendar/).
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
addDays as addDaysJulian,
|
|
7
|
+
julianDate,
|
|
8
|
+
toGregorian,
|
|
9
|
+
type JulianDate,
|
|
10
|
+
} from "./core/calendar/jdate.ts";
|
|
11
|
+
import type { CalendarDate } from "./core/calendar/pcalendar.ts";
|
|
12
|
+
|
|
13
|
+
export type { CalendarDate } from "./core/calendar/pcalendar.ts";
|
|
14
|
+
export {
|
|
15
|
+
addDays,
|
|
16
|
+
julianToGregorianOffset,
|
|
17
|
+
} from "./core/calendar/pcalendar.ts";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Julian calendar date of Orthodox Pascha for the given AD year, computed by
|
|
21
|
+
* Gauss' Easter formula. The returned month/day are on the *Julian* calendar.
|
|
22
|
+
*
|
|
23
|
+
* @throws {RangeError} if `year` is not a positive integer.
|
|
24
|
+
*/
|
|
25
|
+
export function getJulianPascha(year: number): CalendarDate {
|
|
26
|
+
const rich = getJulianPaschaRich(year);
|
|
27
|
+
return { year: rich.year, month: rich.month, day: rich.day };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Gregorian civil date of Orthodox Pascha for the given AD year. */
|
|
31
|
+
export function getOrthodoxPascha(year: number): CalendarDate {
|
|
32
|
+
return toGregorian(getJulianPaschaRich(year));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Pentecost (Pascha + 49 days), Gregorian civil date. */
|
|
36
|
+
export function getPentecost(year: number): CalendarDate {
|
|
37
|
+
return toGregorian(addDaysJulian(getJulianPaschaRich(year), 49));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Ascension (Pascha + 39 days), Gregorian civil date. */
|
|
41
|
+
export function getAscension(year: number): CalendarDate {
|
|
42
|
+
return toGregorian(addDaysJulian(getJulianPaschaRich(year), 39));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Meatfare Sunday (Pascha - 56 days), Gregorian civil date. */
|
|
46
|
+
export function getMeatfare(year: number): CalendarDate {
|
|
47
|
+
return toGregorian(addDaysJulian(getJulianPaschaRich(year), -56));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Cheesefare / Forgiveness Sunday (Pascha - 49 days), Gregorian. */
|
|
51
|
+
export function getCheesefare(year: number): CalendarDate {
|
|
52
|
+
return toGregorian(addDaysJulian(getJulianPaschaRich(year), -49));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Clean Monday, start of Great Lent (Pascha - 48 days), Gregorian. */
|
|
56
|
+
export function getLentStart(year: number): CalendarDate {
|
|
57
|
+
return toGregorian(addDaysJulian(getJulianPaschaRich(year), -48));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Apostles' Fast start (Pascha + 57 days), Gregorian. */
|
|
61
|
+
export function getApostlesFastStart(year: number): CalendarDate {
|
|
62
|
+
return toGregorian(addDaysJulian(getJulianPaschaRich(year), 57));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Julian-calendar date of Pascha as a rich `JulianDate` (with pre-computed
|
|
67
|
+
* Julian Day Number). Used by the engine layer for `nday` derivations.
|
|
68
|
+
*/
|
|
69
|
+
export function getJulianPaschaRich(year: number): JulianDate {
|
|
70
|
+
if (!Number.isInteger(year) || year < 1) {
|
|
71
|
+
throw new RangeError(`year must be a positive integer, got ${year}`);
|
|
72
|
+
}
|
|
73
|
+
const a = year % 19;
|
|
74
|
+
const b = year % 4;
|
|
75
|
+
const c = year % 7;
|
|
76
|
+
const d = (19 * a + 15) % 30;
|
|
77
|
+
const e = (2 * b + 4 * c + 6 * d + 6) % 7;
|
|
78
|
+
const f = d + e;
|
|
79
|
+
return f <= 9 ? julianDate(year, 3, 22 + f) : julianDate(year, 4, f - 9);
|
|
80
|
+
}
|