Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ethiopic calendar #2658

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ethiopic.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./dist/cjs/ethiopic/index.d.ts";
4 changes: 4 additions & 0 deletions ethiopic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable no-undef */
const ethiopic = require("./dist/cjs/ethiopic/index.js");
gpbl marked this conversation as resolved.
Show resolved Hide resolved
module.exports = ethiopic;
16 changes: 16 additions & 0 deletions examples/Ethiopic.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Ethiopic />);
});

test.todo("should render the Ethiopic calendar");
7 changes: 7 additions & 0 deletions examples/Ethiopic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

import { DayPicker } from "react-day-picker/ethiopic";

export function Ethiopic() {
return <DayPicker />;
}
1 change: 1 addition & 0 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion react-day-picker.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"activityBar.inactiveForeground": "#d8d8d877",
"activityBarBadge.background": "#dd006f",
"activityBarBadge.foreground": "#e7e7e7"
}
},
"cSpell.words": ["ethiopic", "gregorian", "julian"]
}
}
5 changes: 5 additions & 0 deletions src/ethiopic/index.test.tsx
Original file line number Diff line number Diff line change
@@ -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");
});
71 changes: 71 additions & 0 deletions src/ethiopic/index.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<DayPickerComponent
{...props}
locale={props.locale ?? ({} as Locale)}
numerals={props.numerals ?? "ethio"}
dateLib={dateLib}
/>
);
}

/** Returns the date library used in the calendar. */
export const getDateLib = (options?: DateLibOptions) => {
return new DateLib(options, ethiopicDateLib);
};
1 change: 1 addition & 0 deletions src/ethiopic/lib/addDays.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("addDays should correctly add days to an Ethiopic date");
12 changes: 12 additions & 0 deletions src/ethiopic/lib/addDays.ts
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/addMonths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("addMonths should correctly add months to an Ethiopic date");
16 changes: 16 additions & 0 deletions src/ethiopic/lib/addMonths.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/addWeeks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("addWeeks should correctly add weeks to an Ethiopic date");
12 changes: 12 additions & 0 deletions src/ethiopic/lib/addWeeks.ts
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/addYears.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("addYears should correctly add years to an Ethiopic date");
20 changes: 20 additions & 0 deletions src/ethiopic/lib/addYears.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
3 changes: 3 additions & 0 deletions src/ethiopic/lib/differenceInCalendarDays.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test.todo(
"differenceInCalendarDays should correctly calculate the number of calendar days between two dates"
);
23 changes: 23 additions & 0 deletions src/ethiopic/lib/differenceInCalendarDays.ts
Original file line number Diff line number Diff line change
@@ -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
);
}
25 changes: 25 additions & 0 deletions src/ethiopic/lib/differenceInCalendarMonths.ts
Original file line number Diff line number Diff line change
@@ -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
);
}
3 changes: 3 additions & 0 deletions src/ethiopic/lib/eachMonthOfInterval.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test.todo(
"should return an array of dates representing the start of each month in the interval"
);
38 changes: 38 additions & 0 deletions src/ethiopic/lib/eachMonthOfInterval.ts
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/endOfMonth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("should return the correct end of the month date for a given date");
17 changes: 17 additions & 0 deletions src/ethiopic/lib/endOfMonth.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/endOfWeek.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("should return the correct end of the week date for a given date");
20 changes: 20 additions & 0 deletions src/ethiopic/lib/endOfWeek.ts
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/endOfYear.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("should return the correct end of the year date for a given date");
17 changes: 17 additions & 0 deletions src/ethiopic/lib/endOfYear.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/getMonth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("should return the correct zero-based month index for a given date");
12 changes: 12 additions & 0 deletions src/ethiopic/lib/getMonth.ts
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions src/ethiopic/lib/getWeek.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test.todo("should return the correct week number for a given date");
Loading
Loading