@intech-software/chronera 0.1.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 +190 -0
- package/README.md +6644 -0
- package/dist/calendar/buddhist/adapter.d.ts +2 -0
- package/dist/calendar/buddhist/adapter.js +33 -0
- package/dist/calendar/buddhist/adapter.js.map +1 -0
- package/dist/calendar/buddhist/constants.d.ts +3 -0
- package/dist/calendar/buddhist/constants.js +14 -0
- package/dist/calendar/buddhist/constants.js.map +1 -0
- package/dist/calendar/buddhist/index.d.ts +3 -0
- package/dist/calendar/buddhist/index.js +4 -0
- package/dist/calendar/buddhist/index.js.map +1 -0
- package/dist/calendar/buddhist/validator.d.ts +4 -0
- package/dist/calendar/buddhist/validator.js +60 -0
- package/dist/calendar/buddhist/validator.js.map +1 -0
- package/dist/calendar/gregory/absolute-day.d.ts +12 -0
- package/dist/calendar/gregory/absolute-day.js +14 -0
- package/dist/calendar/gregory/absolute-day.js.map +1 -0
- package/dist/calendar/gregory/adapter.d.ts +3 -0
- package/dist/calendar/gregory/adapter.js +93 -0
- package/dist/calendar/gregory/adapter.js.map +1 -0
- package/dist/calendar/gregory/constants.d.ts +3 -0
- package/dist/calendar/gregory/constants.js +18 -0
- package/dist/calendar/gregory/constants.js.map +1 -0
- package/dist/calendar/gregory/index.d.ts +4 -0
- package/dist/calendar/gregory/index.js +5 -0
- package/dist/calendar/gregory/index.js.map +1 -0
- package/dist/calendar/gregory/leap-year.d.ts +1 -0
- package/dist/calendar/gregory/leap-year.js +2 -0
- package/dist/calendar/gregory/leap-year.js.map +1 -0
- package/dist/calendar/gregory/validator.d.ts +2 -0
- package/dist/calendar/gregory/validator.js +45 -0
- package/dist/calendar/gregory/validator.js.map +1 -0
- package/dist/calendar/hijri/civil-adapter.d.ts +12 -0
- package/dist/calendar/hijri/civil-adapter.js +132 -0
- package/dist/calendar/hijri/civil-adapter.js.map +1 -0
- package/dist/calendar/hijri/index.d.ts +3 -0
- package/dist/calendar/hijri/index.js +4 -0
- package/dist/calendar/hijri/index.js.map +1 -0
- package/dist/calendar/hijri/tabular-adapter.d.ts +4 -0
- package/dist/calendar/hijri/tabular-adapter.js +79 -0
- package/dist/calendar/hijri/tabular-adapter.js.map +1 -0
- package/dist/calendar/hijri/umm-al-qura-adapter.d.ts +4 -0
- package/dist/calendar/hijri/umm-al-qura-adapter.js +27 -0
- package/dist/calendar/hijri/umm-al-qura-adapter.js.map +1 -0
- package/dist/calendar/index.d.ts +13 -0
- package/dist/calendar/index.js +12 -0
- package/dist/calendar/index.js.map +1 -0
- package/dist/calendar/indian/adapter.d.ts +2 -0
- package/dist/calendar/indian/adapter.js +66 -0
- package/dist/calendar/indian/adapter.js.map +1 -0
- package/dist/calendar/indian/constants.d.ts +5 -0
- package/dist/calendar/indian/constants.js +43 -0
- package/dist/calendar/indian/constants.js.map +1 -0
- package/dist/calendar/indian/index.d.ts +3 -0
- package/dist/calendar/indian/index.js +4 -0
- package/dist/calendar/indian/index.js.map +1 -0
- package/dist/calendar/indian/validator.d.ts +6 -0
- package/dist/calendar/indian/validator.js +65 -0
- package/dist/calendar/indian/validator.js.map +1 -0
- package/dist/calendar/japanese/adapter.d.ts +3 -0
- package/dist/calendar/japanese/adapter.js +63 -0
- package/dist/calendar/japanese/adapter.js.map +1 -0
- package/dist/calendar/japanese/constants.d.ts +17 -0
- package/dist/calendar/japanese/constants.js +85 -0
- package/dist/calendar/japanese/constants.js.map +1 -0
- package/dist/calendar/japanese/index.d.ts +3 -0
- package/dist/calendar/japanese/index.js +4 -0
- package/dist/calendar/japanese/index.js.map +1 -0
- package/dist/calendar/japanese/validator.d.ts +5 -0
- package/dist/calendar/japanese/validator.js +76 -0
- package/dist/calendar/japanese/validator.js.map +1 -0
- package/dist/calendar/persian/adapter.d.ts +2 -0
- package/dist/calendar/persian/adapter.js +42 -0
- package/dist/calendar/persian/adapter.js.map +1 -0
- package/dist/calendar/persian/constants.d.ts +5 -0
- package/dist/calendar/persian/constants.js +43 -0
- package/dist/calendar/persian/constants.js.map +1 -0
- package/dist/calendar/persian/index.d.ts +4 -0
- package/dist/calendar/persian/index.js +5 -0
- package/dist/calendar/persian/index.js.map +1 -0
- package/dist/calendar/persian/math.d.ts +24 -0
- package/dist/calendar/persian/math.js +109 -0
- package/dist/calendar/persian/math.js.map +1 -0
- package/dist/calendar/persian/validator.d.ts +4 -0
- package/dist/calendar/persian/validator.js +52 -0
- package/dist/calendar/persian/validator.js.map +1 -0
- package/dist/calendar/registry.d.ts +9 -0
- package/dist/calendar/registry.js +119 -0
- package/dist/calendar/registry.js.map +1 -0
- package/dist/calendar/roc/adapter.d.ts +2 -0
- package/dist/calendar/roc/adapter.js +47 -0
- package/dist/calendar/roc/adapter.js.map +1 -0
- package/dist/calendar/roc/constants.d.ts +6 -0
- package/dist/calendar/roc/constants.js +34 -0
- package/dist/calendar/roc/constants.js.map +1 -0
- package/dist/calendar/roc/index.d.ts +3 -0
- package/dist/calendar/roc/index.js +4 -0
- package/dist/calendar/roc/index.js.map +1 -0
- package/dist/calendar/roc/validator.d.ts +4 -0
- package/dist/calendar/roc/validator.js +52 -0
- package/dist/calendar/roc/validator.js.map +1 -0
- package/dist/calendar/types.d.ts +34 -0
- package/dist/calendar/types.js +2 -0
- package/dist/calendar/types.js.map +1 -0
- package/dist/core/absolute-day.d.ts +8 -0
- package/dist/core/absolute-day.js +39 -0
- package/dist/core/absolute-day.js.map +1 -0
- package/dist/core/calendar-date.d.ts +11 -0
- package/dist/core/calendar-date.js +37 -0
- package/dist/core/calendar-date.js.map +1 -0
- package/dist/core/day-of-year.d.ts +16 -0
- package/dist/core/day-of-year.js +30 -0
- package/dist/core/day-of-year.js.map +1 -0
- package/dist/core/duration.d.ts +2 -0
- package/dist/core/duration.js +31 -0
- package/dist/core/duration.js.map +1 -0
- package/dist/core/fiscal.d.ts +31 -0
- package/dist/core/fiscal.js +45 -0
- package/dist/core/fiscal.js.map +1 -0
- package/dist/core/gregorian-math.d.ts +4 -0
- package/dist/core/gregorian-math.js +30 -0
- package/dist/core/gregorian-math.js.map +1 -0
- package/dist/core/instant.d.ts +6 -0
- package/dist/core/instant.js +26 -0
- package/dist/core/instant.js.map +1 -0
- package/dist/core/integer.d.ts +1 -0
- package/dist/core/integer.js +7 -0
- package/dist/core/integer.js.map +1 -0
- package/dist/core/iso-week.d.ts +19 -0
- package/dist/core/iso-week.js +41 -0
- package/dist/core/iso-week.js.map +1 -0
- package/dist/core/julian-day.d.ts +26 -0
- package/dist/core/julian-day.js +35 -0
- package/dist/core/julian-day.js.map +1 -0
- package/dist/core/local-date-time.d.ts +2 -0
- package/dist/core/local-date-time.js +15 -0
- package/dist/core/local-date-time.js.map +1 -0
- package/dist/core/local-date.d.ts +2 -0
- package/dist/core/local-date.js +22 -0
- package/dist/core/local-date.js.map +1 -0
- package/dist/core/local-time.d.ts +2 -0
- package/dist/core/local-time.js +28 -0
- package/dist/core/local-time.js.map +1 -0
- package/dist/core/range.d.ts +2 -0
- package/dist/core/range.js +9 -0
- package/dist/core/range.js.map +1 -0
- package/dist/create-chronera.d.ts +2 -0
- package/dist/create-chronera.js +59 -0
- package/dist/create-chronera.js.map +1 -0
- package/dist/errors/error-codes.d.ts +1 -0
- package/dist/errors/error-codes.js +2 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/errors/errors.d.ts +16 -0
- package/dist/errors/errors.js +22 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/format/format-date-range.d.ts +2 -0
- package/dist/format/format-date-range.js +89 -0
- package/dist/format/format-date-range.js.map +1 -0
- package/dist/format/format-date-time.d.ts +2 -0
- package/dist/format/format-date-time.js +51 -0
- package/dist/format/format-date-time.js.map +1 -0
- package/dist/format/format-date.d.ts +4 -0
- package/dist/format/format-date.js +103 -0
- package/dist/format/format-date.js.map +1 -0
- package/dist/format/format-pattern.d.ts +4 -0
- package/dist/format/format-pattern.js +266 -0
- package/dist/format/format-pattern.js.map +1 -0
- package/dist/format/format-relative.d.ts +2 -0
- package/dist/format/format-relative.js +93 -0
- package/dist/format/format-relative.js.map +1 -0
- package/dist/format/format-time.d.ts +2 -0
- package/dist/format/format-time.js +48 -0
- package/dist/format/format-time.js.map +1 -0
- package/dist/format/index.d.ts +13 -0
- package/dist/format/index.js +12 -0
- package/dist/format/index.js.map +1 -0
- package/dist/format/pattern-scanner.d.ts +8 -0
- package/dist/format/pattern-scanner.js +102 -0
- package/dist/format/pattern-scanner.js.map +1 -0
- package/dist/format/presets/japanese-official.d.ts +8 -0
- package/dist/format/presets/japanese-official.js +39 -0
- package/dist/format/presets/japanese-official.js.map +1 -0
- package/dist/format/presets/taiwan-official.d.ts +6 -0
- package/dist/format/presets/taiwan-official.js +23 -0
- package/dist/format/presets/taiwan-official.js.map +1 -0
- package/dist/format/presets/thai-official.d.ts +2 -0
- package/dist/format/presets/thai-official.js +45 -0
- package/dist/format/presets/thai-official.js.map +1 -0
- package/dist/format/presets/types.d.ts +2 -0
- package/dist/format/presets/types.js +2 -0
- package/dist/format/presets/types.js.map +1 -0
- package/dist/format/presets/world-locales.d.ts +29 -0
- package/dist/format/presets/world-locales.js +330 -0
- package/dist/format/presets/world-locales.js.map +1 -0
- package/dist/format/resolve-date-input.d.ts +9 -0
- package/dist/format/resolve-date-input.js +93 -0
- package/dist/format/resolve-date-input.js.map +1 -0
- package/dist/format/rfc2822.d.ts +17 -0
- package/dist/format/rfc2822.js +59 -0
- package/dist/format/rfc2822.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/engine.d.ts +8 -0
- package/dist/internal/engine.js +11 -0
- package/dist/internal/engine.js.map +1 -0
- package/dist/locale/numbering-system.d.ts +4 -0
- package/dist/locale/numbering-system.js +133 -0
- package/dist/locale/numbering-system.js.map +1 -0
- package/dist/locale/resolve-locale.d.ts +7 -0
- package/dist/locale/resolve-locale.js +51 -0
- package/dist/locale/resolve-locale.js.map +1 -0
- package/dist/locale/types.d.ts +1 -0
- package/dist/locale/types.js +2 -0
- package/dist/locale/types.js.map +1 -0
- package/dist/operations/arithmetic.d.ts +4 -0
- package/dist/operations/arithmetic.js +13 -0
- package/dist/operations/arithmetic.js.map +1 -0
- package/dist/operations/compare.d.ts +5 -0
- package/dist/operations/compare.js +50 -0
- package/dist/operations/compare.js.map +1 -0
- package/dist/operations/convert-calendar-date.d.ts +4 -0
- package/dist/operations/convert-calendar-date.js +54 -0
- package/dist/operations/convert-calendar-date.js.map +1 -0
- package/dist/operations/day-of-year.d.ts +7 -0
- package/dist/operations/day-of-year.js +29 -0
- package/dist/operations/day-of-year.js.map +1 -0
- package/dist/operations/fiscal.d.ts +5 -0
- package/dist/operations/fiscal.js +25 -0
- package/dist/operations/fiscal.js.map +1 -0
- package/dist/operations/iso-week.d.ts +9 -0
- package/dist/operations/iso-week.js +29 -0
- package/dist/operations/iso-week.js.map +1 -0
- package/dist/operations/julian-day.d.ts +6 -0
- package/dist/operations/julian-day.js +37 -0
- package/dist/operations/julian-day.js.map +1 -0
- package/dist/operations/validate.d.ts +10 -0
- package/dist/operations/validate.js +55 -0
- package/dist/operations/validate.js.map +1 -0
- package/dist/parse/field-normalizer.d.ts +1 -0
- package/dist/parse/field-normalizer.js +5 -0
- package/dist/parse/field-normalizer.js.map +1 -0
- package/dist/parse/index.d.ts +4 -0
- package/dist/parse/index.js +4 -0
- package/dist/parse/index.js.map +1 -0
- package/dist/parse/parse-instant.d.ts +2 -0
- package/dist/parse/parse-instant.js +97 -0
- package/dist/parse/parse-instant.js.map +1 -0
- package/dist/parse/parse-local-date.d.ts +2 -0
- package/dist/parse/parse-local-date.js +31 -0
- package/dist/parse/parse-local-date.js.map +1 -0
- package/dist/parse/pattern-parser.d.ts +2 -0
- package/dist/parse/pattern-parser.js +150 -0
- package/dist/parse/pattern-parser.js.map +1 -0
- package/dist/parse/safe-parse.d.ts +3 -0
- package/dist/parse/safe-parse.js +46 -0
- package/dist/parse/safe-parse.js.map +1 -0
- package/dist/public-types.d.ts +209 -0
- package/dist/public-types.js +2 -0
- package/dist/public-types.js.map +1 -0
- package/dist/runtime/bounded-lru.d.ts +11 -0
- package/dist/runtime/bounded-lru.js +42 -0
- package/dist/runtime/bounded-lru.js.map +1 -0
- package/dist/runtime/capabilities.d.ts +5 -0
- package/dist/runtime/capabilities.js +61 -0
- package/dist/runtime/capabilities.js.map +1 -0
- package/dist/runtime/intl-date-time.d.ts +11 -0
- package/dist/runtime/intl-date-time.js +38 -0
- package/dist/runtime/intl-date-time.js.map +1 -0
- package/dist/runtime/intl-number.d.ts +7 -0
- package/dist/runtime/intl-number.js +35 -0
- package/dist/runtime/intl-number.js.map +1 -0
- package/dist/runtime/intl-relative-time.d.ts +7 -0
- package/dist/runtime/intl-relative-time.js +35 -0
- package/dist/runtime/intl-relative-time.js.map +1 -0
- package/dist/runtime/timezone.d.ts +14 -0
- package/dist/runtime/timezone.js +95 -0
- package/dist/runtime/timezone.js.map +1 -0
- package/package.json +155 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CalendarValidator } from "../types.js";
|
|
2
|
+
import type { CalendarDate } from "../../public-types.js";
|
|
3
|
+
export declare function assertJapaneseDate(date: CalendarDate): void;
|
|
4
|
+
export declare function resolveJapaneseEra(era?: string): import("./constants.js").JapaneseEraDefinition | undefined;
|
|
5
|
+
export declare const japaneseValidator: CalendarValidator;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { daysInGregorianMonth, isGregorianLeapYear, parseGregorianMonthCode, } from "../../core/gregorian-math.js";
|
|
2
|
+
import { ChroneraError } from "../../errors/errors.js";
|
|
3
|
+
import { ERA_BY_ID, ERA_BY_KANJI, JAPANESE_IDENTITY } from "./constants.js";
|
|
4
|
+
export function assertJapaneseDate(date) {
|
|
5
|
+
if (date.calendar !== "japanese") {
|
|
6
|
+
throw new ChroneraError("CHRONERA_INVALID_CALENDAR", `Expected calendar "japanese", received "${date.calendar}".`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function resolveJapaneseEra(era) {
|
|
10
|
+
if (!era)
|
|
11
|
+
return undefined;
|
|
12
|
+
const lower = era.toLowerCase();
|
|
13
|
+
return ERA_BY_ID.get(lower) ?? ERA_BY_KANJI.get(era);
|
|
14
|
+
}
|
|
15
|
+
export const japaneseValidator = {
|
|
16
|
+
identity: JAPANESE_IDENTITY,
|
|
17
|
+
validate(date) {
|
|
18
|
+
assertJapaneseDate(date);
|
|
19
|
+
const issues = [];
|
|
20
|
+
const monthNum = parseGregorianMonthCode(date.monthCode);
|
|
21
|
+
if (monthNum === null) {
|
|
22
|
+
issues.push({
|
|
23
|
+
code: "CHRONERA_INVALID_DATE",
|
|
24
|
+
message: `Invalid monthCode: "${date.monthCode}". Expected M01-M12.`,
|
|
25
|
+
path: ["monthCode"],
|
|
26
|
+
});
|
|
27
|
+
return issues;
|
|
28
|
+
}
|
|
29
|
+
// Resolve effective Gregorian year
|
|
30
|
+
let effectiveYear = date.year;
|
|
31
|
+
if (date.era) {
|
|
32
|
+
const eraDef = resolveJapaneseEra(date.era);
|
|
33
|
+
if (!eraDef) {
|
|
34
|
+
issues.push({
|
|
35
|
+
code: "CHRONERA_INVALID_DATE",
|
|
36
|
+
message: `Unknown Japanese era: "${date.era}". Expected reiwa, heisei, showa, taisho, meiji.`,
|
|
37
|
+
path: ["era"],
|
|
38
|
+
});
|
|
39
|
+
return issues;
|
|
40
|
+
}
|
|
41
|
+
if (date.eraYear !== undefined) {
|
|
42
|
+
effectiveYear = eraDef.offset + date.eraYear;
|
|
43
|
+
}
|
|
44
|
+
else if (date.year < 100) {
|
|
45
|
+
effectiveYear = eraDef.offset + date.year;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (effectiveYear < 1868 || effectiveYear > 9999) {
|
|
49
|
+
issues.push({
|
|
50
|
+
code: "CHRONERA_OUT_OF_RANGE",
|
|
51
|
+
message: `Japanese calendar supports dates from 1868 (Meiji 1) to 9999 CE. Received year: ${effectiveYear}.`,
|
|
52
|
+
path: ["year"],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const maxDays = daysInGregorianMonth(effectiveYear, monthNum);
|
|
56
|
+
if (date.day < 1 || date.day > maxDays) {
|
|
57
|
+
issues.push({
|
|
58
|
+
code: "CHRONERA_INVALID_DATE",
|
|
59
|
+
message: `Invalid day ${date.day} for year ${effectiveYear} month ${monthNum}. Expected 1..${maxDays}.`,
|
|
60
|
+
path: ["day"],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return issues;
|
|
64
|
+
},
|
|
65
|
+
daysInMonth(year, monthCode) {
|
|
66
|
+
const month = parseGregorianMonthCode(monthCode);
|
|
67
|
+
if (month === null) {
|
|
68
|
+
throw new ChroneraError("CHRONERA_INVALID_DATE", `Invalid monthCode: "${monthCode}". Expected M01-M12.`);
|
|
69
|
+
}
|
|
70
|
+
return daysInGregorianMonth(year, month);
|
|
71
|
+
},
|
|
72
|
+
isLeapYear(year) {
|
|
73
|
+
return isGregorianLeapYear(year);
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/calendar/japanese/validator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n daysInGregorianMonth,\n isGregorianLeapYear,\n parseGregorianMonthCode,\n} from \"../../core/gregorian-math.js\";\nimport { ChroneraError } from \"../../errors/errors.js\";\nimport { ERA_BY_ID, ERA_BY_KANJI, JAPANESE_IDENTITY } from \"./constants.js\";\n\nimport type { CalendarValidator } from \"../types.js\";\nimport type {\n CalendarDate,\n ChroneraIssue,\n MonthCode,\n} from \"../../public-types.js\";\n\nexport function assertJapaneseDate(date: CalendarDate): void {\n if (date.calendar !== \"japanese\") {\n throw new ChroneraError(\n \"CHRONERA_INVALID_CALENDAR\",\n `Expected calendar \"japanese\", received \"${date.calendar}\".`,\n );\n }\n}\n\nexport function resolveJapaneseEra(era?: string) {\n if (!era) return undefined;\n const lower = era.toLowerCase();\n return ERA_BY_ID.get(lower) ?? ERA_BY_KANJI.get(era);\n}\n\nexport const japaneseValidator: CalendarValidator = {\n identity: JAPANESE_IDENTITY,\n\n validate(date: CalendarDate): readonly ChroneraIssue[] {\n assertJapaneseDate(date);\n const issues: ChroneraIssue[] = [];\n\n const monthNum = parseGregorianMonthCode(date.monthCode);\n if (monthNum === null) {\n issues.push({\n code: \"CHRONERA_INVALID_DATE\",\n message: `Invalid monthCode: \"${date.monthCode}\". Expected M01-M12.`,\n path: [\"monthCode\"],\n });\n return issues;\n }\n\n // Resolve effective Gregorian year\n let effectiveYear = date.year;\n if (date.era) {\n const eraDef = resolveJapaneseEra(date.era);\n if (!eraDef) {\n issues.push({\n code: \"CHRONERA_INVALID_DATE\",\n message: `Unknown Japanese era: \"${date.era}\". Expected reiwa, heisei, showa, taisho, meiji.`,\n path: [\"era\"],\n });\n return issues;\n }\n if (date.eraYear !== undefined) {\n effectiveYear = eraDef.offset + date.eraYear;\n } else if (date.year < 100) {\n effectiveYear = eraDef.offset + date.year;\n }\n }\n\n if (effectiveYear < 1868 || effectiveYear > 9999) {\n issues.push({\n code: \"CHRONERA_OUT_OF_RANGE\",\n message: `Japanese calendar supports dates from 1868 (Meiji 1) to 9999 CE. Received year: ${effectiveYear}.`,\n path: [\"year\"],\n });\n }\n\n const maxDays = daysInGregorianMonth(effectiveYear, monthNum);\n if (date.day < 1 || date.day > maxDays) {\n issues.push({\n code: \"CHRONERA_INVALID_DATE\",\n message: `Invalid day ${date.day} for year ${effectiveYear} month ${monthNum}. Expected 1..${maxDays}.`,\n path: [\"day\"],\n });\n }\n\n return issues;\n },\n\n daysInMonth(year: number, monthCode: MonthCode): number {\n const month = parseGregorianMonthCode(monthCode);\n if (month === null) {\n throw new ChroneraError(\n \"CHRONERA_INVALID_DATE\",\n `Invalid monthCode: \"${monthCode}\". Expected M01-M12.`,\n );\n }\n return daysInGregorianMonth(year, month);\n },\n\n isLeapYear(year: number): boolean {\n return isGregorianLeapYear(year);\n },\n};\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { formatGregorianMonthCode, parseGregorianMonthCode, } from "../../core/gregorian-math.js";
|
|
2
|
+
import { ChroneraError } from "../../errors/errors.js";
|
|
3
|
+
import { PERSIAN_EPOCH_ABSOLUTE_DAY, PERSIAN_IDENTITY } from "./constants.js";
|
|
4
|
+
import { absoluteDayToPersian, persianToAbsoluteDay } from "./math.js";
|
|
5
|
+
import { persianValidator } from "./validator.js";
|
|
6
|
+
export const persianAdapter = {
|
|
7
|
+
identity: PERSIAN_IDENTITY,
|
|
8
|
+
validator: persianValidator,
|
|
9
|
+
converter: {
|
|
10
|
+
identity: PERSIAN_IDENTITY,
|
|
11
|
+
toAbsoluteDay(date) {
|
|
12
|
+
const month = parseGregorianMonthCode(date.monthCode);
|
|
13
|
+
if (month === null) {
|
|
14
|
+
throw new ChroneraError("CHRONERA_INVALID_DATE", `Invalid monthCode: "${date.monthCode}". Expected M01-M12.`);
|
|
15
|
+
}
|
|
16
|
+
if (date.year < 1 || date.year > 9999) {
|
|
17
|
+
throw new ChroneraError("CHRONERA_OUT_OF_RANGE", `Persian calendar supports years 1..9999. Received year ${date.year}.`);
|
|
18
|
+
}
|
|
19
|
+
return persianToAbsoluteDay(date.year, month, date.day);
|
|
20
|
+
},
|
|
21
|
+
fromAbsoluteDay(absoluteDay) {
|
|
22
|
+
if (absoluteDay < PERSIAN_EPOCH_ABSOLUTE_DAY) {
|
|
23
|
+
throw new ChroneraError("CHRONERA_OUT_OF_RANGE", `Date is before Persian calendar epoch (622-03-22 CE).`);
|
|
24
|
+
}
|
|
25
|
+
const { year, month, day } = absoluteDayToPersian(absoluteDay);
|
|
26
|
+
if (year > 9999) {
|
|
27
|
+
throw new ChroneraError("CHRONERA_OUT_OF_RANGE", `Persian year exceeds maximum supported year 9999.`);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
kind: "calendar-date",
|
|
31
|
+
calendar: "persian",
|
|
32
|
+
era: "ap", // Anno Persico / Solar Hijri
|
|
33
|
+
eraYear: year,
|
|
34
|
+
year,
|
|
35
|
+
monthCode: formatGregorianMonthCode(month),
|
|
36
|
+
month,
|
|
37
|
+
day,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/calendar/persian/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKlD,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,QAAQ,EAAE,gBAAgB;IAC1B,SAAS,EAAE,gBAAgB;IAC3B,SAAS,EAAE;QACT,QAAQ,EAAE,gBAAgB;QAC1B,aAAa,CAAC,IAAkB;YAC9B,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,MAAM,IAAI,aAAa,CACrB,uBAAuB,EACvB,uBAAuB,IAAI,CAAC,SAAS,sBAAsB,CAC5D,CAAC;YACJ,CAAC;YAED,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;gBACtC,MAAM,IAAI,aAAa,CACrB,uBAAuB,EACvB,0DAA0D,IAAI,CAAC,IAAI,GAAG,CACvE,CAAC;YACJ,CAAC;YAED,OAAO,oBAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1D,CAAC;QAED,eAAe,CAAC,WAAmB;YACjC,IAAI,WAAW,GAAG,0BAA0B,EAAE,CAAC;gBAC7C,MAAM,IAAI,aAAa,CACrB,uBAAuB,EACvB,uDAAuD,CACxD,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;YAC/D,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;gBAChB,MAAM,IAAI,aAAa,CACrB,uBAAuB,EACvB,mDAAmD,CACpD,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,IAAI,EAAE,eAAe;gBACrB,QAAQ,EAAE,SAAS;gBACnB,GAAG,EAAE,IAAI,EAAE,6BAA6B;gBACxC,OAAO,EAAE,IAAI;gBACb,IAAI;gBACJ,SAAS,EAAE,wBAAwB,CAAC,KAAK,CAAC;gBAC1C,KAAK;gBACL,GAAG;aACJ,CAAC;QACJ,CAAC;KACF;CACF,CAAC","sourcesContent":["import {\n formatGregorianMonthCode,\n parseGregorianMonthCode,\n} from \"../../core/gregorian-math.js\";\nimport { ChroneraError } from \"../../errors/errors.js\";\nimport { PERSIAN_EPOCH_ABSOLUTE_DAY, PERSIAN_IDENTITY } from \"./constants.js\";\nimport { absoluteDayToPersian, persianToAbsoluteDay } from \"./math.js\";\nimport { persianValidator } from \"./validator.js\";\n\nimport type { CalendarAdapter } from \"../types.js\";\nimport type { CalendarDate } from \"../../public-types.js\";\n\nexport const persianAdapter: CalendarAdapter = {\n identity: PERSIAN_IDENTITY,\n validator: persianValidator,\n converter: {\n identity: PERSIAN_IDENTITY,\n toAbsoluteDay(date: CalendarDate): number {\n const month = parseGregorianMonthCode(date.monthCode);\n if (month === null) {\n throw new ChroneraError(\n \"CHRONERA_INVALID_DATE\",\n `Invalid monthCode: \"${date.monthCode}\". Expected M01-M12.`,\n );\n }\n\n if (date.year < 1 || date.year > 9999) {\n throw new ChroneraError(\n \"CHRONERA_OUT_OF_RANGE\",\n `Persian calendar supports years 1..9999. Received year ${date.year}.`,\n );\n }\n\n return persianToAbsoluteDay(date.year, month, date.day);\n },\n\n fromAbsoluteDay(absoluteDay: number): CalendarDate {\n if (absoluteDay < PERSIAN_EPOCH_ABSOLUTE_DAY) {\n throw new ChroneraError(\n \"CHRONERA_OUT_OF_RANGE\",\n `Date is before Persian calendar epoch (622-03-22 CE).`,\n );\n }\n\n const { year, month, day } = absoluteDayToPersian(absoluteDay);\n if (year > 9999) {\n throw new ChroneraError(\n \"CHRONERA_OUT_OF_RANGE\",\n `Persian year exceeds maximum supported year 9999.`,\n );\n }\n\n return {\n kind: \"calendar-date\",\n calendar: \"persian\",\n era: \"ap\", // Anno Persico / Solar Hijri\n eraYear: year,\n year,\n monthCode: formatGregorianMonthCode(month),\n month,\n day,\n };\n },\n },\n};\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CalendarIdentity } from "../types.js";
|
|
2
|
+
export declare const PERSIAN_EPOCH_ABSOLUTE_DAY = -492267;
|
|
3
|
+
export declare const PERSIAN_IDENTITY: CalendarIdentity;
|
|
4
|
+
export declare const PERSIAN_MONTH_NAMES: readonly ["Farvardin", "Ordibehesht", "Khordad", "Tir", "Mordad", "Shahrivar", "Mehr", "Aban", "Azar", "Dey", "Bahman", "Esfand"];
|
|
5
|
+
export declare const PERSIAN_MONTH_NAMES_FA: readonly ["فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MAX_ABSOLUTE_DAY } from "../../core/absolute-day.js";
|
|
2
|
+
// Epoch: 1 Farvardin 1 AP = 622-03-22 CE. Absolute day: -492267
|
|
3
|
+
export const PERSIAN_EPOCH_ABSOLUTE_DAY = -492267;
|
|
4
|
+
export const PERSIAN_IDENTITY = {
|
|
5
|
+
id: "persian",
|
|
6
|
+
algorithm: "chronera-persian-v1",
|
|
7
|
+
deterministic: true,
|
|
8
|
+
validRange: {
|
|
9
|
+
first: PERSIAN_EPOCH_ABSOLUTE_DAY,
|
|
10
|
+
last: MAX_ABSOLUTE_DAY,
|
|
11
|
+
},
|
|
12
|
+
validFrom: { kind: "local-date", year: 622, month: 3, day: 22 },
|
|
13
|
+
validTo: { kind: "local-date", year: 9999, month: 12, day: 31 },
|
|
14
|
+
};
|
|
15
|
+
export const PERSIAN_MONTH_NAMES = [
|
|
16
|
+
"Farvardin",
|
|
17
|
+
"Ordibehesht",
|
|
18
|
+
"Khordad",
|
|
19
|
+
"Tir",
|
|
20
|
+
"Mordad",
|
|
21
|
+
"Shahrivar",
|
|
22
|
+
"Mehr",
|
|
23
|
+
"Aban",
|
|
24
|
+
"Azar",
|
|
25
|
+
"Dey",
|
|
26
|
+
"Bahman",
|
|
27
|
+
"Esfand",
|
|
28
|
+
];
|
|
29
|
+
export const PERSIAN_MONTH_NAMES_FA = [
|
|
30
|
+
"فروردین",
|
|
31
|
+
"اردیبهشت",
|
|
32
|
+
"خرداد",
|
|
33
|
+
"تیر",
|
|
34
|
+
"مرداد",
|
|
35
|
+
"شهریور",
|
|
36
|
+
"مهر",
|
|
37
|
+
"آبان",
|
|
38
|
+
"آذر",
|
|
39
|
+
"دی",
|
|
40
|
+
"بهمن",
|
|
41
|
+
"اسفند",
|
|
42
|
+
];
|
|
43
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/calendar/persian/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG9D,gEAAgE;AAChE,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,MAAM,CAAC;AAElD,MAAM,CAAC,MAAM,gBAAgB,GAAqB;IAChD,EAAE,EAAE,SAAS;IACb,SAAS,EAAE,qBAAqB;IAChC,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,gBAAgB;KACvB;IACD,SAAS,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IAC/D,OAAO,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;CAChE,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,WAAW;IACX,aAAa;IACb,SAAS;IACT,KAAK;IACL,QAAQ;IACR,WAAW;IACX,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,QAAQ;IACR,QAAQ;CACA,CAAC;AAEX,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,SAAS;IACT,UAAU;IACV,OAAO;IACP,KAAK;IACL,OAAO;IACP,QAAQ;IACR,KAAK;IACL,MAAM;IACN,KAAK;IACL,IAAI;IACJ,MAAM;IACN,OAAO;CACC,CAAC","sourcesContent":["import { MAX_ABSOLUTE_DAY } from \"../../core/absolute-day.js\";\nimport type { CalendarIdentity } from \"../types.js\";\n\n// Epoch: 1 Farvardin 1 AP = 622-03-22 CE. Absolute day: -492267\nexport const PERSIAN_EPOCH_ABSOLUTE_DAY = -492267;\n\nexport const PERSIAN_IDENTITY: CalendarIdentity = {\n id: \"persian\",\n algorithm: \"chronera-persian-v1\",\n deterministic: true,\n validRange: {\n first: PERSIAN_EPOCH_ABSOLUTE_DAY,\n last: MAX_ABSOLUTE_DAY,\n },\n validFrom: { kind: \"local-date\", year: 622, month: 3, day: 22 },\n validTo: { kind: \"local-date\", year: 9999, month: 12, day: 31 },\n};\n\nexport const PERSIAN_MONTH_NAMES = [\n \"Farvardin\",\n \"Ordibehesht\",\n \"Khordad\",\n \"Tir\",\n \"Mordad\",\n \"Shahrivar\",\n \"Mehr\",\n \"Aban\",\n \"Azar\",\n \"Dey\",\n \"Bahman\",\n \"Esfand\",\n] as const;\n\nexport const PERSIAN_MONTH_NAMES_FA = [\n \"فروردین\",\n \"اردیبهشت\",\n \"خرداد\",\n \"تیر\",\n \"مرداد\",\n \"شهریور\",\n \"مهر\",\n \"آبان\",\n \"آذر\",\n \"دی\",\n \"بهمن\",\n \"اسفند\",\n] as const;\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { PERSIAN_EPOCH_ABSOLUTE_DAY, PERSIAN_MONTH_NAMES, PERSIAN_MONTH_NAMES_FA, } from "./constants.js";
|
|
2
|
+
export { isPersianLeapYear, daysInPersianMonth, persianToAbsoluteDay, absoluteDayToPersian, persianToGregorianFields, gregorianToPersianFields, } from "./math.js";
|
|
3
|
+
export { persianValidator, assertPersianDate } from "./validator.js";
|
|
4
|
+
export { persianAdapter } from "./adapter.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { PERSIAN_EPOCH_ABSOLUTE_DAY, PERSIAN_MONTH_NAMES, PERSIAN_MONTH_NAMES_FA, } from "./constants.js";
|
|
2
|
+
export { isPersianLeapYear, daysInPersianMonth, persianToAbsoluteDay, absoluteDayToPersian, persianToGregorianFields, gregorianToPersianFields, } from "./math.js";
|
|
3
|
+
export { persianValidator, assertPersianDate } from "./validator.js";
|
|
4
|
+
export { persianAdapter } from "./adapter.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/calendar/persian/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export {\n PERSIAN_EPOCH_ABSOLUTE_DAY,\n PERSIAN_MONTH_NAMES,\n PERSIAN_MONTH_NAMES_FA,\n} from \"./constants.js\";\nexport {\n isPersianLeapYear,\n daysInPersianMonth,\n persianToAbsoluteDay,\n absoluteDayToPersian,\n persianToGregorianFields,\n gregorianToPersianFields,\n} from \"./math.js\";\nexport { persianValidator, assertPersianDate } from \"./validator.js\";\nexport { persianAdapter } from \"./adapter.js\";\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts Persian (Solar Hijri / Jalali) year, month, day to Gregorian year, month, day.
|
|
3
|
+
*/
|
|
4
|
+
export declare function persianToGregorianFields(jy: number, jm: number, jd: number): {
|
|
5
|
+
year: number;
|
|
6
|
+
month: number;
|
|
7
|
+
day: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Converts Gregorian year, month, day to Persian (Solar Hijri / Jalali) year, month, day.
|
|
11
|
+
*/
|
|
12
|
+
export declare function gregorianToPersianFields(gy: number, gm: number, gd: number): {
|
|
13
|
+
year: number;
|
|
14
|
+
month: number;
|
|
15
|
+
day: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function persianToAbsoluteDay(year: number, month: number, day: number): number;
|
|
18
|
+
export declare function absoluteDayToPersian(absoluteDay: number): {
|
|
19
|
+
year: number;
|
|
20
|
+
month: number;
|
|
21
|
+
day: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function isPersianLeapYear(year: number): boolean;
|
|
24
|
+
export declare function daysInPersianMonth(year: number, month: number): number;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { absoluteDayFromGregorianFields, gregorianFieldsFromAbsoluteDay, } from "../../core/absolute-day.js";
|
|
2
|
+
/**
|
|
3
|
+
* Converts Persian (Solar Hijri / Jalali) year, month, day to Gregorian year, month, day.
|
|
4
|
+
*/
|
|
5
|
+
export function persianToGregorianFields(jy, jm, jd) {
|
|
6
|
+
let gy = jy <= 979 ? 621 : 1600;
|
|
7
|
+
let adjJy = jy - (jy <= 979 ? 0 : 979);
|
|
8
|
+
let days = 365 * adjJy +
|
|
9
|
+
Math.floor(adjJy / 33) * 8 +
|
|
10
|
+
Math.floor(((adjJy % 33) + 3) / 4) +
|
|
11
|
+
78 +
|
|
12
|
+
jd +
|
|
13
|
+
(jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186);
|
|
14
|
+
gy += 400 * Math.floor(days / 146097);
|
|
15
|
+
days %= 146097;
|
|
16
|
+
if (days > 36524) {
|
|
17
|
+
gy += 100 * Math.floor(--days / 36524);
|
|
18
|
+
days %= 36524;
|
|
19
|
+
if (days >= 365)
|
|
20
|
+
days++;
|
|
21
|
+
}
|
|
22
|
+
gy += 4 * Math.floor(days / 1461);
|
|
23
|
+
days %= 1461;
|
|
24
|
+
if (days > 365) {
|
|
25
|
+
gy += Math.floor((days - 1) / 365);
|
|
26
|
+
days = (days - 1) % 365;
|
|
27
|
+
}
|
|
28
|
+
let gd = days + 1;
|
|
29
|
+
const isLeapGreg = (gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0;
|
|
30
|
+
const sal_a = [
|
|
31
|
+
0,
|
|
32
|
+
31,
|
|
33
|
+
isLeapGreg ? 29 : 28,
|
|
34
|
+
31,
|
|
35
|
+
30,
|
|
36
|
+
31,
|
|
37
|
+
30,
|
|
38
|
+
31,
|
|
39
|
+
31,
|
|
40
|
+
30,
|
|
41
|
+
31,
|
|
42
|
+
30,
|
|
43
|
+
31,
|
|
44
|
+
];
|
|
45
|
+
let gm = 0;
|
|
46
|
+
for (gm = 0; gm < 13 && gd > sal_a[gm]; gm++) {
|
|
47
|
+
gd -= sal_a[gm];
|
|
48
|
+
}
|
|
49
|
+
return { year: gy, month: gm, day: gd };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Converts Gregorian year, month, day to Persian (Solar Hijri / Jalali) year, month, day.
|
|
53
|
+
*/
|
|
54
|
+
export function gregorianToPersianFields(gy, gm, gd) {
|
|
55
|
+
const g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
|
56
|
+
const gy2 = gm > 2 ? gy + 1 : gy;
|
|
57
|
+
let days = 355666 +
|
|
58
|
+
365 * gy +
|
|
59
|
+
Math.floor((gy2 + 3) / 4) -
|
|
60
|
+
Math.floor((gy2 + 99) / 100) +
|
|
61
|
+
Math.floor((gy2 + 399) / 400) +
|
|
62
|
+
gd +
|
|
63
|
+
g_d_m[gm - 1];
|
|
64
|
+
let jy = -1595 + 33 * Math.floor(days / 12053);
|
|
65
|
+
days %= 12053;
|
|
66
|
+
jy += 4 * Math.floor(days / 1461);
|
|
67
|
+
days %= 1461;
|
|
68
|
+
if (days > 365) {
|
|
69
|
+
jy += Math.floor((days - 1) / 365);
|
|
70
|
+
days = (days - 1) % 365;
|
|
71
|
+
}
|
|
72
|
+
let jm;
|
|
73
|
+
let jd;
|
|
74
|
+
if (days < 186) {
|
|
75
|
+
jm = 1 + Math.floor(days / 31);
|
|
76
|
+
jd = 1 + (days % 31);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
jm = 7 + Math.floor((days - 186) / 30);
|
|
80
|
+
jd = 1 + ((days - 186) % 30);
|
|
81
|
+
}
|
|
82
|
+
return { year: jy, month: jm, day: jd };
|
|
83
|
+
}
|
|
84
|
+
export function persianToAbsoluteDay(year, month, day) {
|
|
85
|
+
const g = persianToGregorianFields(year, month, day);
|
|
86
|
+
return absoluteDayFromGregorianFields(g.year, g.month, g.day);
|
|
87
|
+
}
|
|
88
|
+
export function absoluteDayToPersian(absoluteDay) {
|
|
89
|
+
const g = gregorianFieldsFromAbsoluteDay(absoluteDay);
|
|
90
|
+
return gregorianToPersianFields(g.year, g.month, g.day);
|
|
91
|
+
}
|
|
92
|
+
export function isPersianLeapYear(year) {
|
|
93
|
+
const day1 = persianToAbsoluteDay(year, 1, 1);
|
|
94
|
+
const dayNext = persianToAbsoluteDay(year + 1, 1, 1);
|
|
95
|
+
return dayNext - day1 === 366;
|
|
96
|
+
}
|
|
97
|
+
export function daysInPersianMonth(year, month) {
|
|
98
|
+
if (month < 1 || month > 12) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
if (month <= 6) {
|
|
102
|
+
return 31;
|
|
103
|
+
}
|
|
104
|
+
if (month <= 11) {
|
|
105
|
+
return 30;
|
|
106
|
+
}
|
|
107
|
+
return isPersianLeapYear(year) ? 30 : 29;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=math.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../../../src/calendar/persian/math.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n absoluteDayFromGregorianFields,\n gregorianFieldsFromAbsoluteDay,\n} from \"../../core/absolute-day.js\";\n\n/**\n * Converts Persian (Solar Hijri / Jalali) year, month, day to Gregorian year, month, day.\n */\nexport function persianToGregorianFields(\n jy: number,\n jm: number,\n jd: number,\n): { year: number; month: number; day: number } {\n let gy = jy <= 979 ? 621 : 1600;\n let adjJy = jy - (jy <= 979 ? 0 : 979);\n let days =\n 365 * adjJy +\n Math.floor(adjJy / 33) * 8 +\n Math.floor(((adjJy % 33) + 3) / 4) +\n 78 +\n jd +\n (jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186);\n\n gy += 400 * Math.floor(days / 146097);\n days %= 146097;\n\n if (days > 36524) {\n gy += 100 * Math.floor(--days / 36524);\n days %= 36524;\n if (days >= 365) days++;\n }\n\n gy += 4 * Math.floor(days / 1461);\n days %= 1461;\n\n if (days > 365) {\n gy += Math.floor((days - 1) / 365);\n days = (days - 1) % 365;\n }\n\n let gd = days + 1;\n const isLeapGreg = (gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0;\n const sal_a = [\n 0,\n 31,\n isLeapGreg ? 29 : 28,\n 31,\n 30,\n 31,\n 30,\n 31,\n 31,\n 30,\n 31,\n 30,\n 31,\n ];\n\n let gm = 0;\n for (gm = 0; gm < 13 && gd > sal_a[gm]!; gm++) {\n gd -= sal_a[gm]!;\n }\n\n return { year: gy, month: gm, day: gd };\n}\n\n/**\n * Converts Gregorian year, month, day to Persian (Solar Hijri / Jalali) year, month, day.\n */\nexport function gregorianToPersianFields(\n gy: number,\n gm: number,\n gd: number,\n): { year: number; month: number; day: number } {\n const g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n const gy2 = gm > 2 ? gy + 1 : gy;\n let days =\n 355666 +\n 365 * gy +\n Math.floor((gy2 + 3) / 4) -\n Math.floor((gy2 + 99) / 100) +\n Math.floor((gy2 + 399) / 400) +\n gd +\n g_d_m[gm - 1]!;\n\n let jy = -1595 + 33 * Math.floor(days / 12053);\n days %= 12053;\n\n jy += 4 * Math.floor(days / 1461);\n days %= 1461;\n\n if (days > 365) {\n jy += Math.floor((days - 1) / 365);\n days = (days - 1) % 365;\n }\n\n let jm: number;\n let jd: number;\n if (days < 186) {\n jm = 1 + Math.floor(days / 31);\n jd = 1 + (days % 31);\n } else {\n jm = 7 + Math.floor((days - 186) / 30);\n jd = 1 + ((days - 186) % 30);\n }\n\n return { year: jy, month: jm, day: jd };\n}\n\nexport function persianToAbsoluteDay(\n year: number,\n month: number,\n day: number,\n): number {\n const g = persianToGregorianFields(year, month, day);\n return absoluteDayFromGregorianFields(g.year, g.month, g.day);\n}\n\nexport function absoluteDayToPersian(absoluteDay: number): {\n year: number;\n month: number;\n day: number;\n} {\n const g = gregorianFieldsFromAbsoluteDay(absoluteDay);\n return gregorianToPersianFields(g.year, g.month, g.day);\n}\n\nexport function isPersianLeapYear(year: number): boolean {\n const day1 = persianToAbsoluteDay(year, 1, 1);\n const dayNext = persianToAbsoluteDay(year + 1, 1, 1);\n return dayNext - day1 === 366;\n}\n\nexport function daysInPersianMonth(year: number, month: number): number {\n if (month < 1 || month > 12) {\n return 0;\n }\n if (month <= 6) {\n return 31;\n }\n if (month <= 11) {\n return 30;\n }\n return isPersianLeapYear(year) ? 30 : 29;\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { parseGregorianMonthCode } from "../../core/gregorian-math.js";
|
|
2
|
+
import { ChroneraError } from "../../errors/errors.js";
|
|
3
|
+
import { PERSIAN_IDENTITY } from "./constants.js";
|
|
4
|
+
import { daysInPersianMonth, isPersianLeapYear } from "./math.js";
|
|
5
|
+
export function assertPersianDate(date) {
|
|
6
|
+
if (date.calendar !== "persian") {
|
|
7
|
+
throw new ChroneraError("CHRONERA_INVALID_CALENDAR", `Expected calendar "persian", received "${date.calendar}".`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export const persianValidator = {
|
|
11
|
+
identity: PERSIAN_IDENTITY,
|
|
12
|
+
validate(date) {
|
|
13
|
+
assertPersianDate(date);
|
|
14
|
+
const issues = [];
|
|
15
|
+
const monthNum = parseGregorianMonthCode(date.monthCode);
|
|
16
|
+
if (monthNum === null) {
|
|
17
|
+
issues.push({
|
|
18
|
+
code: "CHRONERA_INVALID_DATE",
|
|
19
|
+
message: `Invalid monthCode: "${date.monthCode}". Expected M01-M12.`,
|
|
20
|
+
path: ["monthCode"],
|
|
21
|
+
});
|
|
22
|
+
return issues;
|
|
23
|
+
}
|
|
24
|
+
if (date.year < 1 || date.year > 9999) {
|
|
25
|
+
issues.push({
|
|
26
|
+
code: "CHRONERA_OUT_OF_RANGE",
|
|
27
|
+
message: `Persian calendar supports years 1..9999. Received year: ${date.year}.`,
|
|
28
|
+
path: ["year"],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const maxDays = daysInPersianMonth(date.year, monthNum);
|
|
32
|
+
if (date.day < 1 || date.day > maxDays) {
|
|
33
|
+
issues.push({
|
|
34
|
+
code: "CHRONERA_INVALID_DATE",
|
|
35
|
+
message: `Invalid day ${date.day} for Persian year ${date.year} month ${monthNum}. Expected 1..${maxDays}.`,
|
|
36
|
+
path: ["day"],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return issues;
|
|
40
|
+
},
|
|
41
|
+
daysInMonth(year, monthCode) {
|
|
42
|
+
const month = parseGregorianMonthCode(monthCode);
|
|
43
|
+
if (month === null) {
|
|
44
|
+
throw new ChroneraError("CHRONERA_INVALID_DATE", `Invalid monthCode: "${monthCode}". Expected M01-M12.`);
|
|
45
|
+
}
|
|
46
|
+
return daysInPersianMonth(year, month);
|
|
47
|
+
},
|
|
48
|
+
isLeapYear(year) {
|
|
49
|
+
return isPersianLeapYear(year);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/calendar/persian/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AASlE,MAAM,UAAU,iBAAiB,CAAC,IAAkB;IAClD,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,aAAa,CACrB,2BAA2B,EAC3B,0CAA0C,IAAI,CAAC,QAAQ,IAAI,CAC5D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD,QAAQ,EAAE,gBAAgB;IAE1B,QAAQ,CAAC,IAAkB;QACzB,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,MAAM,GAAoB,EAAE,CAAC;QAEnC,MAAM,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,uBAAuB,IAAI,CAAC,SAAS,sBAAsB;gBACpE,IAAI,EAAE,CAAC,WAAW,CAAC;aACpB,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,2DAA2D,IAAI,CAAC,IAAI,GAAG;gBAChF,IAAI,EAAE,CAAC,MAAM,CAAC;aACf,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,qBAAqB,IAAI,CAAC,IAAI,UAAU,QAAQ,iBAAiB,OAAO,GAAG;gBAC3G,IAAI,EAAE,CAAC,KAAK,CAAC;aACd,CAAC,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,WAAW,CAAC,IAAY,EAAE,SAAoB;QAC5C,MAAM,KAAK,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;QACjD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,aAAa,CACrB,uBAAuB,EACvB,uBAAuB,SAAS,sBAAsB,CACvD,CAAC;QACJ,CAAC;QACD,OAAO,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;CACF,CAAC","sourcesContent":["import { parseGregorianMonthCode } from \"../../core/gregorian-math.js\";\nimport { ChroneraError } from \"../../errors/errors.js\";\nimport { PERSIAN_IDENTITY } from \"./constants.js\";\nimport { daysInPersianMonth, isPersianLeapYear } from \"./math.js\";\n\nimport type { CalendarValidator } from \"../types.js\";\nimport type {\n CalendarDate,\n ChroneraIssue,\n MonthCode,\n} from \"../../public-types.js\";\n\nexport function assertPersianDate(date: CalendarDate): void {\n if (date.calendar !== \"persian\") {\n throw new ChroneraError(\n \"CHRONERA_INVALID_CALENDAR\",\n `Expected calendar \"persian\", received \"${date.calendar}\".`,\n );\n }\n}\n\nexport const persianValidator: CalendarValidator = {\n identity: PERSIAN_IDENTITY,\n\n validate(date: CalendarDate): readonly ChroneraIssue[] {\n assertPersianDate(date);\n const issues: ChroneraIssue[] = [];\n\n const monthNum = parseGregorianMonthCode(date.monthCode);\n if (monthNum === null) {\n issues.push({\n code: \"CHRONERA_INVALID_DATE\",\n message: `Invalid monthCode: \"${date.monthCode}\". Expected M01-M12.`,\n path: [\"monthCode\"],\n });\n return issues;\n }\n\n if (date.year < 1 || date.year > 9999) {\n issues.push({\n code: \"CHRONERA_OUT_OF_RANGE\",\n message: `Persian calendar supports years 1..9999. Received year: ${date.year}.`,\n path: [\"year\"],\n });\n }\n\n const maxDays = daysInPersianMonth(date.year, monthNum);\n if (date.day < 1 || date.day > maxDays) {\n issues.push({\n code: \"CHRONERA_INVALID_DATE\",\n message: `Invalid day ${date.day} for Persian year ${date.year} month ${monthNum}. Expected 1..${maxDays}.`,\n path: [\"day\"],\n });\n }\n\n return issues;\n },\n\n daysInMonth(year: number, monthCode: MonthCode): number {\n const month = parseGregorianMonthCode(monthCode);\n if (month === null) {\n throw new ChroneraError(\n \"CHRONERA_INVALID_DATE\",\n `Invalid monthCode: \"${monthCode}\". Expected M01-M12.`,\n );\n }\n return daysInPersianMonth(year, month);\n },\n\n isLeapYear(year: number): boolean {\n return isPersianLeapYear(year);\n },\n};\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CalendarAdapter } from "./types.js";
|
|
2
|
+
import type { CalendarId, CalendarPlugin } from "../public-types.js";
|
|
3
|
+
export declare class CalendarRegistry {
|
|
4
|
+
private readonly adapters;
|
|
5
|
+
constructor(customPlugins?: readonly CalendarPlugin[]);
|
|
6
|
+
getAdapter(id: CalendarId): CalendarAdapter;
|
|
7
|
+
hasAdapter(id: CalendarId): boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const defaultCalendarRegistry: CalendarRegistry;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { gregorianAdapter, iso8601Adapter } from "./gregory/adapter.js";
|
|
2
|
+
import { buddhistAdapter } from "./buddhist/adapter.js";
|
|
3
|
+
import { islamicCivilAdapter, islamicTblaAdapter, islamicUmalquraAdapter, } from "./hijri/index.js";
|
|
4
|
+
import { japaneseAdapter } from "./japanese/index.js";
|
|
5
|
+
import { rocAdapter } from "./roc/index.js";
|
|
6
|
+
import { persianAdapter } from "./persian/index.js";
|
|
7
|
+
import { indianAdapter } from "./indian/index.js";
|
|
8
|
+
import { ChroneraError } from "../errors/errors.js";
|
|
9
|
+
import { absoluteDayFromGregorian, gregorianFromAbsoluteDay, } from "./gregory/absolute-day.js";
|
|
10
|
+
const BUILT_IN_ADAPTERS = new Map([
|
|
11
|
+
["gregory", gregorianAdapter],
|
|
12
|
+
["iso8601", iso8601Adapter],
|
|
13
|
+
["buddhist", buddhistAdapter],
|
|
14
|
+
["islamic-civil", islamicCivilAdapter],
|
|
15
|
+
["islamic-tbla", islamicTblaAdapter],
|
|
16
|
+
["islamic-umalqura", islamicUmalquraAdapter],
|
|
17
|
+
["japanese", japaneseAdapter],
|
|
18
|
+
["roc", rocAdapter],
|
|
19
|
+
["persian", persianAdapter],
|
|
20
|
+
["indian", indianAdapter],
|
|
21
|
+
]);
|
|
22
|
+
export class CalendarRegistry {
|
|
23
|
+
adapters = new Map(BUILT_IN_ADAPTERS);
|
|
24
|
+
constructor(customPlugins) {
|
|
25
|
+
if (customPlugins) {
|
|
26
|
+
if (customPlugins.length > 64) {
|
|
27
|
+
throw new ChroneraError("CHRONERA_INPUT_TOO_LARGE", `Custom calendar plugins exceed maximum limit of 64; received ${customPlugins.length}.`);
|
|
28
|
+
}
|
|
29
|
+
for (const plugin of customPlugins) {
|
|
30
|
+
if (BUILT_IN_ADAPTERS.has(plugin.id)) {
|
|
31
|
+
throw new ChroneraError("CHRONERA_INCOMPATIBLE_OPTION", `Overriding built-in calendar adapter "${plugin.id}" is forbidden.`);
|
|
32
|
+
}
|
|
33
|
+
const adapter = {
|
|
34
|
+
identity: {
|
|
35
|
+
id: plugin.id,
|
|
36
|
+
algorithm: plugin.algorithm,
|
|
37
|
+
deterministic: plugin.deterministic,
|
|
38
|
+
validRange: {
|
|
39
|
+
first: absoluteDayFromGregorian({
|
|
40
|
+
year: plugin.validFrom.year,
|
|
41
|
+
monthCode: `M${String(plugin.validFrom.month).padStart(2, "0")}`,
|
|
42
|
+
day: plugin.validFrom.day,
|
|
43
|
+
}),
|
|
44
|
+
last: absoluteDayFromGregorian({
|
|
45
|
+
year: plugin.validTo.year,
|
|
46
|
+
monthCode: `M${String(plugin.validTo.month).padStart(2, "0")}`,
|
|
47
|
+
day: plugin.validTo.day,
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
validFrom: plugin.validFrom,
|
|
51
|
+
validTo: plugin.validTo,
|
|
52
|
+
},
|
|
53
|
+
converter: {
|
|
54
|
+
identity: {
|
|
55
|
+
id: plugin.id,
|
|
56
|
+
algorithm: plugin.algorithm,
|
|
57
|
+
deterministic: plugin.deterministic,
|
|
58
|
+
validRange: {
|
|
59
|
+
first: -719162,
|
|
60
|
+
last: 2932896,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
toAbsoluteDay(date) {
|
|
64
|
+
const iso = plugin.toIsoDate(date);
|
|
65
|
+
return absoluteDayFromGregorian({
|
|
66
|
+
year: iso.year,
|
|
67
|
+
monthCode: `M${String(iso.month).padStart(2, "0")}`,
|
|
68
|
+
day: iso.day,
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
fromAbsoluteDay(day) {
|
|
72
|
+
const greg = gregorianFromAbsoluteDay(day);
|
|
73
|
+
const iso = {
|
|
74
|
+
kind: "local-date",
|
|
75
|
+
year: greg.year,
|
|
76
|
+
month: greg.month,
|
|
77
|
+
day: greg.day,
|
|
78
|
+
};
|
|
79
|
+
return plugin.fromIsoDate(iso);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
validator: {
|
|
83
|
+
identity: {
|
|
84
|
+
id: plugin.id,
|
|
85
|
+
algorithm: plugin.algorithm,
|
|
86
|
+
deterministic: plugin.deterministic,
|
|
87
|
+
validRange: {
|
|
88
|
+
first: -719162,
|
|
89
|
+
last: 2932896,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
validate(date) {
|
|
93
|
+
return plugin.validate(date);
|
|
94
|
+
},
|
|
95
|
+
daysInMonth(_year, _monthCode) {
|
|
96
|
+
return 30;
|
|
97
|
+
},
|
|
98
|
+
isLeapYear(_year) {
|
|
99
|
+
return false;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
this.adapters.set(plugin.id, adapter);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
getAdapter(id) {
|
|
108
|
+
const adapter = this.adapters.get(id);
|
|
109
|
+
if (!adapter) {
|
|
110
|
+
throw new ChroneraError("CHRONERA_UNSUPPORTED_CALENDAR", `Unsupported calendar identifier: "${id}".`);
|
|
111
|
+
}
|
|
112
|
+
return adapter;
|
|
113
|
+
}
|
|
114
|
+
hasAdapter(id) {
|
|
115
|
+
return this.adapters.has(id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export const defaultCalendarRegistry = new CalendarRegistry();
|
|
119
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/calendar/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { gregorianAdapter, iso8601Adapter } from \"./gregory/adapter.js\";\nimport { buddhistAdapter } from \"./buddhist/adapter.js\";\nimport {\n islamicCivilAdapter,\n islamicTblaAdapter,\n islamicUmalquraAdapter,\n} from \"./hijri/index.js\";\nimport { japaneseAdapter } from \"./japanese/index.js\";\nimport { rocAdapter } from \"./roc/index.js\";\nimport { persianAdapter } from \"./persian/index.js\";\nimport { indianAdapter } from \"./indian/index.js\";\nimport { ChroneraError } from \"../errors/errors.js\";\nimport {\n absoluteDayFromGregorian,\n gregorianFromAbsoluteDay,\n} from \"./gregory/absolute-day.js\";\n\nimport type { CalendarAdapter } from \"./types.js\";\nimport type {\n CalendarDate,\n CalendarId,\n CalendarPlugin,\n ChroneraIssue,\n LocalDate,\n MonthCode,\n} from \"../public-types.js\";\n\nconst BUILT_IN_ADAPTERS = new Map<string, CalendarAdapter>([\n [\"gregory\", gregorianAdapter],\n [\"iso8601\", iso8601Adapter],\n [\"buddhist\", buddhistAdapter],\n [\"islamic-civil\", islamicCivilAdapter],\n [\"islamic-tbla\", islamicTblaAdapter],\n [\"islamic-umalqura\", islamicUmalquraAdapter],\n [\"japanese\", japaneseAdapter],\n [\"roc\", rocAdapter],\n [\"persian\", persianAdapter],\n [\"indian\", indianAdapter],\n]);\n\nexport class CalendarRegistry {\n private readonly adapters = new Map<string, CalendarAdapter>(\n BUILT_IN_ADAPTERS,\n );\n\n constructor(customPlugins?: readonly CalendarPlugin[]) {\n if (customPlugins) {\n if (customPlugins.length > 64) {\n throw new ChroneraError(\n \"CHRONERA_INPUT_TOO_LARGE\",\n `Custom calendar plugins exceed maximum limit of 64; received ${customPlugins.length}.`,\n );\n }\n\n for (const plugin of customPlugins) {\n if (BUILT_IN_ADAPTERS.has(plugin.id)) {\n throw new ChroneraError(\n \"CHRONERA_INCOMPATIBLE_OPTION\",\n `Overriding built-in calendar adapter \"${plugin.id}\" is forbidden.`,\n );\n }\n\n const adapter: CalendarAdapter = {\n identity: {\n id: plugin.id,\n algorithm: plugin.algorithm,\n deterministic: plugin.deterministic,\n validRange: {\n first: absoluteDayFromGregorian({\n year: plugin.validFrom.year,\n monthCode:\n `M${String(plugin.validFrom.month).padStart(2, \"0\")}` as MonthCode,\n day: plugin.validFrom.day,\n }),\n last: absoluteDayFromGregorian({\n year: plugin.validTo.year,\n monthCode:\n `M${String(plugin.validTo.month).padStart(2, \"0\")}` as MonthCode,\n day: plugin.validTo.day,\n }),\n },\n validFrom: plugin.validFrom,\n validTo: plugin.validTo,\n },\n converter: {\n identity: {\n id: plugin.id,\n algorithm: plugin.algorithm,\n deterministic: plugin.deterministic,\n validRange: {\n first: -719162,\n last: 2932896,\n },\n },\n toAbsoluteDay(date: CalendarDate): number {\n const iso = plugin.toIsoDate(date);\n return absoluteDayFromGregorian({\n year: iso.year,\n monthCode:\n `M${String(iso.month).padStart(2, \"0\")}` as MonthCode,\n day: iso.day,\n });\n },\n fromAbsoluteDay(day: number): CalendarDate {\n const greg = gregorianFromAbsoluteDay(day);\n const iso: LocalDate = {\n kind: \"local-date\",\n year: greg.year,\n month: greg.month,\n day: greg.day,\n };\n return plugin.fromIsoDate(iso);\n },\n },\n validator: {\n identity: {\n id: plugin.id,\n algorithm: plugin.algorithm,\n deterministic: plugin.deterministic,\n validRange: {\n first: -719162,\n last: 2932896,\n },\n },\n validate(date: CalendarDate): readonly ChroneraIssue[] {\n return plugin.validate(date);\n },\n daysInMonth(_year: number, _monthCode: MonthCode): number {\n return 30;\n },\n isLeapYear(_year: number): boolean {\n return false;\n },\n },\n };\n\n this.adapters.set(plugin.id, adapter);\n }\n }\n }\n\n getAdapter(id: CalendarId): CalendarAdapter {\n const adapter = this.adapters.get(id);\n if (!adapter) {\n throw new ChroneraError(\n \"CHRONERA_UNSUPPORTED_CALENDAR\",\n `Unsupported calendar identifier: \"${id}\".`,\n );\n }\n return adapter;\n }\n\n hasAdapter(id: CalendarId): boolean {\n return this.adapters.has(id);\n }\n}\n\nexport const defaultCalendarRegistry = new CalendarRegistry();\n"]}
|