mirror of
https://github.com/quantum5/qcal.git
synced 2025-07-27 03:34:11 -04:00
Compare commits
30 commits
8f3a6dde0f
...
7a3e62df17
Author | SHA1 | Date | |
---|---|---|---|
|
7a3e62df17 | ||
|
fb576a4a5e | ||
|
d0ddad26c5 | ||
|
4381415fa0 | ||
|
ebe627d5de | ||
|
7c302fa44e | ||
|
a8d230f478 | ||
|
fc6bc47f16 | ||
|
df24ecd8ec | ||
|
6e6a921475 | ||
|
5848fbd3f1 | ||
|
5cb6c2797f | ||
|
ded9fa4c64 | ||
|
60d0ce3549 | ||
|
8988b5aa8b | ||
|
e32b4bf774 | ||
|
eb4a7253f1 | ||
|
fcecc7ae1a | ||
|
88bf611c90 | ||
|
59fe324126 | ||
|
36484c19ab | ||
|
d0d2ebcac5 | ||
|
273e8b1d6a | ||
|
3c269ab7c6 | ||
|
a018c47408 | ||
|
73b4a92282 | ||
|
4f8d4f6cea | ||
|
4d24f1188e | ||
|
1c6802ecf7 | ||
|
8acb0b276a |
|
@ -4,5 +4,7 @@ This is **@quantum5**'s collection of calendar webapps, intend to cover all
|
|||
calendars from major cultures.
|
||||
|
||||
Currently, the following calendars are supported:
|
||||
* [French Republican Calendar](frcal/README.md)
|
||||
* [Julian Calendar](jcal/README.md)
|
||||
|
||||
* [French Republican Calendar](frcal/README.md) ([live version](https://frcal.qt.ax))
|
||||
* [Julian Calendar](jcal/README.md) ([live version](https://jcal.qt.ax))
|
||||
* [Gregorian Calendar](gcal/README.md) ([live version](https://gcal.qt.ax))
|
||||
|
|
|
@ -49,6 +49,8 @@ const monthNames: { [key in JulianMonth]: string } = {
|
|||
12: 'December',
|
||||
};
|
||||
|
||||
export type Weekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
|
||||
export const weekdayNames = [
|
||||
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
|
||||
];
|
||||
|
@ -91,6 +93,10 @@ export function monthName(month: JulianMonth): string {
|
|||
return monthNames[month];
|
||||
}
|
||||
|
||||
export function jdnWeekDay(jdn: number): Weekday {
|
||||
return (jdn % 7 + 8) % 7 as Weekday;
|
||||
}
|
||||
|
||||
export function gregorianMonthDays(year: number, month: JulianMonth, julian = false): 28 | 29 | 30 | 31 {
|
||||
switch (month) {
|
||||
case 1:
|
||||
|
|
234
common/src/mayan.test.ts
Normal file
234
common/src/mayan.test.ts
Normal file
|
@ -0,0 +1,234 @@
|
|||
import {
|
||||
formatHaab,
|
||||
formatLordOfNight,
|
||||
formatTzolkin,
|
||||
jdnHaab,
|
||||
jdnLordOfNight,
|
||||
jdnTzolkin,
|
||||
TzolkinName,
|
||||
tzolkinName,
|
||||
} from './mayan';
|
||||
|
||||
describe('tzolkinName', () => {
|
||||
it('should return correct name for IMIX', () => {
|
||||
expect(tzolkinName(TzolkinName.IMIX)).toBe('Imix');
|
||||
});
|
||||
|
||||
it('should return correct name for IK', () => {
|
||||
expect(tzolkinName(TzolkinName.IK)).toBe('Ikʼ');
|
||||
});
|
||||
|
||||
it('should return correct name for AJAW', () => {
|
||||
expect(tzolkinName(TzolkinName.AJAW)).toBe('Ajaw');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatTzolkin', () => {
|
||||
it('should format tzolkin with number 1 and IMIX', () => {
|
||||
expect(formatTzolkin({number: 1, name: TzolkinName.IMIX})).toBe('1 Imix');
|
||||
});
|
||||
|
||||
it('should format tzolkin with number 13 and AJAW', () => {
|
||||
expect(formatTzolkin({number: 13, name: TzolkinName.AJAW})).toBe('13 Ajaw');
|
||||
});
|
||||
|
||||
it('should format tzolkin with number 7 and KABAN', () => {
|
||||
expect(formatTzolkin({number: 7, name: TzolkinName.KABAN})).toBe('7 Kabʼan');
|
||||
});
|
||||
});
|
||||
|
||||
describe('jdnTzolkin', () => {
|
||||
it('converts sample consecutive dates in June 2025 correctly', () => {
|
||||
expect(jdnTzolkin(2460828)).toEqual({number: 12, name: TzolkinName.CHIKCHAN});
|
||||
expect(jdnTzolkin(2460829)).toEqual({number: 13, name: TzolkinName.KIMI});
|
||||
expect(jdnTzolkin(2460830)).toEqual({number: 1, name: TzolkinName.MANIK});
|
||||
expect(jdnTzolkin(2460831)).toEqual({number: 2, name: TzolkinName.LAMAT});
|
||||
expect(jdnTzolkin(2460832)).toEqual({number: 3, name: TzolkinName.MULUK});
|
||||
expect(jdnTzolkin(2460833)).toEqual({number: 4, name: TzolkinName.OK});
|
||||
expect(jdnTzolkin(2460834)).toEqual({number: 5, name: TzolkinName.CHUWEN});
|
||||
expect(jdnTzolkin(2460835)).toEqual({number: 6, name: TzolkinName.EB});
|
||||
expect(jdnTzolkin(2460836)).toEqual({number: 7, name: TzolkinName.BEN});
|
||||
expect(jdnTzolkin(2460837)).toEqual({number: 8, name: TzolkinName.IX});
|
||||
expect(jdnTzolkin(2460838)).toEqual({number: 9, name: TzolkinName.MEN});
|
||||
expect(jdnTzolkin(2460839)).toEqual({number: 10, name: TzolkinName.KIB});
|
||||
expect(jdnTzolkin(2460840)).toEqual({number: 11, name: TzolkinName.KABAN});
|
||||
expect(jdnTzolkin(2460841)).toEqual({number: 12, name: TzolkinName.ETZNAB});
|
||||
expect(jdnTzolkin(2460842)).toEqual({number: 13, name: TzolkinName.KAWAK});
|
||||
expect(jdnTzolkin(2460843)).toEqual({number: 1, name: TzolkinName.AJAW});
|
||||
expect(jdnTzolkin(2460844)).toEqual({number: 2, name: TzolkinName.IMIX});
|
||||
expect(jdnTzolkin(2460845)).toEqual({number: 3, name: TzolkinName.IK});
|
||||
expect(jdnTzolkin(2460846)).toEqual({number: 4, name: TzolkinName.AKBAL});
|
||||
expect(jdnTzolkin(2460847)).toEqual({number: 5, name: TzolkinName.KAN});
|
||||
expect(jdnTzolkin(2460848)).toEqual({number: 6, name: TzolkinName.CHIKCHAN});
|
||||
expect(jdnTzolkin(2460849)).toEqual({number: 7, name: TzolkinName.KIMI});
|
||||
expect(jdnTzolkin(2460850)).toEqual({number: 8, name: TzolkinName.MANIK});
|
||||
expect(jdnTzolkin(2460851)).toEqual({number: 9, name: TzolkinName.LAMAT});
|
||||
expect(jdnTzolkin(2460852)).toEqual({number: 10, name: TzolkinName.MULUK});
|
||||
expect(jdnTzolkin(2460853)).toEqual({number: 11, name: TzolkinName.OK});
|
||||
expect(jdnTzolkin(2460854)).toEqual({number: 12, name: TzolkinName.CHUWEN});
|
||||
expect(jdnTzolkin(2460855)).toEqual({number: 13, name: TzolkinName.EB});
|
||||
expect(jdnTzolkin(2460856)).toEqual({number: 1, name: TzolkinName.BEN});
|
||||
expect(jdnTzolkin(2460857)).toEqual({number: 2, name: TzolkinName.IX});
|
||||
});
|
||||
|
||||
it('converts sample dates from history correctly', () => {
|
||||
expect(jdnTzolkin(584283)).toEqual({number: 4, name: TzolkinName.AJAW}); // Mayan creation
|
||||
expect(jdnTzolkin(1705426)).toEqual({number: 1, name: TzolkinName.AKBAL}); // Ides of March
|
||||
expect(jdnTzolkin(2266296)).toEqual({number: 12, name: TzolkinName.BEN}); // Columbus reaches the Americas
|
||||
expect(jdnTzolkin(2430336)).toEqual({number: 5, name: TzolkinName.BEN}); // a date which will live in infamy
|
||||
expect(jdnTzolkin(2440423)).toEqual({number: 4, name: TzolkinName.AJAW}); // Moon landing
|
||||
expect(jdnTzolkin(2458920)).toEqual({number: 2, name: TzolkinName.KABAN}); // COVID-19 pandemic
|
||||
});
|
||||
|
||||
it('handles negative JDN correctly', () => {
|
||||
expect(jdnTzolkin(-1)).toEqual({number: 5, name: TzolkinName.KIB});
|
||||
expect(jdnTzolkin(-10)).toEqual({number: 9, name: TzolkinName.MANIK});
|
||||
expect(jdnTzolkin(-100)).toEqual({number: 10, name: TzolkinName.KABAN});
|
||||
expect(jdnTzolkin(-1000)).toEqual({number: 7, name: TzolkinName.KABAN});
|
||||
});
|
||||
|
||||
it('completes a full Tzolkin cycle across JD0', () => {
|
||||
const results = new Set();
|
||||
|
||||
for (let i = -130; i < 130; i++) {
|
||||
results.add(formatTzolkin(jdnTzolkin(i)));
|
||||
}
|
||||
|
||||
// Should have 260 unique combinations
|
||||
expect(results.size).toBe(260);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatHaab', () => {
|
||||
it('formats normal month and day', () => {
|
||||
expect(formatHaab({month: 1, day: 5})).toBe('5 Pop');
|
||||
expect(formatHaab({month: 7, day: 12})).toBe('12 Yaxkʼin');
|
||||
});
|
||||
|
||||
it('formats Wayeb month', () => {
|
||||
expect(formatHaab({month: 19, day: 4})).toBe('4 Wayebʼ');
|
||||
});
|
||||
|
||||
it('formats zero day', () => {
|
||||
expect(formatHaab({month: 3, day: 0})).toBe('0 Sip');
|
||||
});
|
||||
});
|
||||
|
||||
describe('jdnHaab', () => {
|
||||
it('converts sample consecutive dates in June 2025 correctly', () => {
|
||||
expect(jdnHaab(2460828)).toEqual({month: 4, day: 3});
|
||||
expect(jdnHaab(2460829)).toEqual({month: 4, day: 4});
|
||||
expect(jdnHaab(2460830)).toEqual({month: 4, day: 5});
|
||||
expect(jdnHaab(2460831)).toEqual({month: 4, day: 6});
|
||||
expect(jdnHaab(2460832)).toEqual({month: 4, day: 7});
|
||||
expect(jdnHaab(2460833)).toEqual({month: 4, day: 8});
|
||||
expect(jdnHaab(2460834)).toEqual({month: 4, day: 9});
|
||||
expect(jdnHaab(2460835)).toEqual({month: 4, day: 10});
|
||||
expect(jdnHaab(2460836)).toEqual({month: 4, day: 11});
|
||||
expect(jdnHaab(2460837)).toEqual({month: 4, day: 12});
|
||||
expect(jdnHaab(2460838)).toEqual({month: 4, day: 13});
|
||||
expect(jdnHaab(2460839)).toEqual({month: 4, day: 14});
|
||||
expect(jdnHaab(2460840)).toEqual({month: 4, day: 15});
|
||||
expect(jdnHaab(2460841)).toEqual({month: 4, day: 16});
|
||||
expect(jdnHaab(2460842)).toEqual({month: 4, day: 17});
|
||||
expect(jdnHaab(2460843)).toEqual({month: 4, day: 18});
|
||||
expect(jdnHaab(2460844)).toEqual({month: 4, day: 19});
|
||||
expect(jdnHaab(2460845)).toEqual({month: 5, day: 0});
|
||||
expect(jdnHaab(2460846)).toEqual({month: 5, day: 1});
|
||||
expect(jdnHaab(2460847)).toEqual({month: 5, day: 2});
|
||||
expect(jdnHaab(2460848)).toEqual({month: 5, day: 3});
|
||||
expect(jdnHaab(2460849)).toEqual({month: 5, day: 4});
|
||||
expect(jdnHaab(2460850)).toEqual({month: 5, day: 5});
|
||||
expect(jdnHaab(2460851)).toEqual({month: 5, day: 6});
|
||||
expect(jdnHaab(2460852)).toEqual({month: 5, day: 7});
|
||||
expect(jdnHaab(2460853)).toEqual({month: 5, day: 8});
|
||||
expect(jdnHaab(2460854)).toEqual({month: 5, day: 9});
|
||||
expect(jdnHaab(2460855)).toEqual({month: 5, day: 10});
|
||||
expect(jdnHaab(2460856)).toEqual({month: 5, day: 11});
|
||||
expect(jdnHaab(2460857)).toEqual({month: 5, day: 12});
|
||||
});
|
||||
|
||||
it('converts sample dates from history correctly', () => {
|
||||
expect(jdnHaab(584283)).toEqual({month: 18, day: 8}); // Mayan creation
|
||||
expect(jdnHaab(1705426)).toEqual({month: 11, day: 11}); // Ides of March
|
||||
expect(jdnHaab(2266296)).toEqual({month: 4, day: 16}); // Columbus reaches the Americas
|
||||
expect(jdnHaab(2430336)).toEqual({month: 12, day: 11}); // a date which will live in infamy
|
||||
expect(jdnHaab(2440423)).toEqual({month: 5, day: 18}); // Moon landing
|
||||
expect(jdnHaab(2458920)).toEqual({month: 18, day: 5}); // COVID-19 pandemic
|
||||
});
|
||||
|
||||
it('handles negative JDN correctly', () => {
|
||||
expect(jdnHaab(-365)).toEqual({month: 4, day: 5});
|
||||
expect(jdnHaab(-1)).toEqual({month: 4, day: 4});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatLordOfNight', () => {
|
||||
it('should format Lord of Night 1', () => {
|
||||
expect(formatLordOfNight(1)).toBe('G1');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 2', () => {
|
||||
expect(formatLordOfNight(2)).toBe('G2');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 3', () => {
|
||||
expect(formatLordOfNight(3)).toBe('G3');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 4', () => {
|
||||
expect(formatLordOfNight(4)).toBe('G4');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 5', () => {
|
||||
expect(formatLordOfNight(5)).toBe('G5');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 6', () => {
|
||||
expect(formatLordOfNight(6)).toBe('G6');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 7', () => {
|
||||
expect(formatLordOfNight(7)).toBe('G7');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 8', () => {
|
||||
expect(formatLordOfNight(8)).toBe('G8');
|
||||
});
|
||||
|
||||
it('should format Lord of Night 9', () => {
|
||||
expect(formatLordOfNight(9)).toBe('G9');
|
||||
});
|
||||
});
|
||||
|
||||
describe('jdnLordOfNight', () => {
|
||||
it('converts two consecutive cycles correctly', () => {
|
||||
expect(jdnLordOfNight(2460828)).toBe(1);
|
||||
expect(jdnLordOfNight(2460829)).toBe(2);
|
||||
expect(jdnLordOfNight(2460830)).toBe(3);
|
||||
expect(jdnLordOfNight(2460831)).toBe(4);
|
||||
expect(jdnLordOfNight(2460832)).toBe(5);
|
||||
expect(jdnLordOfNight(2460833)).toBe(6);
|
||||
expect(jdnLordOfNight(2460834)).toBe(7);
|
||||
expect(jdnLordOfNight(2460835)).toBe(8);
|
||||
expect(jdnLordOfNight(2460836)).toBe(9);
|
||||
expect(jdnLordOfNight(2460837)).toBe(1);
|
||||
expect(jdnLordOfNight(2460838)).toBe(2);
|
||||
expect(jdnLordOfNight(2460839)).toBe(3);
|
||||
expect(jdnLordOfNight(2460840)).toBe(4);
|
||||
expect(jdnLordOfNight(2460841)).toBe(5);
|
||||
expect(jdnLordOfNight(2460842)).toBe(6);
|
||||
expect(jdnLordOfNight(2460843)).toBe(7);
|
||||
expect(jdnLordOfNight(2460844)).toBe(8);
|
||||
expect(jdnLordOfNight(2460845)).toBe(9);
|
||||
});
|
||||
|
||||
it('converts sample dates from history correctly', () => {
|
||||
expect(jdnLordOfNight(584283)).toBe(1); // Mayan creation
|
||||
expect(jdnLordOfNight(1705426)).toBe(5); // Ides of March
|
||||
expect(jdnLordOfNight(2266296)).toBe(4); // Columbus reaches the Americas
|
||||
expect(jdnLordOfNight(2430336)).toBe(1); // a date which will live in infamy
|
||||
expect(jdnLordOfNight(2440423)).toBe(8); // Moon landing
|
||||
expect(jdnLordOfNight(2458920)).toBe(1); // COVID-19 pandemic
|
||||
});
|
||||
});
|
119
common/src/mayan.ts
Normal file
119
common/src/mayan.ts
Normal file
|
@ -0,0 +1,119 @@
|
|||
export enum TzolkinName {
|
||||
IMIX,
|
||||
IK,
|
||||
AKBAL,
|
||||
KAN,
|
||||
CHIKCHAN,
|
||||
KIMI,
|
||||
MANIK,
|
||||
LAMAT,
|
||||
MULUK,
|
||||
OK,
|
||||
CHUWEN,
|
||||
EB,
|
||||
BEN,
|
||||
IX,
|
||||
MEN,
|
||||
KIB,
|
||||
KABAN,
|
||||
ETZNAB,
|
||||
KAWAK,
|
||||
AJAW
|
||||
}
|
||||
|
||||
export type TzolkinNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
|
||||
|
||||
export type Tzolkin = {
|
||||
number: TzolkinNumber,
|
||||
name: TzolkinName,
|
||||
};
|
||||
|
||||
const tzolkinNames: Record<TzolkinName, string> = {
|
||||
[TzolkinName.IMIX]: 'Imix',
|
||||
[TzolkinName.IK]: 'Ikʼ',
|
||||
[TzolkinName.AKBAL]: 'Akʼbʼal',
|
||||
[TzolkinName.KAN]: 'Kʼan',
|
||||
[TzolkinName.CHIKCHAN]: 'Chikchan',
|
||||
[TzolkinName.KIMI]: 'Kimi',
|
||||
[TzolkinName.MANIK]: 'Manikʼ',
|
||||
[TzolkinName.LAMAT]: 'Lamat',
|
||||
[TzolkinName.MULUK]: 'Muluk',
|
||||
[TzolkinName.OK]: 'Ok',
|
||||
[TzolkinName.CHUWEN]: 'Chuwen',
|
||||
[TzolkinName.EB]: 'Ebʼ',
|
||||
[TzolkinName.BEN]: 'Bʼen',
|
||||
[TzolkinName.IX]: 'Ix',
|
||||
[TzolkinName.MEN]: 'Men',
|
||||
[TzolkinName.KIB]: 'Kibʼ',
|
||||
[TzolkinName.KABAN]: 'Kabʼan',
|
||||
[TzolkinName.ETZNAB]: 'Etzʼnabʼ',
|
||||
[TzolkinName.KAWAK]: 'Kawak',
|
||||
[TzolkinName.AJAW]: 'Ajaw',
|
||||
};
|
||||
|
||||
export function tzolkinName(name: TzolkinName): string {
|
||||
return tzolkinNames[name];
|
||||
}
|
||||
|
||||
export function formatTzolkin(tzolkin: Tzolkin): string {
|
||||
return `${tzolkin.number} ${tzolkinName(tzolkin.name)}`;
|
||||
}
|
||||
|
||||
export function jdnTzolkin(jdn: number): Tzolkin {
|
||||
return {
|
||||
number: ((jdn % 13 + 18) % 13 + 1) as TzolkinNumber,
|
||||
name: (jdn % 20 + 36) % 20,
|
||||
};
|
||||
}
|
||||
|
||||
export type HaabMonth = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19;
|
||||
export type HaabDay = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19;
|
||||
|
||||
export type Haab = {
|
||||
month: HaabMonth,
|
||||
day: HaabDay,
|
||||
};
|
||||
|
||||
const haabNames: Record<HaabMonth, string> = {
|
||||
1: 'Pop',
|
||||
2: 'Woʼ',
|
||||
3: 'Sip',
|
||||
4: 'Sotzʼ',
|
||||
5: 'Sek',
|
||||
6: 'Xul',
|
||||
7: 'Yaxkʼin',
|
||||
8: 'Mol',
|
||||
9: 'Chʼen',
|
||||
10: 'Yax',
|
||||
11: 'Sakʼ',
|
||||
12: 'Keh',
|
||||
13: 'Mak',
|
||||
14: 'Kʼankʼin',
|
||||
15: 'Muwan',
|
||||
16: 'Pax',
|
||||
17: 'Kʼayabʼ',
|
||||
18: 'Kumkʼu',
|
||||
19: 'Wayebʼ',
|
||||
};
|
||||
|
||||
export function formatHaab({month, day}: Haab): string {
|
||||
return `${day} ${haabNames[month]}`;
|
||||
}
|
||||
|
||||
export function jdnHaab(jdn: number): Haab {
|
||||
const yearDay = (jdn % 365 + 430) % 365;
|
||||
return {
|
||||
month: (Math.floor(yearDay / 20) + 1) as HaabMonth,
|
||||
day: yearDay % 20 as HaabDay,
|
||||
};
|
||||
}
|
||||
|
||||
export type LordOfNight = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
||||
|
||||
export function formatLordOfNight(lordOfNight: LordOfNight): string {
|
||||
return `G${lordOfNight}`;
|
||||
}
|
||||
|
||||
export function jdnLordOfNight(jdn: number): LordOfNight {
|
||||
return (jdn % 9 + 15) % 9 + 1 as LordOfNight;
|
||||
}
|
|
@ -143,7 +143,7 @@
|
|||
</div>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">Copyright © 2022<%= new Date().getFullYear() > 2022 ? `–${new Date().getFullYear()}` : '' %>
|
||||
<p class="text-muted">Copyright © 2022-<%= new Date().getFullYear() %>
|
||||
<a href="https://quantum5.ca">Quantum</a>.
|
||||
Licensed under <a href="https://www.gnu.org/licenses/agpl-3.0.en.html">GNU AGPLv3</a>.
|
||||
Source code available on <a href="https://github.com/quantum5/qcal">GitHub</a>.<br>
|
||||
|
|
|
@ -1 +1,49 @@
|
|||
# Gregorian Calendar
|
||||
|
||||

|
||||
|
||||
## What is this?
|
||||
|
||||
This is an implementation of the [Gregorian calendar][0], which is the default
|
||||
civil calendar in most countries. It is meant to help you cross-reference dates
|
||||
by providing the conversions various other calendars and time-keeping systems,
|
||||
such as:
|
||||
|
||||
1. the [Julian day number][2] (JDN);
|
||||
2. the [Julian calendar][1] date;
|
||||
3. the [Mesoamerican Long Count calendar][3] date; and
|
||||
4. the [French Republican calendar][4] date.
|
||||
|
||||
You can [see it live here][5]! More conversions will be added in the future.
|
||||
|
||||
## The Gregorian calendar
|
||||
|
||||
The Gregorian calendar itself is based on the Julian calendar, originally
|
||||
introduced in 46 BC by Julius Caesar (with aid of Sosigenes of Alexandria).
|
||||
Unfortunately, the Julian calendar had leap days every four years without
|
||||
question, resulting in the average year having 365.25 days. However, the actual
|
||||
tropical year is roughly 365.2422 days, resulting in it gaining a day every 129
|
||||
years. This means that seasons drift, starting earlier and earlier in the year.
|
||||
|
||||
In 1582, Pope Gregory XIII decided to fix this drift by reducing the number
|
||||
of leap days, motivated by a desire to keep the March equinox on March 21st,
|
||||
since that value was hardcoded in the calculation for the date of Easter. To
|
||||
achieve this, he made years divisible by 100 but not by 400 non-leap years,
|
||||
resulting in 97 leap years every 400 years. To bring the equinox back into
|
||||
alignment, October 5th to October 14th in 1582 were deleted, creating the
|
||||
Gregorian calendar we use today.
|
||||
|
||||
This website extends the Gregorian calendar indefinitely into the past for
|
||||
reference reasons.
|
||||
|
||||
[0]: https://en.wikipedia.org/wiki/Gregorian_calendar
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Julian_calendar
|
||||
|
||||
[2]: https://en.wikipedia.org/wiki/Julian_day
|
||||
|
||||
[3]: https://en.wikipedia.org/wiki/Mesoamerican_Long_Count_calendar
|
||||
|
||||
[4]: https://en.wikipedia.org/wiki/French_Republican_calendar
|
||||
|
||||
[5]: https://gcal.qt.ax/
|
||||
|
|
BIN
gcal/demo.png
Normal file
BIN
gcal/demo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 430 KiB |
|
@ -60,6 +60,7 @@
|
|||
<ul>
|
||||
<li>the <a href="https://en.wikipedia.org/wiki/Julian_calendar">Julian calendar</a> date;</li>
|
||||
<li>the <a href="https://en.wikipedia.org/wiki/Julian_day">Julian day (JD) number</a>;</li>
|
||||
<li>the Mayan <a href="https://en.wikipedia.org/wiki/Tzolk%CA%BCin">Tzolkʼin</a> day cycle;</li>
|
||||
<li>the <a href="https://en.wikipedia.org/wiki/Mesoamerican_Long_Count_calendar">Mesoamerican Long Count
|
||||
calendar</a> date; and
|
||||
</li>
|
||||
|
@ -119,6 +120,20 @@
|
|||
Universal Time noon on that date.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">What is the TZ (Mayan Tzolkʼin) date?</h4>
|
||||
<p>This is the day name in the 260-day <a href="https://en.wikipedia.org/wiki/Tzolk%CA%BCin">Tzolkʼin</a>
|
||||
cycle used in the Mayan calendar. This cycle repeats indefinitely with each cycle unnumbered.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">What is the HA (Mayan Haabʼ) date?</h4>
|
||||
<p>This is the day name in the 365-day <a href="https://en.wikipedia.org/wiki/Haab%CA%BC">Haabʼ</a>
|
||||
cycle used in the Mayan calendar. This cycle repeats indefinitely with each cycle unnumbered.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">What is the LC (Mesoamerican Long Count) date?</h4>
|
||||
|
@ -145,8 +160,7 @@
|
|||
</div>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">Copyright © 2022<%= new Date().getFullYear() > 2022 ? `–${new Date().getFullYear()}`
|
||||
: '' %>
|
||||
<p class="text-muted">Copyright © 2022-<%= new Date().getFullYear() %>
|
||||
<a href="https://quantum5.ca">Quantum</a>.
|
||||
Licensed under <a href="https://www.gnu.org/licenses/agpl-3.0.en.html">GNU AGPLv3</a>.
|
||||
Source code available on <a href="https://github.com/quantum5/qcal">GitHub</a>.<br>
|
||||
|
@ -154,9 +168,9 @@
|
|||
</div>
|
||||
</footer>
|
||||
<script>
|
||||
(function(j,u,l,i,a,n,c){j['GoogleAnalyticsObject']=a;j[a]=j[a]||function(){
|
||||
(j[a].q=j[a].q||[]).push(arguments)},j[a].l=1*new Date();n=u.createElement(l),
|
||||
c=u.getElementsByTagName(l)[0];n.async=1;n.src=i;c.parentNode.insertBefore(n,c)
|
||||
(function(g,r,e,G,o,R,y){g['GoogleAnalyticsObject']=o;g[o]=g[o]||function(){
|
||||
(g[o].q=g[o].q||[]).push(arguments)},g[o].l=1*new Date();R=r.createElement(e),
|
||||
y=r.getElementsByTagName(e)[0];R.async=1;R.src=G;y.parentNode.insertBefore(R,y)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-102581070-4', 'auto');
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import {Calendar} from './Calendar';
|
||||
import {JulianMonth} from '@common/gregorian';
|
||||
import {DayChanger} from '@common/ui/DayChanger';
|
||||
import {jdnJulian} from '@common/julian';
|
||||
import {jdnGregorian} from '@common/gregorian';
|
||||
import MonthBasedApp from '@common/ui/MonthBasedApp';
|
||||
|
||||
export default class App extends MonthBasedApp<number, JulianMonth> {
|
||||
|
@ -13,7 +13,7 @@ export default class App extends MonthBasedApp<number, JulianMonth> {
|
|||
}
|
||||
|
||||
override defaultSelector(todayJDN: number) {
|
||||
const [year, month] = jdnJulian(todayJDN);
|
||||
const [year, month] = jdnGregorian(todayJDN);
|
||||
return {year, month};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,12 @@ import {
|
|||
JulianDay,
|
||||
JulianMonth,
|
||||
monthName,
|
||||
jdnWeekDay,
|
||||
weekdayNames,
|
||||
} from '@common/gregorian';
|
||||
import {formatHaab, formatTzolkin, jdnHaab, jdnTzolkin} from '@common/mayan';
|
||||
import {jdnLongCount} from '@common/longCount';
|
||||
import {jdnJulian, julianJDN, julianMonthDays} from '@common/julian';
|
||||
import {jdnJulian} from '@common/julian';
|
||||
import {frDateFormat, frEndJD, frStartJD, jdnFrench} from '@common/french';
|
||||
import {useMobileTooltipProps} from '@common/ui/MobileTooltip';
|
||||
import {MonthBasedCalendar} from '@common/ui/MonthBasedCalendar';
|
||||
|
@ -40,8 +42,16 @@ function DayDetail({jdn}: { jdn: number }): JSX.Element {
|
|||
<abbr title="Julian date" {...mobile}>J.</abbr>{' '}
|
||||
{formatJG(jdnJulian(jdn))}
|
||||
</div>
|
||||
<div className="DayDetail-tz">
|
||||
<abbr title="Tzolkʼin (Mayan)" {...mobile}>TZ</abbr>{' '}
|
||||
{formatTzolkin(jdnTzolkin(jdn))}
|
||||
</div>
|
||||
<div className="DayDetail-haab">
|
||||
<abbr title="Haabʼ (Mayan)" {...mobile}>HA</abbr>{' '}
|
||||
{formatHaab(jdnHaab(jdn))}
|
||||
</div>
|
||||
{lc && <div className="DayDetail-lc">
|
||||
<abbr title="Mesoamerican long count date" {...mobile}>LC</abbr>{' '}
|
||||
<abbr title="Mesoamerican long count date" {...mobile}>LC</abbr>{' '}
|
||||
{lc.join('.\u200b')}
|
||||
</div>}
|
||||
{jdn >= frStartJD && jdn <= frEndJD && <div className="DayDetail-fr">
|
||||
|
@ -55,7 +65,7 @@ function Day({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JS
|
|||
const jdn = gregorianJDN(year, month, day);
|
||||
return <div className={`Day NormalDay ${jdn === todayJDN ? 'Day-today' : ''}`}>
|
||||
<div className="Day-name">{day}</div>
|
||||
<div className="Day-weekday">{weekdayNames[(jdn + 1) % 7]}</div>
|
||||
<div className="Day-weekday">{weekdayNames[jdnWeekDay(jdn)]}</div>
|
||||
<DayDetail jdn={jdn}/>
|
||||
</div>;
|
||||
}
|
||||
|
@ -63,7 +73,7 @@ function Day({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JS
|
|||
function Month({year, month, todayJDN}: MonthProps & { todayJDN: number }): JSX.Element {
|
||||
const decadeHeads = weekdayNames.map((name, i) => <WeekdayName key={i} name={name}/>);
|
||||
const firstJDN = gregorianJDN(year, month, 1);
|
||||
const firstWeekday = (firstJDN + 1) % 7;
|
||||
const firstWeekday = jdnWeekDay(firstJDN);
|
||||
const daysTotal = gregorianMonthDays(year, month);
|
||||
return <div className="Month">
|
||||
<div className="Month-weekdayHead">{decadeHeads}</div>
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||

|
||||
|
||||
[See it live!][0]
|
||||
|
||||
## What is this?
|
||||
|
||||
The [Julian calendar][0] is a calendar introduced by Julius Caesar in 46 BC. A
|
||||
reformed version, the [Gregorian calendar][1], forms the basis of civil calendar
|
||||
The [Julian calendar][1] is a calendar introduced by Julius Caesar in 46 BC. A
|
||||
reformed version, the [Gregorian calendar][2], forms the basis of civil calendar
|
||||
in most countries.
|
||||
|
||||
Julius Caesar (with aid of Sosigenes of Alexandria) introduced this calendar to
|
||||
|
@ -28,5 +30,8 @@ This website extends the Julian calendar indefinitely into the future for
|
|||
reference reasons. Note that in the 20th and 21st centuries, the Julian
|
||||
calendar is 13 days behind the Gregorian calendar.
|
||||
|
||||
[0]: https://en.wikipedia.org/wiki/Julian_calendar
|
||||
[1]: https://en.wikipedia.org/wiki/Gregorian_calendar
|
||||
[0]: https://jcal.qt.ax/
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Julian_calendar
|
||||
|
||||
[2]: https://en.wikipedia.org/wiki/Gregorian_calendar
|
||||
|
|
|
@ -120,8 +120,7 @@
|
|||
</div>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">Copyright © 2022<%= new Date().getFullYear() > 2022 ? `–${new Date().getFullYear()}`
|
||||
: '' %>
|
||||
<p class="text-muted">Copyright © 2022-<%= new Date().getFullYear() %>
|
||||
<a href="https://quantum5.ca">Quantum</a>.
|
||||
Licensed under <a href="https://www.gnu.org/licenses/agpl-3.0.en.html">GNU AGPLv3</a>.
|
||||
Source code available on <a href="https://github.com/quantum5/qcal">GitHub</a>.<br>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import './Calendar.scss';
|
||||
import {formatJG, jdnGregorian, JulianDay, JulianMonth, monthName, weekdayNames} from '@common/gregorian';
|
||||
import {formatJG, jdnGregorian, JulianDay, JulianMonth, monthName, jdnWeekDay, weekdayNames} from '@common/gregorian';
|
||||
import {jdnLongCount} from '@common/longCount';
|
||||
import {jdnJulian, julianJDN, julianMonthDays} from '@common/julian';
|
||||
import {frDateFormat, frEndJD, frStartJD, jdnFrench} from '@common/french';
|
||||
|
@ -46,7 +46,7 @@ function Day({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JS
|
|||
const jdn = julianJDN(year, month, day);
|
||||
return <div className={`Day NormalDay ${jdn === todayJDN ? 'Day-today' : ''}`}>
|
||||
<div className="Day-name">{day}</div>
|
||||
<div className="Day-weekday">{weekdayNames[(jdn + 1) % 7]}</div>
|
||||
<div className="Day-weekday">{weekdayNames[jdnWeekDay(jdn)]}</div>
|
||||
<DayDetail jdn={jdn}/>
|
||||
</div>;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ function Day({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JS
|
|||
function Month({year, month, todayJDN}: MonthProps & { todayJDN: number }): JSX.Element {
|
||||
const decadeHeads = weekdayNames.map((name, i) => <WeekdayName key={i} name={name}/>);
|
||||
const firstJDN = julianJDN(year, month, 1);
|
||||
const firstWeekday = (firstJDN + 1) % 7;
|
||||
const firstWeekday = jdnWeekDay(firstJDN);
|
||||
const daysTotal = julianMonthDays(year, month);
|
||||
return <div className="Month">
|
||||
<div className="Month-weekdayHead">{decadeHeads}</div>
|
||||
|
|
Loading…
Reference in a new issue