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

Add ethiopic to the numerals #2664

Closed
wants to merge 5 commits into from
Closed
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");
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
@@ -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";
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
@@ -186,7 +196,8 @@
"dependencies": {
"@date-fns/tz": "^1.2.0",
"date-fns": "^4.1.0",
"date-fns-jalali": "^4.1.0-0"
"date-fns-jalali": "^4.1.0-0",
"ethiopian-date": "^0.0.6"
},
"devDependencies": {
"@jest/types": "^29.6.3",
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion react-day-picker.code-workspace
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 3 additions & 0 deletions src/ethiopic/lib/endOfISOWeek.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test.todo(
"should return the correct end of the ISO week date for a given date"
);
13 changes: 13 additions & 0 deletions src/ethiopic/lib/endOfISOWeek.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { addDays } from "./addDays.js";

/**
* End of ISO week
*
* @param {Date} date - The original date
* @returns {Date} The end of the ISO week
*/
export function endOfISOWeek(date: Date): Date {
const day = date.getDay();
const diff = (7 - day + 1) % 7; // ISO week ends on Sunday
return addDays(date, diff);
}
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");
Loading