deploy: v0.12 fix 存档key名

This commit is contained in:
2026-06-24 03:09:02 +00:00
parent 7a08d49fe3
commit e0a5281119
58743 changed files with 7297269 additions and 39505 deletions
+26
View File
@@ -0,0 +1,26 @@
import type { Locale } from "date-fns";
import React from "react";
import { DateLib, type DateLibOptions } from "../index.js";
import type { DayPickerProps } from "../types/props.js";
/**
* Render the Hebrew (lunisolar) calendar.
*
* Months follow the Hebrew lunisolar cycle with leap years containing Adar I
* and Adar II. Weeks remain SundaySaturday.
*
* Defaults:
*
* - `locale`: `he`
* - `dir`: `rtl`
* - `numerals`: `latn`
*/
export declare function DayPicker(props: DayPickerProps & {
locale?: Locale;
dir?: DayPickerProps["dir"];
numerals?: DayPickerProps["numerals"];
dateLib?: DayPickerProps["dateLib"];
}): React.JSX.Element;
/** Returns the date library used in the Hebrew calendar. */
export declare const getDateLib: (options?: DateLibOptions) => DateLib;
export { enUS } from "../locale/en-US.js";
export { he } from "../locale/he.js";
+33
View File
@@ -0,0 +1,33 @@
import React from "react";
import { DateLib, DayPicker as DayPickerComponent, } from "../index.js";
import { he } from "../locale/he.js";
import * as hebrewDateLib from "./lib/index.js";
/**
* Render the Hebrew (lunisolar) calendar.
*
* Months follow the Hebrew lunisolar cycle with leap years containing Adar I
* and Adar II. Weeks remain SundaySaturday.
*
* Defaults:
*
* - `locale`: `he`
* - `dir`: `rtl`
* - `numerals`: `latn`
*/
export function DayPicker(props) {
const dateLib = getDateLib({
locale: props.locale,
weekStartsOn: props.broadcastCalendar ? 1 : props.weekStartsOn,
firstWeekContainsDate: props.firstWeekContainsDate,
useAdditionalWeekYearTokens: props.useAdditionalWeekYearTokens,
useAdditionalDayOfYearTokens: props.useAdditionalDayOfYearTokens,
timeZone: props.timeZone,
});
return (React.createElement(DayPickerComponent, { ...props, locale: props.locale ?? he, numerals: props.numerals ?? "latn", dir: props.dir ?? "rtl", dateLib: dateLib }));
}
/** Returns the date library used in the Hebrew calendar. */
export const getDateLib = (options) => {
return new DateLib(options, hebrewDateLib);
};
export { enUS } from "../locale/en-US.js";
export { he } from "../locale/he.js";
+1
View File
@@ -0,0 +1 @@
export declare function addMonths(date: Date, amount: number): Date;
+12
View File
@@ -0,0 +1,12 @@
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
import { clampHebrewDay, monthIndexToHebrewDate, monthsSinceEpoch, } from "../utils/serial.js";
export function addMonths(date, amount) {
if (amount === 0) {
return new Date(date.getTime());
}
const hebrew = toHebrewDate(date);
const targetIndex = monthsSinceEpoch(hebrew) + amount;
const target = monthIndexToHebrewDate(targetIndex, hebrew.day);
const day = clampHebrewDay(target.year, target.monthIndex, target.day);
return toGregorianDate({ ...target, day });
}
+1
View File
@@ -0,0 +1 @@
export declare function addYears(date: Date, amount: number): Date;
+9
View File
@@ -0,0 +1,9 @@
import { toHebrewDate } from "../utils/dateConversion.js";
import { setYear } from "./setYear.js";
export function addYears(date, amount) {
if (amount === 0) {
return new Date(date.getTime());
}
const hebrew = toHebrewDate(date);
return setYear(date, hebrew.year + amount);
}
@@ -0,0 +1 @@
export declare function differenceInCalendarMonths(dateLeft: Date, dateRight: Date): number;
@@ -0,0 +1,7 @@
import { toHebrewDate } from "../utils/dateConversion.js";
import { monthsSinceEpoch } from "../utils/serial.js";
export function differenceInCalendarMonths(dateLeft, dateRight) {
const left = toHebrewDate(dateLeft);
const right = toHebrewDate(dateRight);
return monthsSinceEpoch(left) - monthsSinceEpoch(right);
}
@@ -0,0 +1,2 @@
import { type Interval } from "date-fns";
export declare function eachMonthOfInterval(interval: Interval): Date[];
@@ -0,0 +1,20 @@
import { toDate } from "date-fns";
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
import { monthIndexToHebrewDate, monthsSinceEpoch } from "../utils/serial.js";
export function eachMonthOfInterval(interval) {
const startDate = toDate(interval.start);
const endDate = toDate(interval.end);
if (endDate.getTime() < startDate.getTime()) {
return [];
}
const startHebrew = toHebrewDate(startDate);
const endHebrew = toHebrewDate(endDate);
const startIndex = monthsSinceEpoch(startHebrew);
const endIndex = monthsSinceEpoch(endHebrew);
const months = [];
for (let index = startIndex; index <= endIndex; index += 1) {
const hebrew = monthIndexToHebrewDate(index, 1);
months.push(toGregorianDate(hebrew));
}
return months;
}
@@ -0,0 +1,2 @@
import { type Interval } from "date-fns";
export declare function eachYearOfInterval(interval: Interval): Date[];
@@ -0,0 +1,16 @@
import { toDate } from "date-fns";
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
export function eachYearOfInterval(interval) {
const start = toDate(interval.start);
const end = toDate(interval.end);
if (end.getTime() < start.getTime()) {
return [];
}
const startYear = toHebrewDate(start).year;
const endYear = toHebrewDate(end).year;
const years = [];
for (let year = startYear; year <= endYear; year += 1) {
years.push(toGregorianDate({ year, monthIndex: 0, day: 1 }));
}
return years;
}
+1
View File
@@ -0,0 +1 @@
export declare function endOfMonth(date: Date): Date;
+7
View File
@@ -0,0 +1,7 @@
import { daysInHebrewMonth } from "../utils/calendarMath.js";
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
export function endOfMonth(date) {
const hebrew = toHebrewDate(date);
const day = daysInHebrewMonth(hebrew.year, hebrew.monthIndex);
return toGregorianDate({ ...hebrew, day });
}
+1
View File
@@ -0,0 +1 @@
export declare function endOfYear(date: Date): Date;
+8
View File
@@ -0,0 +1,8 @@
import { daysInHebrewMonth, monthsInHebrewYear, } from "../utils/calendarMath.js";
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
export function endOfYear(date) {
const hebrew = toHebrewDate(date);
const lastMonth = monthsInHebrewYear(hebrew.year) - 1;
const day = daysInHebrewMonth(hebrew.year, lastMonth);
return toGregorianDate({ year: hebrew.year, monthIndex: lastMonth, day });
}
@@ -0,0 +1 @@
export declare function findMonthIndexByCode(year: number, preferredCode: string): number;
@@ -0,0 +1,10 @@
import { getMonthCode, monthsInHebrewYear } from "../utils/calendarMath.js";
export function findMonthIndexByCode(year, preferredCode) {
const monthsCount = monthsInHebrewYear(year);
for (let index = 0; index < monthsCount; index += 1) {
if (getMonthCode(year, index) === preferredCode) {
return index;
}
}
return -1;
}
+3
View File
@@ -0,0 +1,3 @@
import type { FormatOptions as DateFnsFormatOptions } from "date-fns";
/** Hebrew calendar formatting override. */
export declare function format(date: Date, formatStr: string, options?: DateFnsFormatOptions): string;
+149
View File
@@ -0,0 +1,149 @@
import { getMonthCode } from "../utils/calendarMath.js";
import { toHebrewDate } from "../utils/dateConversion.js";
import { hebrewMonthNumber } from "../utils/serial.js";
const fallbackMonthNames = {
tishrei: { en: "Tishrei", he: "תשרי" },
cheshvan: { en: "Cheshvan", he: "חשוון" },
kislev: { en: "Kislev", he: "כסלו" },
tevet: { en: "Tevet", he: "טבת" },
shevat: { en: "Shevat", he: "שבט" },
adarI: { en: "Adar I", he: "אדר א׳" },
adar: { en: "Adar", he: "אדר" },
nisan: { en: "Nisan", he: "ניסן" },
iyar: { en: "Iyar", he: "אייר" },
sivan: { en: "Sivan", he: "סיוון" },
tamuz: { en: "Tammuz", he: "תמוז" },
av: { en: "Av", he: "אב" },
elul: { en: "Elul", he: "אלול" },
};
const fallbackWeekdayNames = {
long: {
en: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
he: [
"יום ראשון",
"יום שני",
"יום שלישי",
"יום רביעי",
"יום חמישי",
"יום שישי",
"שבת",
],
},
narrow: {
en: ["S", "M", "T", "W", "T", "F", "S"],
he: ["א", "ב", "ג", "ד", "ה", "ו", "ש"],
},
};
const getLocaleCode = (options) => {
return options?.locale?.code ?? "he";
};
const getMonthCodeForDate = (date) => {
const hebrew = toHebrewDate(date);
return getMonthCode(hebrew.year, hebrew.monthIndex);
};
const formatMonthName = (date, localeCode) => {
try {
return new Intl.DateTimeFormat(localeCode, {
month: "long",
calendar: "hebrew",
}).format(date);
}
catch {
const code = getMonthCodeForDate(date);
const isHebrew = localeCode.startsWith("he");
return isHebrew ? fallbackMonthNames[code].he : fallbackMonthNames[code].en;
}
};
const formatWeekdayName = (date, localeCode, width) => {
try {
return new Intl.DateTimeFormat(localeCode, {
weekday: width,
calendar: "hebrew",
}).format(date);
}
catch {
const index = date.getDay();
const isHebrew = localeCode.startsWith("he");
return isHebrew
? fallbackWeekdayNames[width].he[index]
: fallbackWeekdayNames[width].en[index];
}
};
const formatDateStyle = (date, localeCode, style) => {
try {
return new Intl.DateTimeFormat(localeCode, {
dateStyle: style,
calendar: "hebrew",
}).format(date);
}
catch {
const hebrew = toHebrewDate(date);
const month = formatMonthName(date, localeCode);
if (style === "full") {
const weekday = formatWeekdayName(date, localeCode, "long");
return `${weekday}, ${month} ${hebrew.day}, ${hebrew.year}`;
}
return `${month} ${hebrew.day}, ${hebrew.year}`;
}
};
const formatNumber = (value) => {
return value.toString();
};
const buildTimeFormat = (date, localeCode, formatStr) => {
const hour12 = formatStr.includes("a");
return new Intl.DateTimeFormat(localeCode, {
hour: "numeric",
minute: "numeric",
hour12,
}).format(date);
};
/** Hebrew calendar formatting override. */
export function format(date, formatStr, options) {
const extendedOptions = options;
const localeCode = getLocaleCode(extendedOptions);
const hebrew = toHebrewDate(date);
const monthNumber = hebrewMonthNumber(hebrew.monthIndex);
switch (formatStr) {
case "LLLL y":
case "LLLL yyyy":
return `${formatMonthName(date, localeCode)} ${formatNumber(hebrew.year)}`;
case "LLLL":
return formatMonthName(date, localeCode);
case "PPP":
return formatDateStyle(date, localeCode, "long");
case "PPPP":
return formatDateStyle(date, localeCode, "full");
case "cccc":
return formatWeekdayName(date, localeCode, "long");
case "cccccc":
return formatWeekdayName(date, localeCode, "narrow");
case "yyyy":
case "y":
return formatNumber(hebrew.year);
case "yyyy-MM":
return `${formatNumber(hebrew.year)}-${formatNumber(monthNumber).padStart(2, "0")}`;
case "yyyy-MM-dd":
return `${formatNumber(hebrew.year)}-${formatNumber(monthNumber).padStart(2, "0")}-${formatNumber(hebrew.day).padStart(2, "0")}`;
case "MM":
return formatNumber(monthNumber).padStart(2, "0");
case "M":
return formatNumber(monthNumber);
case "dd":
return formatNumber(hebrew.day).padStart(2, "0");
case "d":
return formatNumber(hebrew.day);
default:
if (/[Hh]/.test(formatStr) && /m/.test(formatStr)) {
return buildTimeFormat(date, localeCode, formatStr);
}
return `${formatNumber(hebrew.day)}/${formatNumber(monthNumber)}/${formatNumber(hebrew.year)}`;
}
}
+1
View File
@@ -0,0 +1 @@
export declare function getMonth(date: Date): number;
+4
View File
@@ -0,0 +1,4 @@
import { toHebrewDate } from "../utils/dateConversion.js";
export function getMonth(date) {
return toHebrewDate(date).monthIndex;
}
+1
View File
@@ -0,0 +1 @@
export declare function getYear(date: Date): number;
+4
View File
@@ -0,0 +1,4 @@
import { toHebrewDate } from "../utils/dateConversion.js";
export function getYear(date) {
return toHebrewDate(date).year;
}
+17
View File
@@ -0,0 +1,17 @@
export * from "./addMonths.js";
export * from "./addYears.js";
export * from "./differenceInCalendarMonths.js";
export * from "./eachMonthOfInterval.js";
export * from "./eachYearOfInterval.js";
export * from "./endOfMonth.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./getMonth.js";
export * from "./getYear.js";
export * from "./isSameMonth.js";
export * from "./isSameYear.js";
export * from "./newDate.js";
export * from "./setMonth.js";
export * from "./setYear.js";
export * from "./startOfMonth.js";
export * from "./startOfYear.js";
+17
View File
@@ -0,0 +1,17 @@
export * from "./addMonths.js";
export * from "./addYears.js";
export * from "./differenceInCalendarMonths.js";
export * from "./eachMonthOfInterval.js";
export * from "./eachYearOfInterval.js";
export * from "./endOfMonth.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./getMonth.js";
export * from "./getYear.js";
export * from "./isSameMonth.js";
export * from "./isSameYear.js";
export * from "./newDate.js";
export * from "./setMonth.js";
export * from "./setYear.js";
export * from "./startOfMonth.js";
export * from "./startOfYear.js";
+1
View File
@@ -0,0 +1 @@
export declare function isSameMonth(dateLeft: Date, dateRight: Date): boolean;
+6
View File
@@ -0,0 +1,6 @@
import { toHebrewDate } from "../utils/dateConversion.js";
export function isSameMonth(dateLeft, dateRight) {
const left = toHebrewDate(dateLeft);
const right = toHebrewDate(dateRight);
return left.year === right.year && left.monthIndex === right.monthIndex;
}
+1
View File
@@ -0,0 +1 @@
export declare function isSameYear(dateLeft: Date, dateRight: Date): boolean;
+4
View File
@@ -0,0 +1,4 @@
import { toHebrewDate } from "../utils/dateConversion.js";
export function isSameYear(dateLeft, dateRight) {
return toHebrewDate(dateLeft).year === toHebrewDate(dateRight).year;
}
+1
View File
@@ -0,0 +1 @@
export declare function newDate(year: number, monthIndex: number, day: number): Date;
+4
View File
@@ -0,0 +1,4 @@
import { toGregorianDate } from "../utils/dateConversion.js";
export function newDate(year, monthIndex, day) {
return toGregorianDate({ year, monthIndex, day });
}
+1
View File
@@ -0,0 +1 @@
export declare function setMonth(date: Date, month: number): Date;
+9
View File
@@ -0,0 +1,9 @@
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
import { monthIndexToHebrewDate, monthsSinceEpoch } from "../utils/serial.js";
export function setMonth(date, month) {
const hebrew = toHebrewDate(date);
const baseIndex = monthsSinceEpoch({ year: hebrew.year, monthIndex: 0 });
const targetIndex = baseIndex + month;
const target = monthIndexToHebrewDate(targetIndex, hebrew.day);
return toGregorianDate(target);
}
+1
View File
@@ -0,0 +1 @@
export declare function setYear(date: Date, year: number): Date;
+28
View File
@@ -0,0 +1,28 @@
import { getMonthCode, isHebrewLeapYear, monthsInHebrewYear, } from "../utils/calendarMath.js";
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
import { clampHebrewDay } from "../utils/serial.js";
import { findMonthIndexByCode } from "./findMonthIndexByCode.js";
export function setYear(date, year) {
const hebrew = toHebrewDate(date);
const targetYear = year;
const originalCode = getMonthCode(hebrew.year, hebrew.monthIndex);
let targetMonthIndex = findMonthIndexByCode(targetYear, originalCode);
if (targetMonthIndex === -1) {
if (originalCode === "adarI") {
targetMonthIndex = findMonthIndexByCode(targetYear, "adar");
}
else if (originalCode === "adar" && !isHebrewLeapYear(targetYear)) {
targetMonthIndex = findMonthIndexByCode(targetYear, "adar");
}
else {
const monthsCount = monthsInHebrewYear(targetYear);
targetMonthIndex = Math.min(hebrew.monthIndex, monthsCount - 1);
}
}
const day = clampHebrewDay(targetYear, targetMonthIndex, hebrew.day);
return toGregorianDate({
year: targetYear,
monthIndex: targetMonthIndex,
day,
});
}
+1
View File
@@ -0,0 +1 @@
export declare function startOfMonth(date: Date): Date;
+5
View File
@@ -0,0 +1,5 @@
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
export function startOfMonth(date) {
const hebrew = toHebrewDate(date);
return toGregorianDate({ ...hebrew, day: 1 });
}
+1
View File
@@ -0,0 +1 @@
export declare function startOfYear(date: Date): Date;
+5
View File
@@ -0,0 +1,5 @@
import { toGregorianDate, toHebrewDate } from "../utils/dateConversion.js";
export function startOfYear(date) {
const hebrew = toHebrewDate(date);
return toGregorianDate({ year: hebrew.year, monthIndex: 0, day: 1 });
}
+17
View File
@@ -0,0 +1,17 @@
import { type HebrewMonthCode } from "./constants.js";
/**
* Calculate the modulus that always returns a positive remainder. Useful when
* applying 19-year leap cycles.
*/
export declare function mod(value: number, divisor: number): number;
/** Determine whether a Hebrew year includes the extra Adar I month. */
export declare function isHebrewLeapYear(year: number): boolean;
/** Return the absolute day for Rosh Hashanah (cached for reuse). */
export declare function roshHashanah(year: number): number;
/** Total days in a Hebrew year, accounting for leap and year type. */
export declare function daysInHebrewYear(year: number): number;
/** Returns the number of months in the specified year (12 or 13). */
export declare function monthsInHebrewYear(year: number): number;
/** Number of days in a given Hebrew month (by index). */
export declare function daysInHebrewMonth(year: number, monthIndex: number): number;
export declare function getMonthCode(year: number, monthIndex: number): HebrewMonthCode;
+130
View File
@@ -0,0 +1,130 @@
import { HEBREW_EPOCH, MONTH_SEQUENCE_COMMON, MONTH_SEQUENCE_LEAP, } from "./constants.js";
const roshHashanahCache = new Map();
const yearLengthCache = new Map();
/**
* Calculate the modulus that always returns a positive remainder. Useful when
* applying 19-year leap cycles.
*/
export function mod(value, divisor) {
return ((value % divisor) + divisor) % divisor;
}
/** Determine whether a Hebrew year includes the extra Adar I month. */
export function isHebrewLeapYear(year) {
return mod(7 * year + 1, 19) < 7;
}
/** Count lunar months elapsed since the epoch up to the start of a year. */
function monthsElapsed(year) {
return Math.floor((235 * year - 234) / 19);
}
/** Compute the absolute day (relative to epoch) for the new year. */
function hebrewCalendarElapsedDays(year) {
const months = monthsElapsed(year);
const parts = 204 + 793 * mod(months, 1080);
const hours = 5 + 12 * months + 793 * Math.floor(months / 1080);
const day = 1 + 29 * months + Math.floor(hours / 24);
const partsRemain = mod(hours, 24) * 1080 + parts;
let roshHashanah = day;
const leapYear = isHebrewLeapYear(year);
const lastYearLeap = isHebrewLeapYear(year - 1);
if (partsRemain >= 19440 ||
(mod(roshHashanah, 7) === 2 && partsRemain >= 9924 && !leapYear) ||
(mod(roshHashanah, 7) === 1 && partsRemain >= 16789 && lastYearLeap)) {
roshHashanah += 1;
}
const weekday = mod(roshHashanah, 7);
if (weekday === 0 || weekday === 3 || weekday === 5) {
roshHashanah += 1;
}
return roshHashanah;
}
/** Return the absolute day for Rosh Hashanah (cached for reuse). */
export function roshHashanah(year) {
const cached = roshHashanahCache.get(year);
if (cached !== undefined) {
return cached;
}
const value = HEBREW_EPOCH + hebrewCalendarElapsedDays(year);
roshHashanahCache.set(year, value);
return value;
}
/** Total days in a Hebrew year, accounting for leap and year type. */
export function daysInHebrewYear(year) {
const cached = yearLengthCache.get(year);
if (cached !== undefined) {
return cached;
}
const days = roshHashanah(year + 1) - roshHashanah(year);
yearLengthCache.set(year, days);
return days;
}
/** Classify a year as deficient, regular, or complete. */
function yearType(year) {
const days = daysInHebrewYear(year);
const leap = isHebrewLeapYear(year);
if (leap) {
if (days === 383)
return "deficient";
if (days === 384)
return "regular";
return "complete";
}
if (days === 353)
return "deficient";
if (days === 354)
return "regular";
return "complete";
}
/** Get the sequence of month codes for a year, inserting Adar I as needed. */
function monthSequence(year) {
return isHebrewLeapYear(year) ? MONTH_SEQUENCE_LEAP : MONTH_SEQUENCE_COMMON;
}
/** Retrieve the canonical month code for a year and month index. */
function monthCode(year, monthIndex) {
const sequence = monthSequence(year);
if (monthIndex < 0 || monthIndex >= sequence.length) {
throw new RangeError(`Invalid month index ${monthIndex} for year ${year}`);
}
return sequence[monthIndex];
}
/** Returns the number of months in the specified year (12 or 13). */
export function monthsInHebrewYear(year) {
return monthSequence(year).length;
}
/** Number of days in a given Hebrew month (by index). */
export function daysInHebrewMonth(year, monthIndex) {
const code = monthCode(year, monthIndex);
const type = yearType(year);
switch (code) {
case "tishrei":
return 30;
case "cheshvan":
return type === "complete" ? 30 : 29;
case "kislev":
return type === "deficient" ? 29 : 30;
case "tevet":
return 29;
case "shevat":
return 30;
case "adarI":
return 30;
case "adar":
return 29;
case "nisan":
return 30;
case "iyar":
return 29;
case "sivan":
return 30;
case "tamuz":
return 29;
case "av":
return 30;
case "elul":
return 29;
default:
return 0;
}
}
export function getMonthCode(year, monthIndex) {
return monthCode(year, monthIndex);
}
+13
View File
@@ -0,0 +1,13 @@
export declare const MS_PER_DAY: number;
export declare const GREGORIAN_EPOCH: number;
export declare const HEBREW_EPOCH = -2067381;
export declare const MONTH_SEQUENCE_COMMON: readonly ["tishrei", "cheshvan", "kislev", "tevet", "shevat", "adar", "nisan", "iyar", "sivan", "tamuz", "av", "elul"];
export declare const MONTH_SEQUENCE_LEAP: readonly ["tishrei", "cheshvan", "kislev", "tevet", "shevat", "adarI", "adar", "nisan", "iyar", "sivan", "tamuz", "av", "elul"];
export declare const MONTHS_PER_CYCLE = 235;
export type HebrewMonthCode = (typeof MONTH_SEQUENCE_LEAP)[number];
export type HebrewDate = {
year: number;
monthIndex: number;
day: number;
};
export type YearType = "deficient" | "regular" | "complete";
+33
View File
@@ -0,0 +1,33 @@
export const MS_PER_DAY = 24 * 60 * 60 * 1000;
export const GREGORIAN_EPOCH = Date.UTC(1, 0, 1);
export const HEBREW_EPOCH = -2067381;
export const MONTH_SEQUENCE_COMMON = [
"tishrei",
"cheshvan",
"kislev",
"tevet",
"shevat",
"adar",
"nisan",
"iyar",
"sivan",
"tamuz",
"av",
"elul",
];
export const MONTH_SEQUENCE_LEAP = [
"tishrei",
"cheshvan",
"kislev",
"tevet",
"shevat",
"adarI",
"adar",
"nisan",
"iyar",
"sivan",
"tamuz",
"av",
"elul",
];
export const MONTHS_PER_CYCLE = 235;
@@ -0,0 +1,5 @@
import { type HebrewDate } from "./constants.js";
/** Converts a Gregorian date to the corresponding Hebrew date. */
export declare function toHebrewDate(date: Date): HebrewDate;
/** Converts a Hebrew date back to the Gregorian calendar. */
export declare function toGregorianDate(hebrew: HebrewDate): Date;
+70
View File
@@ -0,0 +1,70 @@
import { daysInHebrewMonth, monthsInHebrewYear, roshHashanah, } from "./calendarMath.js";
import { GREGORIAN_EPOCH, MS_PER_DAY } from "./constants.js";
/** Convert a Gregorian date to an absolute day number from the epoch. */
function dateToAbsolute(date) {
// Years < 100 must use UTC components to avoid JS's 1900 offset; for normal
// years keep local components so we don't reintroduce UTC/local skew in the
// rendered month grid.
const useUTC = date.getFullYear() < 100;
const year = useUTC ? date.getUTCFullYear() : date.getFullYear();
const month = useUTC ? date.getUTCMonth() : date.getMonth();
const day = useUTC ? date.getUTCDate() : date.getDate();
const normalized = new Date(0);
normalized.setUTCFullYear(year, month, day);
normalized.setUTCHours(0, 0, 0, 0);
return Math.floor((normalized.getTime() - GREGORIAN_EPOCH) / MS_PER_DAY) + 1;
}
/** Convert an absolute day number back to a Gregorian date. */
function absoluteToDate(absolute) {
const utc = new Date(GREGORIAN_EPOCH + (absolute - 1) * MS_PER_DAY);
const result = new Date(0);
result.setFullYear(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
result.setHours(0, 0, 0, 0);
return result;
}
/** Convert a Hebrew date to an absolute day number so it can be compared. */
function absoluteFromHebrew({ year, monthIndex, day }) {
let days = day - 1;
for (let index = 0; index < monthIndex; index += 1) {
days += daysInHebrewMonth(year, index);
}
return roshHashanah(year) + days;
}
/** Convert an absolute day number to the equivalent Hebrew date. */
function hebrewFromAbsolute(absolute) {
const date = new Date(GREGORIAN_EPOCH + (absolute - 1) * MS_PER_DAY);
let year = date.getUTCFullYear() + 3760;
if (date.getUTCMonth() >= 8) {
year += 1;
}
while (absolute >= roshHashanah(year + 1)) {
year += 1;
}
while (absolute < roshHashanah(year)) {
year -= 1;
}
let dayOfYear = absolute - roshHashanah(year);
const monthCount = monthsInHebrewYear(year);
let monthIndex = 0;
while (monthIndex < monthCount) {
const monthDays = daysInHebrewMonth(year, monthIndex);
if (dayOfYear < monthDays) {
break;
}
dayOfYear -= monthDays;
monthIndex += 1;
}
return {
year,
monthIndex,
day: dayOfYear + 1,
};
}
/** Converts a Gregorian date to the corresponding Hebrew date. */
export function toHebrewDate(date) {
return hebrewFromAbsolute(dateToAbsolute(date));
}
/** Converts a Hebrew date back to the Gregorian calendar. */
export function toGregorianDate(hebrew) {
return absoluteToDate(absoluteFromHebrew(hebrew));
}
+9
View File
@@ -0,0 +1,9 @@
import { type HebrewDate } from "./constants.js";
/** Serial index for Hebrew months since the epoch (Tishrei of year 1). */
export declare function monthsSinceEpoch({ year, monthIndex, }: Pick<HebrewDate, "year" | "monthIndex">): number;
/** Clamp a day number to the valid number of days in a month. */
export declare function clampHebrewDay(year: number, monthIndex: number, day: number): number;
/** Convert serial month index to a Hebrew date, clamping the day if needed. */
export declare function monthIndexToHebrewDate(monthIndex: number, day: number): HebrewDate;
/** Convert zero-based month index to the user-facing 1..13 number. */
export declare function hebrewMonthNumber(monthIndex: number): number;
+70
View File
@@ -0,0 +1,70 @@
import { daysInHebrewMonth, monthsInHebrewYear } from "./calendarMath.js";
import { MONTHS_PER_CYCLE } from "./constants.js";
/**
* Count how many months have elapsed before the given Hebrew year. Needed to
* compute serial month offsets across leap/non-leap cycles.
*/
function monthsBeforeYear(year) {
if (year <= 1) {
return 0;
}
const cycles = Math.floor((year - 1) / 19);
let months = cycles * MONTHS_PER_CYCLE;
let currentYear = cycles * 19 + 1;
while (currentYear < year) {
months += monthsInHebrewYear(currentYear);
currentYear += 1;
}
return months;
}
/** Serial index for Hebrew months since the epoch (Tishrei of year 1). */
export function monthsSinceEpoch({ year, monthIndex, }) {
return monthsBeforeYear(year) + monthIndex;
}
/**
* Convert a serial month index back into Hebrew year/month. Supports negative
* indices for pre-epoch dates.
*/
function hebrewFromMonthIndex(monthIndex) {
let index = monthIndex;
let year = 1;
if (index >= 0) {
const cycles = Math.floor(index / MONTHS_PER_CYCLE);
year += cycles * 19;
index -= cycles * MONTHS_PER_CYCLE;
while (true) {
const months = monthsInHebrewYear(year);
if (index < months) {
break;
}
index -= months;
year += 1;
}
return { year, month: index };
}
// Handle negative month indices (dates before the epoch)
while (index < 0) {
year -= 1;
const months = monthsInHebrewYear(year);
index += months;
}
return { year, month: index };
}
/** Clamp a day number to the valid number of days in a month. */
export function clampHebrewDay(year, monthIndex, day) {
const maxDay = daysInHebrewMonth(year, monthIndex);
return Math.min(day, maxDay);
}
/** Convert serial month index to a Hebrew date, clamping the day if needed. */
export function monthIndexToHebrewDate(monthIndex, day) {
const { year, month } = hebrewFromMonthIndex(monthIndex);
return {
year,
monthIndex: month,
day: clampHebrewDay(year, month, day),
};
}
/** Convert zero-based month index to the user-facing 1..13 number. */
export function hebrewMonthNumber(monthIndex) {
return monthIndex + 1;
}