Favivon - Correção

This commit is contained in:
2026-05-30 19:59:39 -03:00
parent 76ddaa815d
commit d7dfd221f0
32859 changed files with 5459654 additions and 404 deletions
+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;
}