Skip to content

Commit

Permalink
feat: new hijri/islamic calendar system available.
Browse files Browse the repository at this point in the history
fix: locale releated bugs fixed and perf improved
  • Loading branch information
amirhmoradi committed May 23, 2023
1 parent 3eb585f commit 5a14c21
Show file tree
Hide file tree
Showing 21 changed files with 668 additions and 251 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ With the `@calidy/dayjs-calendarsystems` plugin, we bring the capacity to run an
- Convert between different calendar systems.
- Default Gregorian calendar system included.
- Persian Calendar system available.
- **[WIP]** Islamic (Hijri, Umalqura) Calendar system. Note: we will use the default "islamic-umalqura" calendar system for "islamic" calendar system.
- Islamic (Hijri, Umalqura) Calendar system. Note: we will use the default "islamic-umalqura" calendar system for "islamic" calendar system.
- **[WIP]** Hebrew (Jewish) Calendar system.
- **[WIP]** Ethiopian Calendar system.
- **[TODO]** Parse date strings from different calendar systems
Expand Down
87 changes: 50 additions & 37 deletions src/calendarSystems/CalendarSystemBase.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,63 @@
/**
* Calendar System Base Class.
* Calendar System Base Class.
* @file CalendarSystemBase.js
* @project dayjs-calendarsystems
* @license see LICENSE file included in the project
* @author Calidy.com, Amir Moradi (https://calidy.com/)
* @description see README.md file included in the project
*
*
*/

// Possible calendars based on the Intl API:
// "buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic", "gregory", "hebrew",
// "indian", "islamic", "islamic-umalqura", "islamic-tbla", "islamic-civil", "islamic-rgsa",
// Possible calendars based on the Intl API:
// "buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic", "gregory", "hebrew",
// "indian", "islamic", "islamic-umalqura", "islamic-tbla", "islamic-civil", "islamic-rgsa",
// "iso8601", "japanese", "persian", "roc", "islamicc".

export default class CalendarSystemBase {
static typeName = 'CalendarSystemBase';

constructor(locale = 'en') {
this.locale = locale;
static typeName = "CalendarSystemBase";

constructor(locale = "en") {
this.locale = locale;
}

convertFromGregorian(date) {
throw new Error(
"Method convertFromGregorian must be implemented by subclass"
);
}

convertToGregorian(date) {
throw new Error(
"Method convertToGregorian must be implemented by subclass"
);
}

convertToJulian(date) {
throw new Error("Method convertToJulian must be implemented by subclass");
}

monthNames(locale, calendar, firstMonthName) {
throw new Error("Method monthNames must be implemented by subclass");
}

getLocalizedMonthName(monthIndex) {
const monthNames = this.monthNames();
if (monthIndex < 0 || monthIndex >= monthNames.length) {
throw new Error("Invalid month index.");
}

convertFromGregorian(date) {
throw new Error('Method convertFromGregorian must be implemented by subclass');
}

convertToGregorian(date) {
throw new Error('Method convertToGregorian must be implemented by subclass');
}

monthNames(locale) {
throw new Error('Method monthNames must be implemented by subclass');
}

getLocalizedMonthName(monthIndex) {
const monthNames = this.monthNames();
if (monthIndex < 0 || monthIndex >= monthNames.length) {
throw new Error('Invalid month index.');
}

return new Intl.DateTimeFormat(this.locale, { month: 'long' }).format(new Date(2022, monthIndex));
}

localeOverride(locale) {
return {
months: this.monthNames(locale),
monthsShort: this.monthNames(locale).map(month => month.substring(0, 3))
}
}
}
return new Intl.DateTimeFormat(this.locale, { month: "long" }).format(
new Date(2022, monthIndex)
);
}

localeOverride(locale) {
return {
gregorianMonths: this.monthNames(locale, "gregory", "January"),
months: this.monthNames(locale),
monthsShort: this.monthNames(locale).map((month) =>
month.substring(0, 3)
),
};
}
}
105 changes: 65 additions & 40 deletions src/calendarSystems/GregoryCalendarSystem.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,80 @@
/**
* Gregorian Calendar System
*
*
* @file GregoryCalendarSystem.js
* @project dayjs-calendarsystems
* @license see LICENSE file included in the project
* @author Calidy.com, Amir Moradi (https://calidy.com/)
* @description see README.md file included in the project
*
*
*/

import CalendarSystemBase from './CalendarSystemBase';
import CalendarSystemBase from "./CalendarSystemBase";
import { generateMonthNames } from "../calendarUtils/IntlUtils";

export default class GregoryCalendarSystem extends CalendarSystemBase {
convertFromGregorian(date) {
// extract year, month, day from date.
// date can be of type Date, Dayjs or object.
// if date is object, it should have year, month and day properties.
// if date is Dayjs, it should have $y, $M and $D properties.
// if date is Date, it should have getFullYear(), getMonth() and getDate() methods.
// if date is string, it should be in ISO format.
// if date is number, it should be in milliseconds.
// if date is undefined, it should be now.
// if date is null, it should be now.
if (date === undefined || date === null) {
date = new Date();
} else if (typeof date === 'string') {
date = new Date(date);
} else if (typeof date === 'number') {
date = new Date(date);
} else if (date instanceof Date) {
// do nothing
} else if (date.$y !== undefined && date.$M !== undefined && date.$D !== undefined) {
date = new Date(date.$y, date.$M, date.$D);
} else if (date.year !== undefined && date.month !== undefined && date.day !== undefined) {
date = new Date(date.year, date.month, date.day);
} else {
throw new Error('Invalid date');
}
convertToJulian(calendarYear, calendarMonth, calendarDay) {
// calendarMonth = calendarMonth+1 because the *_to_jd function month is 1-based
return CalendarUtils.gregorian_to_jd(
calendarYear,
calendarMonth + 1,
calendarDay
);
}

return date;
convertFromGregorian(date) {
// extract year, month, day from date.
// date can be of type Date, Dayjs or object.
// if date is object, it should have year, month and day properties.
// if date is Dayjs, it should have $y, $M and $D properties.
// if date is Date, it should have getFullYear(), getMonth() and getDate() methods.
// if date is string, it should be in ISO format.
// if date is number, it should be in milliseconds.
// if date is undefined, it should be now.
// if date is null, it should be now.
if (date === undefined || date === null) {
date = new Date();
} else if (typeof date === "string") {
date = new Date(date);
} else if (typeof date === "number") {
date = new Date(date);
} else if (date instanceof Date) {
// do nothing
} else if (
date.$y !== undefined &&
date.$M !== undefined &&
date.$D !== undefined
) {
date = new Date(date.$y, date.$M, date.$D);
} else if (
date.year !== undefined &&
date.month !== undefined &&
date.day !== undefined
) {
date = new Date(date.year, date.month, date.day);
} else {
throw new Error("Invalid date");
}

convertToGregorian(year, month, day) {
return {
year: year,
month: month,
day: day
};
}
return date;
}

monthNames() {
return Array.from({ length: 12 }, (_, i) => new Intl.DateTimeFormat('en-US', { month: 'long' }).format(new Date(1970, i)));
}
}
convertToGregorian(year, month, day) {
return {
year: year,
month: month,
day: day,
};
}

monthNames(locale = "en", calendar = "gregory", firstMonthName = "January") {
return generateMonthNames(locale, calendar, firstMonthName);
}
// monthNames() {
// return Array.from({ length: 12 }, (_, i) =>
// new Intl.DateTimeFormat("en-US", { month: "long" }).format(
// new Date(1970, i)
// )
// );
// }
}
105 changes: 105 additions & 0 deletions src/calendarSystems/HijriCalendarSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Hijri Calendar System
*
* @file HijriCalendarSystem.js
* @project dayjs-calendarsystems
* @license see LICENSE file included in the project
* @author Calidy.com, Amir Moradi (https://calidy.com/)
* @description see README.md file included in the project
*
*/

import CalendarSystemBase from "./CalendarSystemBase";
import * as CalendarUtils from "../calendarUtils/fourmilabCalendar";
import { generateMonthNames } from "../calendarUtils/IntlUtils";

export default class HijriCalendarSystem extends CalendarSystemBase {
constructor(locale = "en") {
super();
this.firstDayOfWeek = 6; // Saturday
this.locale = locale;
this.monthNamesLocalized = generateMonthNames(
locale,
"islamic-umalqura",
"Muharram"
);
}

convertToJulian(calendarYear, calendarMonth, calendarDay) {
// calendarMonth = calendarMonth+1 because the *_to_jd function month is 1-based
return CalendarUtils.islamic_to_jd(
calendarYear,
calendarMonth + 1,
calendarDay
);
}

convertFromGregorian(date) {
// extract year, month, day from date.
// date can be of type Date, Dayjs or object.
// if date is object, it should have year, month and day properties.
// if date is Dayjs, it should have $y, $M and $D properties.
// if date is Date, it should have getFullYear(), getMonth() and getDate() methods.
// if date is string, it should be in ISO format.
// if date is number, it should be in milliseconds.
// if date is undefined, it should be now.
// if date is null, it should be now.
if (date === undefined || date === null) {
date = new Date();
} else if (typeof date === "string") {
date = new Date(date);
} else if (typeof date === "number") {
date = new Date(date);
} else if (date instanceof Date) {
// do nothing
} else if (
date.$y !== undefined &&
date.$M !== undefined &&
date.$D !== undefined
) {
date = new Date(date.$y, date.$M, date.$D);
} else if (
date.year !== undefined &&
date.month !== undefined &&
date.day !== undefined
) {
date = new Date(date.year, date.month, date.day);
} else {
throw new Error("Invalid date");
}

const julianDay = CalendarUtils.gregorian_to_jd(
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
);
const convertedDateArray = CalendarUtils.jd_to_islamic(julianDay);
return {
year: convertedDateArray[0],
month: convertedDateArray[1] - 1, // -1 because the Persian month is 0-based
day: convertedDateArray[2],
};
}

// Expects a zero-based month index
convertToGregorian(calendarYear, calendarMonth, calendarDay) {
const julianDay = this.convertToJulian(
calendarYear,
calendarMonth,
calendarDay
);
const gregorianDateArray = CalendarUtils.jd_to_gregorian(julianDay);
return {
year: gregorianDateArray[0],
month: gregorianDateArray[1] - 1, // -1 because the Gregorian month is 0-based
day: gregorianDateArray[2],
};
}

monthNames(locale = "en", calendar = "islamic-umalqura", firstMonthName = "Muharram") {
return generateMonthNames(locale, calendar, firstMonthName);
}
getLocalizedMonthName(monthIndex) {
return this.monthNamesLocalized[monthIndex];
}
}
Loading

0 comments on commit 5a14c21

Please sign in to comment.