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
+44
View File
@@ -0,0 +1,44 @@
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 Ethiopic calendar.
*
* Defaults:
*
* - `locale`: `am-ET` (Amharic) via an Intl-backed date-fns locale
* - `numerals`: `geez` (Ethiopic digits)
*
* Notes:
*
* - Weekday names are taken from `Intl.DateTimeFormat(locale.code)`.
* - Month names are Amharic by default; they switch to Latin transliteration when
* `locale.code` starts with `en` or when `numerals` is `latn`.
* - Time tokens like `hh:mm a` are formatted via `Intl.DateTimeFormat` using the
* provided `locale`.
*
* @see https://daypicker.dev/docs/localization#ethiopic-calendar
*/
export declare function DayPicker(props: DayPickerProps & {
/**
* The locale to use in the calendar.
*
* @default `am-ET`
*/
locale?: Locale;
/**
* The numeral system to use when formatting dates.
*
* - `latn`: Latin (Western Arabic)
* - `geez`: Ge'ez (Ethiopic numerals)
*
* @defaultValue `geez` (Ethiopic numerals)
* @see https://daypicker.dev/docs/translation#numeral-systems
*/
numerals?: DayPickerProps["numerals"];
}): React.JSX.Element;
/** Returns the date library used in the calendar. */
export declare const getDateLib: (options?: DateLibOptions) => DateLib;
export { amET } from "../locale/am-ET.js";
export { enUS } from "../locale/en-US.js";
+34
View File
@@ -0,0 +1,34 @@
import React from "react";
import { DateLib, DayPicker as DayPickerComponent, } from "../index.js";
import amET from "../locale/am-ET.js";
import * as ethiopicDateLib from "./lib/index.js";
/**
* Render the Ethiopic calendar.
*
* Defaults:
*
* - `locale`: `am-ET` (Amharic) via an Intl-backed date-fns locale
* - `numerals`: `geez` (Ethiopic digits)
*
* Notes:
*
* - Weekday names are taken from `Intl.DateTimeFormat(locale.code)`.
* - Month names are Amharic by default; they switch to Latin transliteration when
* `locale.code` starts with `en` or when `numerals` is `latn`.
* - Time tokens like `hh:mm a` are formatted via `Intl.DateTimeFormat` using the
* provided `locale`.
*
* @see https://daypicker.dev/docs/localization#ethiopic-calendar
*/
export function DayPicker(props) {
return (React.createElement(DayPickerComponent, { ...props, locale: props.locale ?? amET, numerals: props.numerals ?? "geez",
// Pass overrides, not a DateLib instance
dateLib: ethiopicDateLib }));
}
/** Returns the date library used in the calendar. */
export const getDateLib = (options) => {
return new DateLib(options, ethiopicDateLib);
};
// Export a minimal Amharic (Ethiopia) date-fns locale that uses Intl
export { amET } from "../locale/am-ET.js";
export { enUS } from "../locale/en-US.js";
+9
View File
@@ -0,0 +1,9 @@
/**
* Adds the specified number of months to the given Ethiopian date. Handles
* month overflow and year boundaries correctly.
*
* @param date - The starting gregorian date
* @param amount - The number of months to add (can be negative)
* @returns A new gregorian date with the months added
*/
export declare function addMonths(date: Date, amount: number): Date;
+24
View File
@@ -0,0 +1,24 @@
import { daysInMonth } from "../utils/daysInMonth.js";
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Adds the specified number of months to the given Ethiopian date. Handles
* month overflow and year boundaries correctly.
*
* @param date - The starting gregorian date
* @param amount - The number of months to add (can be negative)
* @returns A new gregorian date with the months added
*/
export function addMonths(date, amount) {
const { year, month, day } = toEthiopicDate(date);
let newMonth = month + amount;
const yearAdjustment = Math.floor((newMonth - 1) / 13);
newMonth = ((newMonth - 1) % 13) + 1;
if (newMonth < 1) {
newMonth += 13;
}
const newYear = year + yearAdjustment;
// Adjust day if it exceeds the month length
const monthLength = daysInMonth(newMonth, newYear);
const newDay = Math.min(day, monthLength);
return toGregorianDate({ year: newYear, month: newMonth, day: newDay });
}
+9
View File
@@ -0,0 +1,9 @@
/**
* Adds the specified number of years to the given Ethiopian date. Handles leap
* year transitions for Pagume month.
*
* @param date - The starting gregorian date
* @param amount - The number of years to add (can be negative)
* @returns A new gregorian date with the years added
*/
export declare function addYears(date: Date, amount: number): Date;
+23
View File
@@ -0,0 +1,23 @@
import { isEthiopicLeapYear, toEthiopicDate, toGregorianDate, } from "../utils/index.js";
/**
* Adds the specified number of years to the given Ethiopian date. Handles leap
* year transitions for Pagume month.
*
* @param date - The starting gregorian date
* @param amount - The number of years to add (can be negative)
* @returns A new gregorian date with the years added
*/
export function addYears(date, amount) {
const etDate = toEthiopicDate(date);
const day = isEthiopicLeapYear(etDate.year) &&
etDate.month === 13 &&
etDate.day === 6 &&
amount % 4 !== 0
? 5
: etDate.day;
return toGregorianDate({
month: etDate.month,
day: day,
year: etDate.year + amount,
});
}
@@ -0,0 +1,8 @@
/**
* Difference in calendar months
*
* @param {Date} dateLeft - The later date
* @param {Date} dateRight - The earlier date
* @returns {number} The number of calendar months between the two dates
*/
export declare function differenceInCalendarMonths(dateLeft: Date, dateRight: Date): number;
@@ -0,0 +1,14 @@
import { toEthiopicDate } from "../utils/index.js";
/**
* Difference in calendar months
*
* @param {Date} dateLeft - The later date
* @param {Date} dateRight - The earlier date
* @returns {number} The number of calendar months between the two dates
*/
export function differenceInCalendarMonths(dateLeft, dateRight) {
const ethiopicLeft = toEthiopicDate(dateLeft);
const ethiopicRight = toEthiopicDate(dateRight);
return ((ethiopicLeft.year - ethiopicRight.year) * 13 +
(ethiopicLeft.month - ethiopicRight.month));
}
@@ -0,0 +1,11 @@
import type { Interval } from "date-fns";
/**
* Each month of an interval
*
* @param {Object} interval - The interval object
* @param {Date} interval.start - The start date of the interval
* @param {Date} interval.end - The end date of the interval
* @returns {Date[]} An array of dates representing the start of each month in
* the interval
*/
export declare function eachMonthOfInterval(interval: Interval): Date[];
@@ -0,0 +1,27 @@
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Each month of an interval
*
* @param {Object} interval - The interval object
* @param {Date} interval.start - The start date of the interval
* @param {Date} interval.end - The end date of the interval
* @returns {Date[]} An array of dates representing the start of each month in
* the interval
*/
export function eachMonthOfInterval(interval) {
const start = toEthiopicDate(new Date(interval.start));
const end = toEthiopicDate(new Date(interval.end));
const dates = [];
let currentYear = start.year;
let currentMonth = start.month;
while (currentYear < end.year ||
(currentYear === end.year && currentMonth <= end.month)) {
dates.push(toGregorianDate({ year: currentYear, month: currentMonth, day: 1 }));
currentMonth++;
if (currentMonth > 13) {
currentMonth = 1;
currentYear++;
}
}
return dates;
}
@@ -0,0 +1,7 @@
import type { Interval } from "date-fns";
/**
* Returns the start of each Ethiopic year included in the given interval.
*
* @param interval The interval whose years should be returned.
*/
export declare function eachYearOfInterval(interval: Interval): Date[];
@@ -0,0 +1,18 @@
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Returns the start of each Ethiopic year included in the given interval.
*
* @param interval The interval whose years should be returned.
*/
export function eachYearOfInterval(interval) {
const start = toEthiopicDate(new Date(interval.start));
const end = toEthiopicDate(new Date(interval.end));
if (end.year < start.year) {
return [];
}
const years = [];
for (let year = start.year; year <= end.year; year += 1) {
years.push(toGregorianDate({ year, month: 1, day: 1 }));
}
return years;
}
+8
View File
@@ -0,0 +1,8 @@
/**
* Returns the last day of the Ethiopian month for the given date.
*
* @param date - The gregorian date to get the end of month for
* @returns A new gregorian date representing the last day of the Ethiopian
* month
*/
export declare function endOfMonth(date: Date): Date;
+14
View File
@@ -0,0 +1,14 @@
import { daysInMonth } from "../utils/daysInMonth.js";
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Returns the last day of the Ethiopian month for the given date.
*
* @param date - The gregorian date to get the end of month for
* @returns A new gregorian date representing the last day of the Ethiopian
* month
*/
export function endOfMonth(date) {
const { year, month } = toEthiopicDate(date);
const day = daysInMonth(month, year);
return toGregorianDate({ year, month, day: day });
}
+9
View File
@@ -0,0 +1,9 @@
import { type EndOfWeekOptions } from "date-fns";
/**
* End of week
*
* @param {Date} date - The original date
* @param {EndOfWeekOptions} [options] - The options object
* @returns {Date} The end of the week
*/
export declare function endOfWeek(date: Date, options?: EndOfWeekOptions): Date;
+13
View File
@@ -0,0 +1,13 @@
import { endOfWeek as endOfWeekFns } from "date-fns";
/**
* End of week
*
* @param {Date} date - The original date
* @param {EndOfWeekOptions} [options] - The options object
* @returns {Date} The end of the week
*/
export function endOfWeek(date, options) {
const weekStartsOn = options?.weekStartsOn ?? 0; // Default to Monday (1)
const endOfWeek = endOfWeekFns(date, { weekStartsOn });
return endOfWeek;
}
+7
View File
@@ -0,0 +1,7 @@
/**
* End of year
*
* @param {Date} date - The original date
* @returns {Date} The end of the year
*/
export declare function endOfYear(date: Date): Date;
+12
View File
@@ -0,0 +1,12 @@
import { isEthiopicLeapYear, toEthiopicDate, toGregorianDate, } from "../utils/index.js";
/**
* End of year
*
* @param {Date} date - The original date
* @returns {Date} The end of the year
*/
export function endOfYear(date) {
const { year } = toEthiopicDate(date);
const day = isEthiopicLeapYear(year) ? 6 : 5;
return toGregorianDate({ year, month: 13, day });
}
+22
View File
@@ -0,0 +1,22 @@
import type { FormatOptions as DateFnsFormatOptions } from "date-fns";
/** Options for formatting dates in the Ethiopian calendar */
export type FormatOptions = DateFnsFormatOptions;
/**
* Format an Ethiopic calendar date using a subset of date-fns tokens.
*
* Behavior specifics for Ethiopic mode:
*
* - Weekday names ("cccc", "cccccc") come from `Intl.DateTimeFormat` using
* `options.locale?.code` (default: `am-ET`). Narrow form is a single letter.
* - Month names ("LLLL") are Amharic by default and switch to Latin
* transliteration when the locale code starts with `en` or when
* `options.numerals === 'latn'`.
* - Time parts such as `hh:mm a` are delegated to `Intl.DateTimeFormat` with the
* given locale.
* - Digits are converted to Ethiopic (Geez) when `options.numerals === 'geez'`.
*/
export declare function format(date: Date, formatStr: string, options?: DateFnsFormatOptions): string;
export declare const ethMonths: string[];
export declare const ethMonthsLatin: string[];
export declare const shortDays: string[];
export declare const longDays: string[];
+131
View File
@@ -0,0 +1,131 @@
import { toEthiopicDate } from "../utils/index.js";
import { formatNumber } from "./formatNumber.js";
function getEtDayName(day, short = true, localeCode = "am-ET") {
try {
const dtf = new Intl.DateTimeFormat(localeCode, {
// Ethiopic calendar expects single-letter for "cccccc" -> use narrow
weekday: short ? "narrow" : "long",
});
return dtf.format(day);
}
catch {
const dayOfWeek = day.getDay();
return short ? shortDays[dayOfWeek] : longDays[dayOfWeek];
}
}
function getEtMonthName(m, latin = false) {
if (m > 0 && m <= 13) {
return latin ? ethMonthsLatin[m - 1] : ethMonths[m - 1];
}
return "";
}
function formatEthiopianDate(dateObj, formatStr, numerals, localeCode) {
const etDate = dateObj ? toEthiopicDate(dateObj) : undefined;
if (!etDate)
return "";
const useLatin = (localeCode?.startsWith("en") ?? false) || numerals === "latn";
const yearTokenMatch = formatStr.match(/^(\s*)(y+)(\s*)$/);
if (yearTokenMatch) {
const [, leading = "", yearToken, trailing = ""] = yearTokenMatch;
const year = etDate.year.toString();
let formattedYear;
if (yearToken.length === 1) {
formattedYear = year;
}
else if (yearToken.length === 2) {
formattedYear = year.slice(-2).padStart(2, "0");
}
else {
formattedYear = year.padStart(yearToken.length, "0");
}
return `${leading}${formattedYear}${trailing}`;
}
switch (formatStr) {
case "LLLL yyyy":
case "LLLL y":
return `${getEtMonthName(etDate.month, useLatin)} ${etDate.year}`;
case "LLLL":
return getEtMonthName(etDate.month, useLatin);
case "yyyy-MM-dd":
return `${etDate.year}-${etDate.month
.toString()
.padStart(2, "0")}-${etDate.day.toString().padStart(2, "0")}`;
case "yyyy-MM":
return `${etDate.year}-${etDate.month.toString().padStart(2, "0")}`;
case "d":
return etDate.day.toString();
case "PPP":
return ` ${getEtMonthName(etDate.month, useLatin)} ${etDate.day}, ${etDate.year}`;
case "PPPP":
if (!dateObj)
return "";
return `${getEtDayName(dateObj, false, localeCode)}, ${getEtMonthName(etDate.month, useLatin)} ${etDate.day}, ${etDate.year}`;
case "cccc":
return dateObj ? getEtDayName(dateObj, false, localeCode) : "";
case "cccccc":
return dateObj ? getEtDayName(dateObj, true, localeCode) : "";
default:
return `${etDate.day}/${etDate.month}/${etDate.year}`;
}
}
/**
* Format an Ethiopic calendar date using a subset of date-fns tokens.
*
* Behavior specifics for Ethiopic mode:
*
* - Weekday names ("cccc", "cccccc") come from `Intl.DateTimeFormat` using
* `options.locale?.code` (default: `am-ET`). Narrow form is a single letter.
* - Month names ("LLLL") are Amharic by default and switch to Latin
* transliteration when the locale code starts with `en` or when
* `options.numerals === 'latn'`.
* - Time parts such as `hh:mm a` are delegated to `Intl.DateTimeFormat` with the
* given locale.
* - Digits are converted to Ethiopic (Geez) when `options.numerals === 'geez'`.
*/
export function format(date, formatStr, options) {
const extendedOptions = options;
if (formatStr.includes("hh:mm") || formatStr.includes("a")) {
return new Intl.DateTimeFormat(extendedOptions?.locale?.code ?? "en-US", {
hour: "numeric",
minute: "numeric",
hour12: formatStr.includes("a"),
}).format(date);
}
const formatted = formatEthiopianDate(date, formatStr, extendedOptions?.numerals, extendedOptions?.locale?.code ?? "am-ET");
if (extendedOptions?.numerals && extendedOptions.numerals === "geez") {
return formatted.replace(/\d+/g, (match) => formatNumber(parseInt(match, 10), "geez"));
}
return formatted;
}
export const ethMonths = [
"መስከረም",
"ጥቅምት",
"ህዳር",
"ታህሳስ",
"ጥር",
"የካቲት",
"መጋቢት",
"ሚያዚያ",
"ግንቦት",
"ሰኔ",
"ሐምሌ",
"ነሀሴ",
"ጳጉሜ",
];
export const ethMonthsLatin = [
"Meskerem",
"Tikimt",
"Hidar",
"Tahsas",
"Tir",
"Yekatit",
"Megabit",
"Miyazya",
"Ginbot",
"Sene",
"Hamle",
"Nehase",
"Pagumen",
];
export const shortDays = ["እ", "ሰ", "ማ", "ረ", "ሐ", "ዓ", "ቅ"];
export const longDays = ["እሁድ", "ሰኞ", "ማክሰኞ", "ረቡዕ", "ሐሙስ", "ዓርብ", "ቅዳሜ"];
+19
View File
@@ -0,0 +1,19 @@
/**
* Formats a number using either Latin or Ethiopic (Geez) numerals
*
* @example
* ```ts
* formatNumber(123) // '123'
* formatNumber(123, 'geez') // '፻፳፫'
* formatNumber(2023, 'geez') // '፳፻፳፫'
* ```;
*
* @param value - The number to format
* @param numerals - The numeral system to use:
*
* - 'latn': Latin numerals (1, 2, 3...)
* - 'geez': Ethiopic numerals (፩, ፪, ፫...)
*
* @returns The formatted number string
*/
export declare function formatNumber(value: number, numerals?: string): string;
+29
View File
@@ -0,0 +1,29 @@
import { toGeezNumerals } from "../utils/toGeezNumerals.js";
/**
* Formats a number using either Latin or Ethiopic (Geez) numerals
*
* @example
* ```ts
* formatNumber(123) // '123'
* formatNumber(123, 'geez') // '፻፳፫'
* formatNumber(2023, 'geez') // '፳፻፳፫'
* ```;
*
* @param value - The number to format
* @param numerals - The numeral system to use:
*
* - 'latn': Latin numerals (1, 2, 3...)
* - 'geez': Ethiopic numerals (፩, ፪, ፫...)
*
* @returns The formatted number string
*/
export function formatNumber(value, numerals = "latn") {
if (numerals === "geez") {
return toGeezNumerals(value);
}
// Use Intl.NumberFormat for other numeral systems
const formatter = new Intl.NumberFormat("en-US", {
numberingSystem: numerals,
});
return formatter.format(value);
}
+7
View File
@@ -0,0 +1,7 @@
/**
* Get month
*
* @param {Date} date - The original date
* @returns {number} The zero-based month index
*/
export declare function getMonth(date: Date): number;
+11
View File
@@ -0,0 +1,11 @@
import { toEthiopicDate } from "../utils/index.js";
/**
* Get month
*
* @param {Date} date - The original date
* @returns {number} The zero-based month index
*/
export function getMonth(date) {
const { month } = toEthiopicDate(date);
return month - 1; // Return zero-based month index
}
+9
View File
@@ -0,0 +1,9 @@
import { type GetWeekOptions } from "date-fns";
/**
* Get week number for Ethiopian calendar
*
* @param {Date} date - The original date
* @param {GetWeekOptions} [options] - The options object
* @returns {number} The week number
*/
export declare function getWeek(date: Date, options?: GetWeekOptions): number;
+41
View File
@@ -0,0 +1,41 @@
import { differenceInDays, getWeek as getWeekFns, } from "date-fns";
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
import { startOfWeek } from "./startOfWeek.js";
/**
* Get week number for Ethiopian calendar
*
* @param {Date} date - The original date
* @param {GetWeekOptions} [options] - The options object
* @returns {number} The week number
*/
export function getWeek(date, options) {
const weekStartsOn = options?.weekStartsOn ?? 1; // Default to Monday (1)
const etDate = toEthiopicDate(date);
const currentWeekStart = startOfWeek(date, { weekStartsOn });
// Get the first day of the current year
const firstDayOfYear = toGregorianDate({
year: etDate.year,
month: 1,
day: 1,
});
const firstWeekStart = startOfWeek(firstDayOfYear, { weekStartsOn });
// If date is before the first week of its year
if (date < firstWeekStart) {
return getWeekFns(date, { weekStartsOn, firstWeekContainsDate: 1 });
}
// If date falls into the first week of the NEXT Ethiopic year, return 1
const nextYearFirstDay = toGregorianDate({
year: etDate.year + 1,
month: 1,
day: 1,
});
const nextYearFirstWeekStart = startOfWeek(nextYearFirstDay, {
weekStartsOn,
});
if (date >= nextYearFirstWeekStart) {
return 1;
}
// Calculate week number based on days since first week
const daysSinceFirstWeek = differenceInDays(currentWeekStart, firstWeekStart);
return Math.floor(daysSinceFirstWeek / 7) + 1;
}
+7
View File
@@ -0,0 +1,7 @@
/**
* Get year
*
* @param {Date} date - The original date
* @returns {number} The year
*/
export declare function getYear(date: Date): number;
+11
View File
@@ -0,0 +1,11 @@
import { toEthiopicDate } from "../utils/index.js";
/**
* Get year
*
* @param {Date} date - The original date
* @returns {number} The year
*/
export function getYear(date) {
const { year } = toEthiopicDate(date);
return year;
}
+22
View File
@@ -0,0 +1,22 @@
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 "./endOfWeek.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./formatNumber.js";
export * from "./getMonth.js";
export * from "./getWeek.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 "./startOfDay.js";
export * from "./startOfMonth.js";
export * from "./startOfWeek.js";
export * from "./startOfYear.js";
+22
View File
@@ -0,0 +1,22 @@
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 "./endOfWeek.js";
export * from "./endOfYear.js";
export * from "./format.js";
export * from "./formatNumber.js";
export * from "./getMonth.js";
export * from "./getWeek.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 "./startOfDay.js";
export * from "./startOfMonth.js";
export * from "./startOfWeek.js";
export * from "./startOfYear.js";
+8
View File
@@ -0,0 +1,8 @@
/**
* Is same month
*
* @param {Date} dateLeft - The first date
* @param {Date} dateRight - The second date
* @returns {boolean} True if the two dates are in the same month
*/
export declare function isSameMonth(dateLeft: Date, dateRight: Date): boolean;
+13
View File
@@ -0,0 +1,13 @@
import { toEthiopicDate } from "../utils/index.js";
/**
* Is same month
*
* @param {Date} dateLeft - The first date
* @param {Date} dateRight - The second date
* @returns {boolean} True if the two dates are in the same month
*/
export function isSameMonth(dateLeft, dateRight) {
const left = toEthiopicDate(dateLeft);
const right = toEthiopicDate(dateRight);
return left.year === right.year && left.month === right.month;
}
+8
View File
@@ -0,0 +1,8 @@
/**
* Checks if two dates fall in the same Ethiopian year.
*
* @param dateLeft - The first gregorian date to compare
* @param dateRight - The second gregorian date to compare
* @returns True if the dates are in the same Ethiopian year
*/
export declare function isSameYear(dateLeft: Date, dateRight: Date): boolean;
+13
View File
@@ -0,0 +1,13 @@
import { toEthiopicDate } from "../utils/index.js";
/**
* Checks if two dates fall in the same Ethiopian year.
*
* @param dateLeft - The first gregorian date to compare
* @param dateRight - The second gregorian date to compare
* @returns True if the dates are in the same Ethiopian year
*/
export function isSameYear(dateLeft, dateRight) {
const left = toEthiopicDate(dateLeft);
const right = toEthiopicDate(dateRight);
return left.year === right.year;
}
+9
View File
@@ -0,0 +1,9 @@
/**
* Creates a new Ethiopic date
*
* @param {number} year - The year of the Ethiopic date
* @param {number} monthIndex - The zero-based month index of the Ethiopic date
* @param {number} date - The day of the month of the Ethiopic date
* @returns {Date} The corresponding Gregorian date
*/
export declare function newDate(year: number, monthIndex: number, date: number): Date;
+22
View File
@@ -0,0 +1,22 @@
import { toGregorianDate } from "../utils/index.js";
import { isEthiopicDateValid } from "../utils/isEthiopicDateValid.js";
/**
* Creates a new Ethiopic date
*
* @param {number} year - The year of the Ethiopic date
* @param {number} monthIndex - The zero-based month index of the Ethiopic date
* @param {number} date - The day of the month of the Ethiopic date
* @returns {Date} The corresponding Gregorian date
*/
export function newDate(year, monthIndex, date) {
// Convert from 0-based month index to 1-based Ethiopic month
const month = monthIndex + 1;
if (!isEthiopicDateValid({ year, month, day: date })) {
throw new Error("Invalid Ethiopic date");
}
return toGregorianDate({
year: year,
month: month,
day: date,
});
}
+8
View File
@@ -0,0 +1,8 @@
/**
* Set month
*
* @param {Date} date - The original date
* @param {number} month - The zero-based month index
* @returns {Date} The new date with the month set
*/
export declare function setMonth(date: Date, month: number): Date;
+14
View File
@@ -0,0 +1,14 @@
import { daysInMonth, toEthiopicDate, toGregorianDate, } from "../utils/index.js";
/**
* Set month
*
* @param {Date} date - The original date
* @param {number} month - The zero-based month index
* @returns {Date} The new date with the month set
*/
export function setMonth(date, month) {
const { year, day } = toEthiopicDate(date);
const targetMonth = month + 1; // Convert from zero-based index
const safeDay = Math.min(day, daysInMonth(targetMonth, year));
return toGregorianDate({ year, month: targetMonth, day: safeDay });
}
+8
View File
@@ -0,0 +1,8 @@
/**
* Set year
*
* @param {Date} date - The original date
* @param {number} year - The year to set
* @returns {Date} The new date with the year set
*/
export declare function setYear(date: Date, year: number): Date;
+16
View File
@@ -0,0 +1,16 @@
import { daysInMonth } from "../utils/daysInMonth.js";
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Set year
*
* @param {Date} date - The original date
* @param {number} year - The year to set
* @returns {Date} The new date with the year set
*/
export function setYear(date, year) {
const { month, day } = toEthiopicDate(date);
// Check if the day is valid in the new year (handles leap year changes)
const maxDays = daysInMonth(month, year);
const newDay = Math.min(day, maxDays);
return toGregorianDate({ year, month, day: newDay });
}
+7
View File
@@ -0,0 +1,7 @@
/**
* Start of day
*
* @param {Date} date - The original date
* @returns {Date} The start of the day
*/
export declare function startOfDay(date: Date): Date;
+11
View File
@@ -0,0 +1,11 @@
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Start of day
*
* @param {Date} date - The original date
* @returns {Date} The start of the day
*/
export function startOfDay(date) {
const { year, month, day } = toEthiopicDate(date);
return toGregorianDate({ year, month, day });
}
@@ -0,0 +1,7 @@
/**
* Start of month
*
* @param {Date} date - The original date
* @returns {Date} The start of the month
*/
export declare function startOfMonth(date: Date): Date;
+11
View File
@@ -0,0 +1,11 @@
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Start of month
*
* @param {Date} date - The original date
* @returns {Date} The start of the month
*/
export function startOfMonth(date) {
const { year, month } = toEthiopicDate(date);
return toGregorianDate({ year, month, day: 1 });
}
+9
View File
@@ -0,0 +1,9 @@
import { type StartOfWeekOptions } from "date-fns";
/**
* Start of week
*
* @param {Date} date - The original date
* @param {StartOfWeekOptions} [options] - The options object
* @returns {Date} The start of the week
*/
export declare function startOfWeek(date: Date, options?: StartOfWeekOptions): Date;
+12
View File
@@ -0,0 +1,12 @@
import { startOfWeek as startOfWeekFns, } from "date-fns";
/**
* Start of week
*
* @param {Date} date - The original date
* @param {StartOfWeekOptions} [options] - The options object
* @returns {Date} The start of the week
*/
export function startOfWeek(date, options) {
const weekStartsOn = options?.weekStartsOn ?? 1; // Default to Monday (1)
return startOfWeekFns(date, { weekStartsOn: weekStartsOn });
}
+7
View File
@@ -0,0 +1,7 @@
/**
* Start of year
*
* @param {Date} date - The original date
* @returns {Date} The start of the year
*/
export declare function startOfYear(date: Date): Date;
+11
View File
@@ -0,0 +1,11 @@
import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
/**
* Start of year
*
* @param {Date} date - The original date
* @returns {Date} The start of the year
*/
export function startOfYear(date) {
const { year } = toEthiopicDate(date);
return toGregorianDate({ year, month: 1, day: 1 });
}
@@ -0,0 +1,17 @@
/**
* Represents a date in the Ethiopic calendar system.
*
* The Ethiopic calendar has:
*
* - 13 months
* - 12 months of 30 days each
* - A 13th month (Pagume) of 5 or 6 days
*/
export interface EthiopicDate {
/** The Ethiopic year */
year: number;
/** The month number (1-13) */
month: number;
/** The day of the month (1-30, or 1-5/6 for month 13) */
day: number;
}
@@ -0,0 +1 @@
export {};
+13
View File
@@ -0,0 +1,13 @@
/**
* Returns the number of days in the specified month of the Ethiopic calendar.
*
* In the Ethiopic calendar:
*
* - Months 1-12 have 30 days each
* - Month 13 (Pagume) has 5 days in regular years, 6 days in leap years
*
* @param month - The month number (1-13)
* @param year - The Ethiopic year
* @returns The number of days in the specified month
*/
export declare function daysInMonth(month: number, year: number): number;
+19
View File
@@ -0,0 +1,19 @@
import { isEthiopicLeapYear } from "./isEthiopicLeapYear.js";
/**
* Returns the number of days in the specified month of the Ethiopic calendar.
*
* In the Ethiopic calendar:
*
* - Months 1-12 have 30 days each
* - Month 13 (Pagume) has 5 days in regular years, 6 days in leap years
*
* @param month - The month number (1-13)
* @param year - The Ethiopic year
* @returns The number of days in the specified month
*/
export function daysInMonth(month, year) {
if (month === 13) {
return isEthiopicLeapYear(year) ? 6 : 5;
}
return 30;
}
+6
View File
@@ -0,0 +1,6 @@
export * from "./daysInMonth.js";
export * from "./EthiopicDate.js";
export * from "./isEthiopicLeapYear.js";
export * from "./toEthiopicDate.js";
export * from "./toGeezNumerals.js";
export * from "./toGregorianDate.js";
+6
View File
@@ -0,0 +1,6 @@
export * from "./daysInMonth.js";
export * from "./EthiopicDate.js";
export * from "./isEthiopicLeapYear.js";
export * from "./toEthiopicDate.js";
export * from "./toGeezNumerals.js";
export * from "./toGregorianDate.js";
@@ -0,0 +1,2 @@
import type { EthiopicDate } from "./EthiopicDate.js";
export declare function isEthiopicDateValid(date: EthiopicDate): boolean;
@@ -0,0 +1,12 @@
import { daysInMonth } from "./daysInMonth.js";
export function isEthiopicDateValid(date) {
if (date.month < 1)
return false;
if (date.day < 1)
return false;
if (date.month > 13)
return false;
if (date.day > daysInMonth(date.month, date.year))
return false;
return true;
}
@@ -0,0 +1,7 @@
/**
* Checks if a given Ethiopic year is a leap year.
*
* @param year - The Ethiopic year.
* @returns True if the year is a leap year; otherwise, false.
*/
export declare function isEthiopicLeapYear(year: number): boolean;
@@ -0,0 +1,9 @@
/**
* Checks if a given Ethiopic year is a leap year.
*
* @param year - The Ethiopic year.
* @returns True if the year is a leap year; otherwise, false.
*/
export function isEthiopicLeapYear(year) {
return year % 4 === 3;
}
@@ -0,0 +1,17 @@
import type { EthiopicDate } from "./EthiopicDate.js";
/**
* Calculates the number of days between January 1, 0001 and the given date.
*
* @param date - A JavaScript Date object to calculate days from
* @returns The number of days since January 1, 0001. Returns 0 if the input is
* not a valid Date.
*/
export declare function getDayNoGregorian(date: Date): number;
/**
* Converts a Gregorian date to an Ethiopic date.
*
* @param gregorianDate - A JavaScript Date object representing the Gregorian
* date.
* @returns An EthiopicDate object.
*/
export declare function toEthiopicDate(gregorianDate: Date): EthiopicDate;
@@ -0,0 +1,50 @@
import { differenceInCalendarDays } from "date-fns";
/**
* Calculates the number of days between January 1, 0001 and the given date.
*
* @param date - A JavaScript Date object to calculate days from
* @returns The number of days since January 1, 0001. Returns 0 if the input is
* not a valid Date.
*/
export function getDayNoGregorian(date) {
if (!(date instanceof Date)) {
return 0;
}
// Create the start date as January 1, 0001 in the LOCAL timezone.
const adStart = new Date(0);
adStart.setFullYear(1, 0, 1);
adStart.setHours(0, 0, 0, 0);
// Calculate the number of days between the two dates, then add 1.
const dayNumber = differenceInCalendarDays(date, adStart) + 1;
return dayNumber;
}
function createEthiopicDate(dn) {
const num = Math.floor(dn / 1461);
const num2 = dn % 1461;
const num3 = Math.floor(num2 / 365);
const num4 = num2 % 365;
if (num2 !== 1460) {
return {
year: num * 4 + num3,
month: Math.floor(num4 / 30) + 1,
day: (num4 % 30) + 1,
};
}
else {
return {
year: num * 4 + num3 - 1,
month: 13,
day: 6,
};
}
}
/**
* Converts a Gregorian date to an Ethiopic date.
*
* @param gregorianDate - A JavaScript Date object representing the Gregorian
* date.
* @returns An EthiopicDate object.
*/
export function toEthiopicDate(gregorianDate) {
return createEthiopicDate(getDayNoGregorian(gregorianDate) - 2431);
}
@@ -0,0 +1,8 @@
/**
* Converts a number to Geez (Ethiopic) numerals.
*
* @param num - The number to convert
* @returns The number in Geez numerals
* @throws {Error} When input is 0 (Geez has no zero representation)
*/
export declare function toGeezNumerals(num: number): string;
@@ -0,0 +1,48 @@
/**
* Converts a number to Geez (Ethiopic) numerals.
*
* @param num - The number to convert
* @returns The number in Geez numerals
* @throws {Error} When input is 0 (Geez has no zero representation)
*/
export function toGeezNumerals(num) {
const geezDigits = ["፩", "፪", "፫", "፬", "፭", "፮", "፯", "፰", "፱"];
const geezTens = ["፲", "፳", "፴", "፵", "፶", "፷", "፸", "፹", "፺"];
const geezHundreds = "፻";
const geezThousands = "፼";
if (num === 0)
return "-";
if (num < 0)
return `-${toGeezNumerals(-num)}`;
let result = "";
let remaining = num;
// Handle thousands (10,000 and above)
if (remaining >= 10000) {
const thousandsValue = Math.floor(remaining / 10000);
result +=
thousandsValue === 1
? geezThousands
: toGeezNumerals(thousandsValue) + geezThousands;
remaining %= 10000;
}
// Handle hundreds (100 - 9,900)
if (remaining >= 100) {
const hundredsValue = Math.floor(remaining / 100);
result +=
hundredsValue === 1
? geezHundreds
: toGeezNumerals(hundredsValue) + geezHundreds;
remaining %= 100;
}
// Handle tens (10 - 90)
if (remaining >= 10) {
const tensValue = Math.floor(remaining / 10);
result += geezTens[tensValue - 1];
remaining %= 10;
}
// Handle ones (1 - 9)
if (remaining > 0) {
result += geezDigits[remaining - 1];
}
return result;
}
@@ -0,0 +1,9 @@
import type { EthiopicDate } from "./EthiopicDate.js";
export declare function getDayNoEthiopian(etDate: EthiopicDate): number;
/**
* Converts an Ethiopic date to a Gregorian date.
*
* @param ethiopicDate - An EthiopicDate object.
* @returns A JavaScript Date object representing the Gregorian date.
*/
export declare function toGregorianDate(ethiopicDate: EthiopicDate): Date;
@@ -0,0 +1,55 @@
import { getDaysInMonth } from "date-fns";
import { isEthiopicDateValid } from "./isEthiopicDateValid.js";
export function getDayNoEthiopian(etDate) {
const num = Math.floor(etDate.year / 4);
const num2 = etDate.year % 4;
return num * 1461 + num2 * 365 + (etDate.month - 1) * 30 + etDate.day - 1;
}
function gregorianDateFromDayNo(dayNum) {
let year = 1, month = 1, day;
const num400 = Math.floor(dayNum / 146097); // number of full 400-year periods
dayNum %= 146097;
if (dayNum === 0) {
return new Date(400 * num400, 12 - 1, 31);
}
const num100 = Math.min(Math.floor(dayNum / 36524), 3); // number of full 100-year periods, but not more than 3
dayNum -= num100 * 36524;
if (dayNum === 0) {
return new Date(400 * num400 + 100 * num100, 12 - 1, 31);
}
const num4 = Math.floor(dayNum / 1461); // number of full 4-year periods
dayNum %= 1461;
if (dayNum === 0) {
return new Date(400 * num400 + 100 * num100 + 4 * num4, 12 - 1, 31);
}
const num1 = Math.min(Math.floor(dayNum / 365), 3); // number of full years, but not more than 3
dayNum -= num1 * 365;
if (dayNum === 0) {
return new Date(400 * num400 + 100 * num100 + 4 * num4 + num1, 12 - 1, 31);
}
year += 400 * num400 + 100 * num100 + 4 * num4 + num1;
while (dayNum > 0) {
const tempDate = new Date(year, month - 1);
const daysInMonth = getDaysInMonth(tempDate);
if (dayNum <= daysInMonth) {
day = dayNum;
break;
}
dayNum -= daysInMonth;
month++;
}
// Remember in JavaScript Date object, months are 0-based.
return new Date(year, month - 1, day);
}
/**
* Converts an Ethiopic date to a Gregorian date.
*
* @param ethiopicDate - An EthiopicDate object.
* @returns A JavaScript Date object representing the Gregorian date.
*/
export function toGregorianDate(ethiopicDate) {
if (!isEthiopicDateValid(ethiopicDate)) {
throw new Error("Invalid Ethiopic date");
}
return gregorianDateFromDayNo(getDayNoEthiopian(ethiopicDate) + 2431);
}