Skip to content

Commit

Permalink
feat: code optimization, cleanup and more functions. Added a WIP for …
Browse files Browse the repository at this point in the history
…Ethiopian calendar, still not stable. Fixed timezone and utc calculations
  • Loading branch information
amirhmoradi committed Jun 21, 2023
1 parent a6eaecd commit 731d5c5
Show file tree
Hide file tree
Showing 13 changed files with 554 additions and 281 deletions.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "yarn" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
119 changes: 63 additions & 56 deletions dev/__wip__/overwrites.dayjs.function.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,70 @@ dayjsClass.prototype.tz = function (timezone = defaultTimezone, keepLocalTime) {
return ins
}
dayjsClass.prototype.utcOffset = function (input, keepLocalTime) {
const { u } = this.$utils()
if (u(input)) {
if (this.$u) {
return 0
}
if (!u(this.$offset)) {
return this.$offset
}
return -Math.round(this.$d.getTimezoneOffset() / 15) * 15;
}
// dayjsClass.prototype.OFF_utcOffset = function (input, keepLocalTime) {
// const { u } = this.$utils()
// if (u(input)) {
// if (this.$u) {
// return 0
// }
// if (!u(this.$offset)) {
// return this.$offset
// }
// return -Math.round(this.$d.getTimezoneOffset() / 15) * 15;
// }
if (typeof input === 'string') {
input = offsetFromString(input)
if (input === null) {
return this
}
}
const offset = Math.abs(input) <= 16 ? input * 60 : input
let ins = this.clone();
if (keepLocalTime) {
ins.$offset = offset
ins.$u = input === 0
return ins
}
if (input !== 0) {
const localTimezoneOffset = this.$u
? this.toDate().getTimezoneOffset() : -1 * this.utcOffset()
// if (typeof input === 'string') {
// input = offsetFromString(input)
// if (input === null) {
// return this
// }
// }
// const offset = Math.abs(input) <= 16 ? input * 60 : input
// let ins = this.clone();
// if (keepLocalTime) {
// ins.$offset = offset
// ins.$u = input === 0
// return ins
// }
// if (input !== 0) {
// const localTimezoneOffset = this.$u
// ? this.toDate().getTimezoneOffset() : -1 * this.utcOffset()
ins = this.local().add(offset + localTimezoneOffset, MIN)
ins.$offset = offset
ins.$x.$localOffset = localTimezoneOffset
} else {
ins = this.utc()
}
return ins
}
// ins = this.local().add(offset + localTimezoneOffset, MIN)
// ins.$offset = offset
// ins.$x.$localOffset = localTimezoneOffset
// } else {
// ins = this.utc()
// }
// return ins
// }
dayjsClass.prototype.utc = function (keepLocalTime) {
let y = this.$y;
let m = this.$M;
let d = this.$D;
// if calendar system is not gregorian, convert the date to gregorian
if ("$C" in this && this.$C !== "gregory") {
const convertedDate = calendarSystems[this.$C].convertToGregorian(
this.$y,
this.$M,
this.$D
);
y = convertedDate.year;
m = convertedDate.month;
d = convertedDate.day;
}
const ins = wrapper(Date.UTC(y, m, d), { ...this, locale: this.$L, utc: true, $u: true })
if (keepLocalTime) {
return ins.add(this.utcOffset(), "minute")
}
return ins
}
// dayjsClass.prototype.OFF_utc = function (keepLocalTime) {
// let y = this.$y;
// let m = this.$M;
// let d = this.$D;
// // if calendar system is not gregorian, convert the date to gregorian
// if ("$C" in this && this.$C !== "gregory") {
// const convertedDate = calendarSystems[this.$C].convertToGregorian(
// this.$y,
// this.$M,
// this.$D,
// this.$H,
// this.$m,
// this.$s,
// this.$ms
// );
// y = convertedDate.year;
// m = convertedDate.month;
// d = convertedDate.day;
// }
// //const ins = wrapper(Date.UTC(y, m, d), { ...this, locale: this.$L, utc: true });
// const date = this.format("YYYY-MM-DDTHH:mm:ss.SSS");
// const ins = wrapper(date, { ...this, locale: this.$L, utc: true });
// if (keepLocalTime) {
// return ins.add(this.utcOffset(), "minute")
// }
// return ins
// }
*/
56 changes: 56 additions & 0 deletions src/calendarSystems/CalendarSystemBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,17 @@ export default class CalendarSystemBase {
);
}

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

// Expects a zero-based month index
// Retrieve the Julian date equivalent for this date,
// i.e. days since January 1, 4713 BCE Greenwich noon.
// The Julian Day starts at noon, not at midnight.
// So, when you convert a Gregorian date to a Julian Day number,
// the result is the Julian Day number for the noon of that day.
// If the time of the date is noon or later, the Julian Day number will be for the next day.
convertToJulian(date) {
throw new Error("Method convertToJulian must be implemented by subclass");
}
Expand Down Expand Up @@ -73,4 +83,50 @@ export default class CalendarSystemBase {
),
};
}

validateDate(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,
date.$H,
date.$m,
date.$s,
date.$ms
);
} 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");
}

return date;
}

}
142 changes: 142 additions & 0 deletions src/calendarSystems/EthiopianCalendarSystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* Ethiopian Calendar System
*
* @file EthiopianCalendarSystem.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 EthiopianCalendarSystem extends CalendarSystemBase {
constructor(locale = "en") {
super();
this.firstDayOfWeek = 0; // Sunday
this.locale = locale;
// Julian date of start of Ethiopian epoch: 27 August 8 CE (Gregorian).
this.julianDayEpoch = 1724220.5;
this.intlCalendar = "ethiopic";
this.firstMonthNameEnglish = "Meskerem";
this.monthNamesLocalized = generateMonthNames(
locale,
"ethiopic",
"Meskerem"
);
}

convertFromJulian(julianDayNumber) {
// Calculate the number of days since the Ethiopian epoch
const days = Math.floor(julianDayNumber) + 0.5 - this.julianDayEpoch;
// Calculate the Ethiopian year
const year = Math.floor((days - Math.floor((days + 366) / 1461)) / 365) + 1;
// Calculate the day of the year (1-366)
const dayOfYear = days - (year - 1) * 365 - Math.floor((year - 1) / 4);
// Calculate the Ethiopian month (1-13)
const month = dayOfYear > 330 ? 13 : Math.floor((dayOfYear - 1) / 30) + 1;
// Calculate the day of the month (1-30 for months 1-12, 1-5 or 1-6 for month 13)
const day = Math.floor(dayOfYear - (month - 1) * 30) + 1;
// Return the Ethiopian date
return [year, month, day];
}

// Expects a zero-based month index
// The Julian Day starts at noon, not at midnight.
// So, when you convert a Gregorian date to a Julian Day number,
// the result is the Julian Day number for the noon of that day.
// If the time of the date is noon or later, the Julian Day number will be for the next day.
convertToJulian(
calendarYear,
calendarMonth,
calendarDay,
hour = 0,
minute = 0,
second = 0
) {
// Calculate the Julian Day number for the start of this Ethiopian year
const yearStart =
(calendarYear - 1) * 365 +
Math.floor(calendarYear / 4) +
this.julianDayEpoch;
// Calculate the Julian Day number for the start of this Ethiopian month
const monthStart = yearStart + calendarMonth * 30;
// Calculate the Julian Day number for this Ethiopian day
const dayStart = monthStart + calendarDay - 1;
// Adjust for the time of day
const time = (second + 60 * (minute + 60 * hour)) / 86400.0;
// Return the total Julian Day number
return dayStart + time;
}

convertFromGregorian(date) {
date = this.validateDate(date);

const julianDay =
CalendarUtils.gregorian_to_jd(
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
) +
Math.floor(
date.getSeconds() +
60 * (date.getMinutes() + 60 * date.getHours()) +
0.5
) /
86400.0 -
0.5;
const convertedDateArray = this.convertFromJulian(julianDay);
return {
year: convertedDateArray[0],
month: convertedDateArray[1] - 1, // -1 because the month is 0-based
day: convertedDateArray[2],
};
}

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

isLeapYear() {
this.$y = this.$y + (this.$y < 0 ? 1 : 0); // No year zero
return this.$y % 4 === 3 || this.$y % 4 === -1;
}

monthNames(
locale = "en",
calendar = "ethiopic",
firstMonthName = "Meskerem"
) {
return generateMonthNames(locale, calendar, firstMonthName);
}
getLocalizedMonthName(monthIndex) {
return this.monthNamesLocalized[monthIndex];
}
}
34 changes: 2 additions & 32 deletions src/calendarSystems/GregoryCalendarSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default class GregoryCalendarSystem extends CalendarSystemBase {
this.monthNamesLocalized = generateMonthNames(locale, "gregory", "January");
}

// Expects a zero-based month index
convertToJulian(calendarYear, calendarMonth, calendarDay) {
// calendarMonth = calendarMonth+1 because the *_to_jd function month is 1-based
return CalendarUtils.gregorian_to_jd(
Expand All @@ -32,38 +33,7 @@ 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");
}
date = this.validateDate(date);

return date;
}
Expand Down
Loading

0 comments on commit 731d5c5

Please sign in to comment.