diff --git a/ethiopic.d.ts b/ethiopic.d.ts
new file mode 100644
index 000000000..25c8b9938
--- /dev/null
+++ b/ethiopic.d.ts
@@ -0,0 +1 @@
+export * from "./dist/cjs/ethiopic/index.d.ts";
diff --git a/ethiopic.js b/ethiopic.js
new file mode 100644
index 000000000..8e859f82f
--- /dev/null
+++ b/ethiopic.js
@@ -0,0 +1,4 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+/* eslint-disable no-undef */
+const ethiopic = require("./dist/cjs/ethiopic/index.js");
+module.exports = ethiopic;
diff --git a/examples/Ethiopic.test.tsx b/examples/Ethiopic.test.tsx
new file mode 100644
index 000000000..c9f3964d1
--- /dev/null
+++ b/examples/Ethiopic.test.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+
+import { render } from "@/test/render";
+
+import { Ethiopic } from "./Ethiopic.jsx";
+
+const today = new Date(2021, 10, 25);
+
+beforeAll(() => jest.setSystemTime(today));
+afterAll(() => jest.useRealTimers());
+
+beforeEach(() => {
+ render();
+});
+
+test.todo("should render the Ethiopic calendar");
diff --git a/examples/Ethiopic.tsx b/examples/Ethiopic.tsx
new file mode 100644
index 000000000..0f3aef54f
--- /dev/null
+++ b/examples/Ethiopic.tsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+import { DayPicker } from "react-day-picker/ethiopic";
+
+export function Ethiopic() {
+ return ;
+}
diff --git a/examples/index.ts b/examples/index.ts
index 0e1910563..bde95af30 100644
--- a/examples/index.ts
+++ b/examples/index.ts
@@ -18,6 +18,7 @@ export * from "./DisableNavigation";
export * from "./Dropdown";
export * from "./DropdownMonths";
export * from "./DropdownMultipleMonths";
+export * from "./Ethiopic";
export * from "./Fixedweeks";
export * from "./FocusRecursive";
export * from "./Footer";
diff --git a/package.json b/package.json
index fc084be83..ec47e5ad6 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,16 @@
"default": "./dist/cjs/persian.js"
}
},
+ "./ethiopic": {
+ "import": {
+ "types": "./dist/esm/ethiopic/index.d.ts",
+ "default": "./dist/esm/ethiopic/index.js"
+ },
+ "require": {
+ "types": "./dist/cjs/ethiopic/index.d.ts",
+ "default": "./dist/cjs/ethiopic/index.js"
+ }
+ },
"./locale": {
"import": {
"types": "./dist/esm/locale.d.ts",
diff --git a/react-day-picker.code-workspace b/react-day-picker.code-workspace
index 0c71bb1e8..51f204563 100644
--- a/react-day-picker.code-workspace
+++ b/react-day-picker.code-workspace
@@ -57,6 +57,7 @@
"activityBar.inactiveForeground": "#d8d8d877",
"activityBarBadge.background": "#dd006f",
"activityBarBadge.foreground": "#e7e7e7"
- }
+ },
+ "cSpell.words": ["ethiopic", "gregorian", "julian"]
}
}
diff --git a/src/ethiopic/index.test.tsx b/src/ethiopic/index.test.tsx
new file mode 100644
index 000000000..d26b38a59
--- /dev/null
+++ b/src/ethiopic/index.test.tsx
@@ -0,0 +1,5 @@
+describe("DayPicker", () => {
+ test.todo("should render with default props");
+ test.todo("should render with custom locale");
+ test.todo("should render with custom numerals");
+});
diff --git a/src/ethiopic/index.tsx b/src/ethiopic/index.tsx
new file mode 100644
index 000000000..c0c637325
--- /dev/null
+++ b/src/ethiopic/index.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+
+import type { Locale } from "date-fns";
+
+import {
+ DateLib,
+ DateLibOptions,
+ DayPicker as DayPickerComponent
+} from "../index.js";
+import type { DayPickerProps } from "../types/props.js";
+
+import * as ethiopicDateLib from "./lib/index.js";
+
+/**
+ * Render the Persian Calendar.
+ *
+ * @see https://daypicker.dev/docs/localization#persian-calendar
+ */
+export 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)
+ * - `arab`: Arabic-Indic
+ * - `arabext`: Eastern Arabic-Indic (Persian)
+ * - `deva`: Devanagari
+ * - `ethio`: Ethiopic
+ * - `beng`: Bengali
+ * - `guru`: Gurmukhi
+ * - `gujr`: Gujarati
+ * - `orya`: Oriya
+ * - `tamldec`: Tamil
+ * - `telu`: Telugu
+ * - `knda`: Kannada
+ * - `mlym`: Malayalam
+ *
+ * @defaultValue `ethio` Eastern Arabic-Indic (Persian)
+ * @see https://daypicker.dev/docs/translation#numeral-systems
+ */
+ numerals?: DayPickerProps["numerals"];
+ }
+) {
+ 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 (
+
+ );
+}
+
+/** Returns the date library used in the calendar. */
+export const getDateLib = (options?: DateLibOptions) => {
+ return new DateLib(options, ethiopicDateLib);
+};
diff --git a/src/ethiopic/lib/addDays.test.ts b/src/ethiopic/lib/addDays.test.ts
new file mode 100644
index 000000000..7bc0f6a0c
--- /dev/null
+++ b/src/ethiopic/lib/addDays.test.ts
@@ -0,0 +1 @@
+test.todo("addDays should correctly add days to an Ethiopic date");
diff --git a/src/ethiopic/lib/addDays.ts b/src/ethiopic/lib/addDays.ts
new file mode 100644
index 000000000..91974defc
--- /dev/null
+++ b/src/ethiopic/lib/addDays.ts
@@ -0,0 +1,12 @@
+/**
+ * Adds days to an Ethiopic date
+ *
+ * @param {Date} date - The original date
+ * @param {number} amount - The number of days to add
+ * @returns {Date} The new date
+ */
+export function addDays(date: Date, amount: number): Date {
+ const julianDay = Math.floor(date.getTime() / 86400000 + 2440587.5);
+ const newJulianDay = julianDay + amount;
+ return new Date((newJulianDay - 2440587.5) * 86400000);
+}
diff --git a/src/ethiopic/lib/addMonths.test.ts b/src/ethiopic/lib/addMonths.test.ts
new file mode 100644
index 000000000..1b8806873
--- /dev/null
+++ b/src/ethiopic/lib/addMonths.test.ts
@@ -0,0 +1 @@
+test.todo("addMonths should correctly add months to an Ethiopic date");
diff --git a/src/ethiopic/lib/addMonths.ts b/src/ethiopic/lib/addMonths.ts
new file mode 100644
index 000000000..85d2b4220
--- /dev/null
+++ b/src/ethiopic/lib/addMonths.ts
@@ -0,0 +1,16 @@
+import { toEthiopicDate, toGregorianDate } from "../utils/index.js";
+
+/**
+ * Adds months to an Ethiopic date
+ *
+ * @param {Date} date - The original date
+ * @param {number} amount - The number of months to add
+ * @returns {Date} The new date
+ */
+export function addMonths(date: Date, amount: number): Date {
+ const { year, month, day } = toEthiopicDate(date);
+ const totalMonths = month + amount - 1;
+ const newYear = year + Math.floor(totalMonths / 12);
+ const newMonth = (totalMonths % 12) + 1;
+ return toGregorianDate({ year: newYear, month: newMonth, day });
+}
diff --git a/src/ethiopic/lib/addWeeks.test.ts b/src/ethiopic/lib/addWeeks.test.ts
new file mode 100644
index 000000000..67fa362aa
--- /dev/null
+++ b/src/ethiopic/lib/addWeeks.test.ts
@@ -0,0 +1 @@
+test.todo("addWeeks should correctly add weeks to an Ethiopic date");
diff --git a/src/ethiopic/lib/addWeeks.ts b/src/ethiopic/lib/addWeeks.ts
new file mode 100644
index 000000000..5363453f0
--- /dev/null
+++ b/src/ethiopic/lib/addWeeks.ts
@@ -0,0 +1,12 @@
+import { addDays } from "./addDays.js";
+
+/**
+ * Adds weeks to an Ethiopic date
+ *
+ * @param {Date} date - The original date
+ * @param {number} amount - The number of weeks to add
+ * @returns {Date} The new date
+ */
+export function addWeeks(date: Date, amount: number): Date {
+ return addDays(date, amount * 7);
+}
diff --git a/src/ethiopic/lib/addYears.test.ts b/src/ethiopic/lib/addYears.test.ts
new file mode 100644
index 000000000..09bfb117a
--- /dev/null
+++ b/src/ethiopic/lib/addYears.test.ts
@@ -0,0 +1 @@
+test.todo("addYears should correctly add years to an Ethiopic date");
diff --git a/src/ethiopic/lib/addYears.ts b/src/ethiopic/lib/addYears.ts
new file mode 100644
index 000000000..e80d26fd2
--- /dev/null
+++ b/src/ethiopic/lib/addYears.ts
@@ -0,0 +1,20 @@
+import {
+ toEthiopicDate,
+ isEthiopicLeapYear,
+ toGregorianDate
+} from "../utils/index.js";
+
+/**
+ * Adds years to an Ethiopic date
+ *
+ * @param {Date} date - The original date
+ * @param {number} amount - The number of years to add
+ * @returns {Date} The new date
+ */
+export function addYears(date: Date, amount: number): Date {
+ const { year, month, day } = toEthiopicDate(date);
+ const newYear = year + amount;
+ const newDay =
+ month === 13 && day === 6 && !isEthiopicLeapYear(newYear) ? 5 : day;
+ return toGregorianDate({ year: newYear, month, day: newDay });
+}
diff --git a/src/ethiopic/lib/differenceInCalendarDays.test.ts b/src/ethiopic/lib/differenceInCalendarDays.test.ts
new file mode 100644
index 000000000..95aa1e954
--- /dev/null
+++ b/src/ethiopic/lib/differenceInCalendarDays.test.ts
@@ -0,0 +1,3 @@
+test.todo(
+ "differenceInCalendarDays should correctly calculate the number of calendar days between two dates"
+);
diff --git a/src/ethiopic/lib/differenceInCalendarDays.ts b/src/ethiopic/lib/differenceInCalendarDays.ts
new file mode 100644
index 000000000..8e642ff67
--- /dev/null
+++ b/src/ethiopic/lib/differenceInCalendarDays.ts
@@ -0,0 +1,23 @@
+import { toEthiopicDate, isEthiopicLeapYear } from "../utils/index.js";
+
+/**
+ * Difference in calendar days
+ *
+ * @param {Date} dateLeft - The later date
+ * @param {Date} dateRight - The earlier date
+ * @returns {number} The number of calendar days between the two dates
+ */
+export function differenceInCalendarDays(
+ dateLeft: Date,
+ dateRight: Date
+): number {
+ const leftYear = toEthiopicDate(dateLeft).year;
+ const rightYear = toEthiopicDate(dateRight).year;
+ const leapDays = Array.from(
+ { length: leftYear - rightYear },
+ (_, i) => rightYear + i
+ ).filter(isEthiopicLeapYear).length;
+ return (
+ Math.floor((dateLeft.getTime() - dateRight.getTime()) / 86400000) + leapDays
+ );
+}
diff --git a/src/ethiopic/lib/differenceInCalendarMonths.ts b/src/ethiopic/lib/differenceInCalendarMonths.ts
new file mode 100644
index 000000000..03cc06d8d
--- /dev/null
+++ b/src/ethiopic/lib/differenceInCalendarMonths.ts
@@ -0,0 +1,25 @@
+import { toEthiopicDate, isEthiopicLeapYear } 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: Date,
+ dateRight: Date
+): number {
+ const ethiopicLeft = toEthiopicDate(dateLeft);
+ const ethiopicRight = toEthiopicDate(dateRight);
+ const leapDays = Array.from(
+ { length: ethiopicLeft.year - ethiopicRight.year },
+ (_, i) => ethiopicRight.year + i
+ ).filter(isEthiopicLeapYear).length;
+ return (
+ (ethiopicLeft.year - ethiopicRight.year) * 12 +
+ (ethiopicLeft.month - ethiopicRight.month) +
+ leapDays
+ );
+}
diff --git a/src/ethiopic/lib/eachMonthOfInterval.test.ts b/src/ethiopic/lib/eachMonthOfInterval.test.ts
new file mode 100644
index 000000000..7cadbf2d7
--- /dev/null
+++ b/src/ethiopic/lib/eachMonthOfInterval.test.ts
@@ -0,0 +1,3 @@
+test.todo(
+ "should return an array of dates representing the start of each month in the interval"
+);
diff --git a/src/ethiopic/lib/eachMonthOfInterval.ts b/src/ethiopic/lib/eachMonthOfInterval.ts
new file mode 100644
index 000000000..2acd82d2e
--- /dev/null
+++ b/src/ethiopic/lib/eachMonthOfInterval.ts
@@ -0,0 +1,38 @@
+import type { Interval } from "date-fns";
+
+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: Interval): Date[] {
+ const start = toEthiopicDate(new Date(interval.start));
+ const end = toEthiopicDate(new Date(interval.end));
+ const dates: Date[] = [];
+
+ 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 > 12) {
+ currentMonth = 1;
+ currentYear++;
+ }
+ }
+
+ return dates;
+}
diff --git a/src/ethiopic/lib/endOfMonth.test.ts b/src/ethiopic/lib/endOfMonth.test.ts
new file mode 100644
index 000000000..4fd3d089c
--- /dev/null
+++ b/src/ethiopic/lib/endOfMonth.test.ts
@@ -0,0 +1 @@
+test.todo("should return the correct end of the month date for a given date");
diff --git a/src/ethiopic/lib/endOfMonth.ts b/src/ethiopic/lib/endOfMonth.ts
new file mode 100644
index 000000000..dbc8c9a2c
--- /dev/null
+++ b/src/ethiopic/lib/endOfMonth.ts
@@ -0,0 +1,17 @@
+import {
+ toEthiopicDate,
+ isEthiopicLeapYear,
+ toGregorianDate
+} from "../utils/index.js";
+
+/**
+ * End of month
+ *
+ * @param {Date} date - The original date
+ * @returns {Date} The end of the month
+ */
+export function endOfMonth(date: Date): Date {
+ const { year, month } = toEthiopicDate(date);
+ const daysInMonth = month === 13 ? (isEthiopicLeapYear(year) ? 6 : 5) : 30;
+ return toGregorianDate({ year, month, day: daysInMonth });
+}
diff --git a/src/ethiopic/lib/endOfWeek.test.ts b/src/ethiopic/lib/endOfWeek.test.ts
new file mode 100644
index 000000000..51706060d
--- /dev/null
+++ b/src/ethiopic/lib/endOfWeek.test.ts
@@ -0,0 +1 @@
+test.todo("should return the correct end of the week date for a given date");
diff --git a/src/ethiopic/lib/endOfWeek.ts b/src/ethiopic/lib/endOfWeek.ts
new file mode 100644
index 000000000..696761e92
--- /dev/null
+++ b/src/ethiopic/lib/endOfWeek.ts
@@ -0,0 +1,20 @@
+import { addDays } from "./addDays.js";
+
+/**
+ * End of week
+ *
+ * @param {Date} date - The original date
+ * @param {Object} [options] - The options object
+ * @param {number} [options.weekStartsOn=0] - The index of the first day of the
+ * week (0 - Sunday). Default is `0`
+ * @returns {Date} The end of the week
+ */
+export function endOfWeek(
+ date: Date,
+ options?: { weekStartsOn?: number }
+): Date {
+ const weekStartsOn = options?.weekStartsOn ?? 0;
+ const day = date.getDay();
+ const diff = (7 - day + weekStartsOn - 1) % 7;
+ return addDays(date, diff);
+}
diff --git a/src/ethiopic/lib/endOfYear.test.ts b/src/ethiopic/lib/endOfYear.test.ts
new file mode 100644
index 000000000..6b88f61e8
--- /dev/null
+++ b/src/ethiopic/lib/endOfYear.test.ts
@@ -0,0 +1 @@
+test.todo("should return the correct end of the year date for a given date");
diff --git a/src/ethiopic/lib/endOfYear.ts b/src/ethiopic/lib/endOfYear.ts
new file mode 100644
index 000000000..f28854704
--- /dev/null
+++ b/src/ethiopic/lib/endOfYear.ts
@@ -0,0 +1,17 @@
+import {
+ toEthiopicDate,
+ isEthiopicLeapYear,
+ 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: Date): Date {
+ const { year } = toEthiopicDate(date);
+ const day = isEthiopicLeapYear(year) ? 6 : 5;
+ return toGregorianDate({ year, month: 13, day });
+}
diff --git a/src/ethiopic/lib/getMonth.test.ts b/src/ethiopic/lib/getMonth.test.ts
new file mode 100644
index 000000000..addfd9364
--- /dev/null
+++ b/src/ethiopic/lib/getMonth.test.ts
@@ -0,0 +1 @@
+test.todo("should return the correct zero-based month index for a given date");
diff --git a/src/ethiopic/lib/getMonth.ts b/src/ethiopic/lib/getMonth.ts
new file mode 100644
index 000000000..ef334c8f2
--- /dev/null
+++ b/src/ethiopic/lib/getMonth.ts
@@ -0,0 +1,12 @@
+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: Date): number {
+ const { month } = toEthiopicDate(date);
+ return month - 1; // Return zero-based month index
+}
diff --git a/src/ethiopic/lib/getWeek.test.ts b/src/ethiopic/lib/getWeek.test.ts
new file mode 100644
index 000000000..f3a007310
--- /dev/null
+++ b/src/ethiopic/lib/getWeek.test.ts
@@ -0,0 +1 @@
+test.todo("should return the correct week number for a given date");
diff --git a/src/ethiopic/lib/getWeek.ts b/src/ethiopic/lib/getWeek.ts
new file mode 100644
index 000000000..6032b828a
--- /dev/null
+++ b/src/ethiopic/lib/getWeek.ts
@@ -0,0 +1,26 @@
+import { toGregorianDate, toEthiopicDate } from "../utils/index.js";
+
+import { differenceInCalendarDays } from "./differenceInCalendarDays.js";
+
+/**
+ * Get week
+ *
+ * @param {Date} date - The original date
+ * @param {Object} [options] - The options object
+ * @param {number} [options.weekStartsOn=0] - The index of the first day of the
+ * week (0 - Sunday). Default is `0`
+ * @returns {number} The week number
+ */
+export function getWeek(
+ date: Date,
+ options?: { weekStartsOn?: number }
+): number {
+ const weekStartsOn = options?.weekStartsOn ?? 0; // Default to Sunday
+ const startOfYear = toGregorianDate({
+ year: toEthiopicDate(date).year,
+ month: 1,
+ day: 1
+ });
+ const diffInDays = differenceInCalendarDays(date, startOfYear);
+ return Math.floor((diffInDays + weekStartsOn) / 7) + 1;
+}
diff --git a/src/ethiopic/lib/getYear.test.ts b/src/ethiopic/lib/getYear.test.ts
new file mode 100644
index 000000000..32fb406fc
--- /dev/null
+++ b/src/ethiopic/lib/getYear.test.ts
@@ -0,0 +1 @@
+test.todo("should return the correct Ethiopic year for a given date");
diff --git a/src/ethiopic/lib/getYear.ts b/src/ethiopic/lib/getYear.ts
new file mode 100644
index 000000000..c8eac3d2b
--- /dev/null
+++ b/src/ethiopic/lib/getYear.ts
@@ -0,0 +1,12 @@
+import { toEthiopicDate } from "../utils/index.js";
+
+/**
+ * Get year
+ *
+ * @param {Date} date - The original date
+ * @returns {number} The year
+ */
+export function getYear(date: Date): number {
+ const { year } = toEthiopicDate(date);
+ return year;
+}
diff --git a/src/ethiopic/lib/index.ts b/src/ethiopic/lib/index.ts
new file mode 100644
index 000000000..0809e9e8a
--- /dev/null
+++ b/src/ethiopic/lib/index.ts
@@ -0,0 +1,23 @@
+export * from "./addDays.js";
+export * from "./addMonths.js";
+export * from "./addWeeks.js";
+export * from "./addYears.js";
+export * from "./differenceInCalendarDays.js";
+export * from "./differenceInCalendarMonths.js";
+export * from "./eachMonthOfInterval.js";
+export * from "./endOfMonth.js";
+export * from "./endOfWeek.js";
+export * from "./endOfYear.js";
+export * from "./getMonth.js";
+export * from "./getWeek.js";
+export * from "./getYear.js";
+export * from "./isSameDay.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";
diff --git a/src/ethiopic/lib/isSameDay.test.ts b/src/ethiopic/lib/isSameDay.test.ts
new file mode 100644
index 000000000..4363ac2fd
--- /dev/null
+++ b/src/ethiopic/lib/isSameDay.test.ts
@@ -0,0 +1 @@
+test.todo("isSameDay should return true if two dates are on the same day");
diff --git a/src/ethiopic/lib/isSameDay.ts b/src/ethiopic/lib/isSameDay.ts
new file mode 100644
index 000000000..90364abd9
--- /dev/null
+++ b/src/ethiopic/lib/isSameDay.ts
@@ -0,0 +1,18 @@
+import { toEthiopicDate } from "../utils/index.js";
+
+/**
+ * Is same day
+ *
+ * @param {Date} dateLeft - The first date
+ * @param {Date} dateRight - The second date
+ * @returns {boolean} True if the two dates are on the same day
+ */
+export function isSameDay(dateLeft: Date, dateRight: Date): boolean {
+ const left = toEthiopicDate(dateLeft);
+ const right = toEthiopicDate(dateRight);
+ return (
+ left.year === right.year &&
+ left.month === right.month &&
+ left.day === right.day
+ );
+}
diff --git a/src/ethiopic/lib/isSameMonth.test.ts b/src/ethiopic/lib/isSameMonth.test.ts
new file mode 100644
index 000000000..d5355b629
--- /dev/null
+++ b/src/ethiopic/lib/isSameMonth.test.ts
@@ -0,0 +1 @@
+test.todo("isSameMonth should return true if two dates are in the same month");
diff --git a/src/ethiopic/lib/isSameMonth.ts b/src/ethiopic/lib/isSameMonth.ts
new file mode 100644
index 000000000..02f7d2bd6
--- /dev/null
+++ b/src/ethiopic/lib/isSameMonth.ts
@@ -0,0 +1,14 @@
+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: Date, dateRight: Date): boolean {
+ const left = toEthiopicDate(dateLeft);
+ const right = toEthiopicDate(dateRight);
+ return left.year === right.year && left.month === right.month;
+}
diff --git a/src/ethiopic/lib/isSameYear.test.ts b/src/ethiopic/lib/isSameYear.test.ts
new file mode 100644
index 000000000..a5bc214df
--- /dev/null
+++ b/src/ethiopic/lib/isSameYear.test.ts
@@ -0,0 +1 @@
+test.todo("isSameYear should return true if two dates are in the same year");
diff --git a/src/ethiopic/lib/isSameYear.ts b/src/ethiopic/lib/isSameYear.ts
new file mode 100644
index 000000000..28c146e44
--- /dev/null
+++ b/src/ethiopic/lib/isSameYear.ts
@@ -0,0 +1,14 @@
+import { toEthiopicDate } from "../utils/index.js";
+
+/**
+ * Is same year
+ *
+ * @param {Date} dateLeft - The first date
+ * @param {Date} dateRight - The second date
+ * @returns {boolean} True if the two dates are in the same year
+ */
+export function isSameYear(dateLeft: Date, dateRight: Date): boolean {
+ const left = toEthiopicDate(dateLeft);
+ const right = toEthiopicDate(dateRight);
+ return left.year === right.year;
+}
diff --git a/src/ethiopic/lib/newDate.test.ts b/src/ethiopic/lib/newDate.test.ts
new file mode 100644
index 000000000..d798aaad6
--- /dev/null
+++ b/src/ethiopic/lib/newDate.test.ts
@@ -0,0 +1,3 @@
+describe("newDate", () => {
+ test.todo("should create a new Ethiopic date");
+});
diff --git a/src/ethiopic/lib/newDate.ts b/src/ethiopic/lib/newDate.ts
new file mode 100644
index 000000000..c494ccefc
--- /dev/null
+++ b/src/ethiopic/lib/newDate.ts
@@ -0,0 +1,13 @@
+import { toGregorianDate } from "../utils/index.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: number, monthIndex: number, date: number): Date {
+ return toGregorianDate({ year, month: monthIndex + 1, day: date });
+}
diff --git a/src/ethiopic/lib/setMonth.test.ts b/src/ethiopic/lib/setMonth.test.ts
new file mode 100644
index 000000000..1004cfcca
--- /dev/null
+++ b/src/ethiopic/lib/setMonth.test.ts
@@ -0,0 +1,3 @@
+describe("setMonth", () => {
+ test.todo("set the Ethiopic month for a given date");
+});
diff --git a/src/ethiopic/lib/setMonth.ts b/src/ethiopic/lib/setMonth.ts
new file mode 100644
index 000000000..ab2207598
--- /dev/null
+++ b/src/ethiopic/lib/setMonth.ts
@@ -0,0 +1,13 @@
+import { 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: Date, month: number): Date {
+ const { year, day } = toEthiopicDate(date);
+ return toGregorianDate({ year, month: month + 1, day }); // Add 1 to month as it's zero-based
+}
diff --git a/src/ethiopic/lib/setYear.test.ts b/src/ethiopic/lib/setYear.test.ts
new file mode 100644
index 000000000..cb6524ee8
--- /dev/null
+++ b/src/ethiopic/lib/setYear.test.ts
@@ -0,0 +1,3 @@
+describe("setYear", () => {
+ test.todo("set the Ethiopic year for a given date");
+});
diff --git a/src/ethiopic/lib/setYear.ts b/src/ethiopic/lib/setYear.ts
new file mode 100644
index 000000000..e86877735
--- /dev/null
+++ b/src/ethiopic/lib/setYear.ts
@@ -0,0 +1,13 @@
+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: Date, year: number): Date {
+ const { month, day } = toEthiopicDate(date);
+ return toGregorianDate({ year, month, day });
+}
diff --git a/src/ethiopic/lib/startOfDay.test.ts b/src/ethiopic/lib/startOfDay.test.ts
new file mode 100644
index 000000000..195e1963b
--- /dev/null
+++ b/src/ethiopic/lib/startOfDay.test.ts
@@ -0,0 +1,3 @@
+describe("startOfDay", () => {
+ test.todo("should return the start of the Ethiopic day for a given date");
+});
diff --git a/src/ethiopic/lib/startOfDay.ts b/src/ethiopic/lib/startOfDay.ts
new file mode 100644
index 000000000..b94e7d5e7
--- /dev/null
+++ b/src/ethiopic/lib/startOfDay.ts
@@ -0,0 +1,12 @@
+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: Date): Date {
+ const { year, month, day } = toEthiopicDate(date);
+ return toGregorianDate({ year, month, day });
+}
diff --git a/src/ethiopic/lib/startOfMonth.test.ts b/src/ethiopic/lib/startOfMonth.test.ts
new file mode 100644
index 000000000..b9e725910
--- /dev/null
+++ b/src/ethiopic/lib/startOfMonth.test.ts
@@ -0,0 +1 @@
+test.todo("startOfMonth should return the start of the Ethiopic month");
diff --git a/src/ethiopic/lib/startOfMonth.ts b/src/ethiopic/lib/startOfMonth.ts
new file mode 100644
index 000000000..8f4e156f6
--- /dev/null
+++ b/src/ethiopic/lib/startOfMonth.ts
@@ -0,0 +1,12 @@
+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: Date): Date {
+ const { year, month } = toEthiopicDate(date);
+ return toGregorianDate({ year, month, day: 1 });
+}
diff --git a/src/ethiopic/lib/startOfWeek.test.ts b/src/ethiopic/lib/startOfWeek.test.ts
new file mode 100644
index 000000000..37eb654e9
--- /dev/null
+++ b/src/ethiopic/lib/startOfWeek.test.ts
@@ -0,0 +1 @@
+test.todo("startOfWeek should return the start of the week");
diff --git a/src/ethiopic/lib/startOfWeek.ts b/src/ethiopic/lib/startOfWeek.ts
new file mode 100644
index 000000000..ebf774cec
--- /dev/null
+++ b/src/ethiopic/lib/startOfWeek.ts
@@ -0,0 +1,12 @@
+import { addDays } from "./addDays.js";
+
+/**
+ * Start of week
+ *
+ * @param {Date} date - The original date
+ * @returns {Date} The start of the week
+ */
+export function startOfWeek(date: Date): Date {
+ const day = date.getDay();
+ return addDays(date, -day); // Subtract days to get to Sunday (start of week)
+}
diff --git a/src/ethiopic/lib/startOfYear.test.ts b/src/ethiopic/lib/startOfYear.test.ts
new file mode 100644
index 000000000..7031f7887
--- /dev/null
+++ b/src/ethiopic/lib/startOfYear.test.ts
@@ -0,0 +1 @@
+test.todo("startOfYear should return the start of the Ethiopic year");
diff --git a/src/ethiopic/lib/startOfYear.ts b/src/ethiopic/lib/startOfYear.ts
new file mode 100644
index 000000000..6d2354625
--- /dev/null
+++ b/src/ethiopic/lib/startOfYear.ts
@@ -0,0 +1,12 @@
+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: Date): Date {
+ const { year } = toEthiopicDate(date);
+ return toGregorianDate({ year, month: 1, day: 1 });
+}
diff --git a/src/ethiopic/utils/EthiopicDate.ts b/src/ethiopic/utils/EthiopicDate.ts
new file mode 100644
index 000000000..ae2ea143e
--- /dev/null
+++ b/src/ethiopic/utils/EthiopicDate.ts
@@ -0,0 +1,5 @@
+export interface EthiopicDate {
+ year: number;
+ month: number;
+ day: number;
+}
diff --git a/src/ethiopic/utils/consts.ts b/src/ethiopic/utils/consts.ts
new file mode 100644
index 000000000..c8e6f9069
--- /dev/null
+++ b/src/ethiopic/utils/consts.ts
@@ -0,0 +1,4 @@
+/** Julian Day number offset for the Ethiopic calendar */
+export const ETHIOPIC_EPOCH_OFFSET = -285019;
+
+// TODO: Add more constants
diff --git a/src/ethiopic/utils/index.ts b/src/ethiopic/utils/index.ts
new file mode 100644
index 000000000..1dc38356a
--- /dev/null
+++ b/src/ethiopic/utils/index.ts
@@ -0,0 +1,4 @@
+export * from "./EthiopicDate.js";
+export * from "./isEthiopicLeapYear.js";
+export * from "./toEthiopicDate.js";
+export * from "./toGregorianDate.js";
diff --git a/src/ethiopic/utils/isEthiopicLeapYear.test.ts b/src/ethiopic/utils/isEthiopicLeapYear.test.ts
new file mode 100644
index 000000000..ece0ec678
--- /dev/null
+++ b/src/ethiopic/utils/isEthiopicLeapYear.test.ts
@@ -0,0 +1,4 @@
+describe("isEthiopicLeapYear", () => {
+ test.todo("should return true for a leap year");
+ test.todo("should return false for a non-leap year");
+});
diff --git a/src/ethiopic/utils/isEthiopicLeapYear.ts b/src/ethiopic/utils/isEthiopicLeapYear.ts
new file mode 100644
index 000000000..d3eb04651
--- /dev/null
+++ b/src/ethiopic/utils/isEthiopicLeapYear.ts
@@ -0,0 +1,10 @@
+/**
+ * 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: number): boolean {
+ return year % 4 === 3;
+}
diff --git a/src/ethiopic/utils/toEthiopicDate.test.ts b/src/ethiopic/utils/toEthiopicDate.test.ts
new file mode 100644
index 000000000..3d30b8285
--- /dev/null
+++ b/src/ethiopic/utils/toEthiopicDate.test.ts
@@ -0,0 +1,3 @@
+test.todo("should convert a Gregorian date to an Ethiopic date correctly");
+test.todo("should handle leap years correctly");
+test.todo("should handle dates before the Ethiopic epoch correctly");
diff --git a/src/ethiopic/utils/toEthiopicDate.ts b/src/ethiopic/utils/toEthiopicDate.ts
new file mode 100644
index 000000000..a100b0d87
--- /dev/null
+++ b/src/ethiopic/utils/toEthiopicDate.ts
@@ -0,0 +1,21 @@
+import type { EthiopicDate } from "./EthiopicDate.js";
+import { ETHIOPIC_EPOCH_OFFSET } from "./consts.js";
+
+/**
+ * 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: Date): EthiopicDate {
+ const julianDay = Math.floor(gregorianDate.getTime() / 86400000 + 2440587.5);
+ const ethiopicDayNumber = julianDay + ETHIOPIC_EPOCH_OFFSET;
+
+ const year = Math.floor((4 * ethiopicDayNumber + 1463) / 1461);
+ const dayOfYear = ethiopicDayNumber - (365 * year + Math.floor(year / 4));
+ const month = Math.floor(dayOfYear / 30) + 1;
+ const day = (dayOfYear % 30) + 1;
+
+ return { year, month, day };
+}
diff --git a/src/ethiopic/utils/toGregorianDate.test.ts b/src/ethiopic/utils/toGregorianDate.test.ts
new file mode 100644
index 000000000..2937f87bf
--- /dev/null
+++ b/src/ethiopic/utils/toGregorianDate.test.ts
@@ -0,0 +1,8 @@
+describe("toGregorianDate", () => {
+ test.todo("convert an Ethiopic date to the correct Gregorian date");
+ test.todo("handle leap years correctly");
+ test.todo("handle the last day of the year correctly");
+ test.todo("handle the first day of the year correctly");
+ test.todo("handle invalid dates gracefully");
+ test.todo("handle edge cases for month and day values");
+});
diff --git a/src/ethiopic/utils/toGregorianDate.ts b/src/ethiopic/utils/toGregorianDate.ts
new file mode 100644
index 000000000..5dd0ec151
--- /dev/null
+++ b/src/ethiopic/utils/toGregorianDate.ts
@@ -0,0 +1,19 @@
+import type { EthiopicDate } from "./EthiopicDate.js";
+import { ETHIOPIC_EPOCH_OFFSET } from "./consts.js";
+
+/**
+ * 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: EthiopicDate): Date {
+ const yearStartJulianDay =
+ 365 * ethiopicDate.year +
+ Math.floor(ethiopicDate.year / 4) -
+ ETHIOPIC_EPOCH_OFFSET;
+ const julianDay =
+ yearStartJulianDay + (ethiopicDate.month - 1) * 30 + (ethiopicDate.day - 1);
+ const gregorianTime = (julianDay - 2440587.5) * 86400000;
+ return new Date(gregorianTime);
+}
diff --git a/src/persian.tsx b/src/persian.tsx
index 9a1a2ff2d..7af6b3c49 100644
--- a/src/persian.tsx
+++ b/src/persian.tsx
@@ -46,6 +46,7 @@ export function DayPicker(
* - `arab`: Arabic-Indic
* - `arabext`: Eastern Arabic-Indic (Persian)
* - `deva`: Devanagari
+ * - `ethio`: Ethiopic
* - `beng`: Bengali
* - `guru`: Gurmukhi
* - `gujr`: Gujarati
diff --git a/src/types/props.ts b/src/types/props.ts
index e9d8496f0..d75a8dd96 100644
--- a/src/types/props.ts
+++ b/src/types/props.ts
@@ -398,6 +398,7 @@ export interface PropsBase {
* - `arab`: Arabic-Indic
* - `arabext`: Eastern Arabic-Indic (Persian)
* - `deva`: Devanagari
+ * - `ethio`: Ethiopic
* - `beng`: Bengali
* - `guru`: Gurmukhi
* - `gujr`: Gujarati
diff --git a/src/types/shared.ts b/src/types/shared.ts
index be3cae5e5..27cb3a8a4 100644
--- a/src/types/shared.ts
+++ b/src/types/shared.ts
@@ -416,6 +416,7 @@ export type MoveFocusBy =
* - `arab`: Arabic-Indic
* - `arabext`: Eastern Arabic-Indic (Persian)
* - `deva`: Devanagari
+ * - `ethio`: Ethiopic
* - `beng`: Bengali
* - `guru`: Gurmukhi
* - `gujr`: Gujarati
@@ -432,6 +433,7 @@ export type Numerals =
| "arab"
| "arabext"
| "deva"
+ | "ethio"
| "beng"
| "guru"
| "gujr"