Skip to content

Commit 34b4e5e

Browse files
author
wikus
committed
fix: amazigh calendar and tests are stable now.
1 parent 567a641 commit 34b4e5e

13 files changed

+430
-173
lines changed

Diff for: README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616

1717
Day.js Calendar Systems Plugin extends Day.js library to allow the use of different non-gregorian calendar systems like:
18+
1819
* Persian (a.k.a.: Jalaali, Shamsi, Khorshidi),
1920
* Arabic (a.k.a: Hijri, Islamic, Umalqura, Ghamari),
2021
* Hebrew (a.k.a: Jewish),
@@ -41,7 +42,7 @@ With the `@calidy/dayjs-calendarsystems` plugin, we bring the capacity to run an
4142
- 🌍 🗓️ 🇮🇷 Persian Calendar system available.
4243
- 🌍 🗓️ 🇸🇦 Islamic (Hijri, Umalqura) Calendar system. Note: we will use the default "islamic-umalqura" calendar system for "islamic" calendar system.
4344
- 🌍 🗓️ 🇮🇱 Hebrew (Jewish) Calendar system.
44-
- 🌍 🗓️ ⵣ **[Need more testing]** Amazigh (Berber) Calendar system.
45+
- 🌍 🗓️ ⵣ Amazigh (Berber) Calendar system.
4546
- 🌍 🗓️ 🇪🇹 **[WIP]** Ethiopian Calendar system.
4647
- 🌍 🗓️ 🇮🇳 **[TODO]** Indian Calendar system.
4748
- 🌍 🗓️ 🇨🇳 **[TODO]** Chinese Calendar system.

Diff for: src/calendarSystems/AmazighCalendarSystem.js

+200-101
Original file line numberDiff line numberDiff line change
@@ -27,140 +27,239 @@ export default class AmazighCalendarSystem extends CalendarSystemBase {
2727
);
2828
}
2929

30+
// Returns a zero-based month index
3031
/**
3132
* Converts a Julian Day Number to an Amazigh date.
3233
* @param {number} jdn - The Julian Day Number.
3334
* @returns {Object} An object containing the Amazigh year, month, and day.
3435
*/
35-
convertFromJulian(jdn) {
36-
// Constants for JDN of the Julian calendar start and the Amazigh calendar start year
37-
const JDN_JULIAN_START = 2299160.5; // October 15, 1582, Gregorian calendar start (end of Julian calendar)
38-
const AMZ_YEAR_START = 950; // Amazigh calendar start year in BC
39-
const DAYS_IN_YEAR = 365.25; // Average days in a year accounting for leap years in Julian calendar
40-
const GREGORIAN_START_YEAR = 1582; // Year the Gregorian calendar starts
41-
const YENNAYER_JDN_OFFSET = 13; // Offset for Yennayer in the Gregorian calendar as of the 21st century
42-
43-
// Calculate the Gregorian year for the given JDN
44-
let year = GREGORIAN_START_YEAR + Math.floor((jdn - JDN_JULIAN_START) / DAYS_IN_YEAR);
45-
// Adjust the year based on the Amazigh calendar start year
46-
let amazighYear = year + (AMZ_YEAR_START - (year < 0 ? 1 : 0)); // Adjust for no year 0 in historical counting
47-
48-
// Calculate the JDN for January 1st of the given year
49-
let jdnJan1 = jdn - ((jdn - JDN_JULIAN_START) % DAYS_IN_YEAR);
50-
// Calculate the day of the year from JDN
51-
let dayOfYear = jdn - jdnJan1 + 1; // +1 since January 1st is day 1
52-
53-
// Adjust dayOfYear based on the Yennayer offset
54-
dayOfYear -= YENNAYER_JDN_OFFSET;
55-
56-
// Correct the year and dayOfYear if the adjustment crosses into the previous year
57-
if (dayOfYear <= 0) {
58-
amazighYear -= 1;
59-
dayOfYear += DAYS_IN_YEAR; // Add the days in a year to the negative dayOfYear
60-
}
36+
convertFromJulian(julianDayNumber) {
37+
// The Julian Day starts at noon, not at midnight.
38+
julianDayNumber = julianDayNumber + 0.5;
39+
const [gy, gm, gd] = CalendarUtils.jd_to_gregorian(julianDayNumber);
40+
const amazighDate = this.adjustForYennayer({
41+
year: gy,
42+
month: gm - 1, // -1 because the jd_to_gregorian returns 1-based months but we need 0-based months for adjustForYennayer
43+
day: gd,
44+
});
45+
return new Array(amazighDate.year, amazighDate.month, amazighDate.day);
46+
}
6147

62-
// Determine the month and day from dayOfYear
63-
let month = 0, day = dayOfYear;
64-
const daysInMonths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // Days in each month for Julian calendar
65-
while (day > daysInMonths[month]) {
66-
day -= daysInMonths[month];
67-
month += 1;
68-
}
48+
// Expects a zero-based month index
49+
// The Julian Day starts at noon, not at midnight.
50+
// So, when you convert a Gregorian date to a Julian Day number,
51+
// the result is the Julian Day number for the noon of that day.
52+
// If the time of the date is noon or later, the Julian Day number will be for the next day.
53+
convertToJulian(
54+
calendarYear,
55+
calendarMonth,
56+
calendarDay,
57+
hour = 0,
58+
minute = 0,
59+
second = 0
60+
) {
61+
// Convert Amazigh year to Gregorian year
62+
const gregorianYear = calendarYear - 950; // Amazigh calendar starts in 950 BC in the Gregorian calendar
6963

70-
// Adjust for the Amazigh calendar specifics if necessary
71-
// Note: This example uses a simplified approach and might need adjustments for leap years and accurate month lengths
64+
// Initial conversion without adjusting for Yennayer
65+
let gregorianDate = {
66+
year: gregorianYear,
67+
month: calendarMonth,
68+
day: calendarDay,
69+
};
7270

73-
return [
74-
amazighYear,
75-
month + 1, // +1 to make the month 1-based
76-
day
77-
];
78-
}
71+
const yennayerGregorianDate = new Date(gregorianYear, 0, 14); // January is month 0 in JavaScript Date
7972

73+
// Calculate the Julian Day Number (JDN) for Yennayer
74+
let julianDayYennayer = CalendarUtils.gregorian_to_jd(
75+
yennayerGregorianDate.getFullYear(),
76+
yennayerGregorianDate.getMonth() + 1,
77+
yennayerGregorianDate.getDate()
78+
);
8079

81-
// Convert Amazigh date to Julian Day
82-
convertToJulian(calendarYear, calendarMonth, calendarDay) {
83-
// Convert Amazigh year to Gregorian year
84-
const gregorianYear = calendarYear + 950;
85-
// Adjusting for Yennayer starting on January 14th in the Gregorian calendar
86-
const isBeforeYennayer = calendarMonth === 0 && calendarDay < 14;
87-
const adjustedYear = gregorianYear - (isBeforeYennayer ? 1 : 0);
88-
const adjustedMonth = isBeforeYennayer ? 12 : calendarMonth + 1; // Adjust month to 1-based for calculation
89-
const adjustedDay = calendarDay + (isBeforeYennayer ? 18 : 13); // Adjust days for Yennayer start, considering the current 13-day discrepancy
90-
91-
// Convert adjusted Gregorian date to Julian Day
92-
return CalendarUtils.gregorian_to_jd(adjustedYear, adjustedMonth, adjustedDay);
93-
}
80+
// Step 3: Calculate the total number of days from Yennayer to the given Amazigh date
81+
// Adjust month and day based on dayOfYear
82+
const daysInMonths = [
83+
31,
84+
calendarYear % 4 === 0 ? 29 : 28,
85+
31,
86+
30,
87+
31,
88+
30,
89+
31,
90+
31,
91+
30,
92+
31,
93+
30,
94+
31,
95+
]; // Considering leap years
96+
let daysSinceYennayer = 0;
97+
for (let month = 0; month < calendarMonth; month++) {
98+
// Assuming 30 days per month for simplicity; adjust based on the actual Amazigh calendar if necessary
99+
daysSinceYennayer += daysInMonths[month];
100+
}
94101

102+
daysSinceYennayer += calendarDay - 1; // Subtract 1 since Yennayer is day 1
103+
104+
// Calculate the final Julian Day Number (JDN) by adding the days since Yennayer to the JDN of Yennayer
105+
let finalJdn = julianDayYennayer + daysSinceYennayer
106+
// Adjust for the time of day
107+
+ Math.floor(second + 60 * (minute + 60 * hour) + 0.5) / 86400.0
108+
109+
return finalJdn;
110+
}
95111

96112
// Convert from Gregorian date to Amazigh date
113+
// Returns a zero-based month index
114+
// Expects a zero-based month index
97115
convertFromGregorian(date) {
98-
const julianDay = CalendarUtils.gregorian_to_jd(date.getFullYear(), date.getMonth() + 1, date.getDate());
99-
const gregorianYear = date.getFullYear();
100-
const gregorianMonth = date.getMonth() + 1; // 1-based month
101-
const gregorianDay = date.getDate();
102-
103-
// Calculate the Amazigh year
104-
let amazighYear = gregorianYear - 950;
105-
if (gregorianMonth < 1 || (gregorianMonth === 1 && gregorianDay < 14)) {
106-
amazighYear -= 1; // Adjust for Yennayer
107-
}
108-
109-
// Convert Julian day back to Gregorian to adjust for Yennayer offset
110-
const { year, month, day } = CalendarUtils.jd_to_gregorian(julianDay - 13);
116+
date = this.validateDate(date);
111117

118+
const julianDay =
119+
CalendarUtils.gregorian_to_jd(
120+
date.getFullYear(),
121+
date.getMonth() + 1,
122+
date.getDate()
123+
) +
124+
Math.floor(
125+
date.getSeconds() +
126+
60 * (date.getMinutes() + 60 * date.getHours()) +
127+
0.5
128+
) /
129+
86400.0 -
130+
0.5;
131+
const convertedDateArray = this.convertFromJulian(julianDay);
112132
return {
113-
year: year - 950,
114-
month: month - 1, // Convert to 0-based month index
115-
day: day,
133+
year: convertedDateArray[0],
134+
month: convertedDateArray[1] - 1, // -1 because the month is 0-based
135+
day: convertedDateArray[2],
116136
};
117137
}
118138

119-
convertToGregorian(calendarYear, calendarMonth, calendarDay) {
120-
// Calculate the Gregorian year for the given Amazigh year.
121-
const baseYear = -950; // Starting point of the Amazigh calendar in the Gregorian calendar (950 BC).
122-
let gregorianYear = calendarYear + baseYear;
123-
124-
// Adjust for the current discrepancy between the Julian and Gregorian calendars.
125-
const discrepancyDays = 13; // As of the 21st century, there's a 13-day difference between the calendars.
126-
const yennayerGregorianDate = new Date(gregorianYear, 0, 14 + discrepancyDays); // January 14th + discrepancy in days.
127-
128-
// Calculate the Julian Day Number for Yennayer of the given Gregorian year.
129-
let julianDayYennayer = this.convertToJulian(yennayerGregorianDate.getFullYear(), yennayerGregorianDate.getMonth(), yennayerGregorianDate.getDate());
130-
131-
// Considering the Amazigh calendar follows the Julian calendar with months having the same length,
132-
// we calculate the total number of days since Yennayer to the Amazigh date.
133-
let daysSinceYennayer = 0;
134-
for (let month = 0; month < calendarMonth; month++) {
135-
daysSinceYennayer += month === 1 ? 28 : (month < 7 ? (month % 2 === 0 ? 31 : 30) : (month % 2 === 0 ? 30 : 31));
136-
}
137-
daysSinceYennayer += calendarDay - 1; // Subtract one since Yennayer is considered day 1.
138-
139-
// Calculate the total Julian Day and convert it back to a Gregorian date.
140-
let julianDay = julianDayYennayer + daysSinceYennayer;
139+
// Returns a zero-based month index
140+
// Expects a zero-based month index
141+
convertToGregorian(
142+
calendarYear,
143+
calendarMonth,
144+
calendarDay,
145+
hour = 0,
146+
minute = 0,
147+
second = 0,
148+
millisecond = 0
149+
) {
150+
const julianDay = this.convertToJulian(
151+
calendarYear,
152+
calendarMonth,
153+
calendarDay,
154+
hour,
155+
minute,
156+
second,
157+
millisecond
158+
);
141159
const gregorianDateArray = CalendarUtils.jd_to_gregorian(julianDay);
142160
return {
143161
year: gregorianDateArray[0],
144162
month: gregorianDateArray[1] - 1, // -1 because the Gregorian month is 0-based
145163
day: gregorianDateArray[2],
146164
};
147-
}
165+
}
148166

149-
isLeapYear(year) {
167+
isLeapYear(year = null) {
168+
if (year === null) {
169+
year = this.$y;
170+
}
150171
// Adjust if Amazigh leap year rules differ, using Gregorian as placeholder
151172
const adjustedYear = year + 950;
152-
return (adjustedYear % 4 === 0 && adjustedYear % 100 !== 0) || adjustedYear % 400 === 0;
173+
return (
174+
(adjustedYear % 4 === 0 && adjustedYear % 100 !== 0) ||
175+
adjustedYear % 400 === 0
176+
);
153177
}
154-
monthNames(
155-
locale = "en",
156-
calendar = "amazigh",
157-
firstMonthName = "Yennayer"
158-
) {
178+
179+
monthNames(locale = "en", calendar = "amazigh", firstMonthName = "Yennayer") {
159180
return generateMonthNames(locale, calendar, firstMonthName);
160181
}
161182

162183
getLocalizedMonthName(monthIndex) {
163184
return this.monthNamesLocalized[monthIndex];
164185
}
165-
}
166186

187+
gregorianToAmazighYear(gregorianYear) {
188+
// The Amazigh year 2974 corresponds to Gregorian year 2024
189+
const referenceAmazighYear = 2974;
190+
const referenceGregorianYear = 2024;
191+
const yearDifference = gregorianYear - referenceGregorianYear;
192+
return referenceAmazighYear + yearDifference;
193+
}
194+
195+
// Returns a zero-based month index
196+
// Expects a zero-based month index
197+
adjustForYennayer(gregorianDate) {
198+
// Constants for the Amazigh New Year in the Gregorian calendar
199+
const yennayerMonth = 0; // January, zero-based index
200+
const yennayerDay = 14;
201+
202+
// Convert the Gregorian year to the Amazigh year
203+
let amazighYear = this.gregorianToAmazighYear(gregorianDate.year);
204+
205+
// Check if the Gregorian date is before Yennayer and adjust the Amazigh year accordingly
206+
if (
207+
gregorianDate.month < yennayerMonth ||
208+
(gregorianDate.month === yennayerMonth && gregorianDate.day < yennayerDay)
209+
) {
210+
amazighYear -= 1; // The date is in the previous Amazigh year
211+
}
212+
213+
// Calculate the Julian Day Number for the given Gregorian date and for Yennayer
214+
const jdnForGregorianDate = CalendarUtils.gregorian_to_jd(
215+
gregorianDate.year,
216+
gregorianDate.month + 1,
217+
gregorianDate.day
218+
);
219+
const jdnForYennayerThisYear = CalendarUtils.gregorian_to_jd(
220+
gregorianDate.year,
221+
yennayerMonth + 1,
222+
yennayerDay
223+
);
224+
225+
// Determine if we need to use Yennayer from the previous Gregorian year for the calculation
226+
const usePreviousYearYennayer =
227+
gregorianDate.month < yennayerMonth ||
228+
(gregorianDate.month === yennayerMonth &&
229+
gregorianDate.day < yennayerDay);
230+
const jdnForYennayer = usePreviousYearYennayer
231+
? CalendarUtils.gregorian_to_jd(
232+
gregorianDate.year - 1,
233+
yennayerMonth + 1,
234+
yennayerDay
235+
)
236+
: jdnForYennayerThisYear;
237+
238+
// Calculate the day of the year in the Amazigh calendar
239+
let dayOfYear = jdnForGregorianDate - jdnForYennayer + 1; // +1 because Yennayer is day 1
240+
241+
// Determine the Amazigh month and day from the day of the year
242+
const daysInAmazighMonths = [
243+
31,
244+
amazighYear % 4 === 0 ? 29 : 28,
245+
31,
246+
30,
247+
31,
248+
30,
249+
31,
250+
31,
251+
30,
252+
31,
253+
30,
254+
31,
255+
]; // Considering leap years
256+
let month = 0;
257+
while (dayOfYear > daysInAmazighMonths[month]) {
258+
dayOfYear -= daysInAmazighMonths[month];
259+
month++;
260+
}
261+
262+
// Adjust month to be zero-based and ensure dayOfYear is correctly calculated
263+
return { year: amazighYear, month: month + 1, day: dayOfYear };
264+
}
265+
}

0 commit comments

Comments
 (0)