Compare commits

..

No commits in common. "7a3e62df1747de45f17ae809604aa7fe5598d05a" and "8f3a6dde0fdb230eb18927f5aac90e80d99af250" have entirely different histories.

13 changed files with 23 additions and 460 deletions

View file

@ -4,7 +4,5 @@ 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) ([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))
* [French Republican Calendar](frcal/README.md)
* [Julian Calendar](jcal/README.md)

View file

@ -49,8 +49,6 @@ 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',
];
@ -93,10 +91,6 @@ 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:

View file

@ -1,234 +0,0 @@
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
});
});

View file

@ -1,119 +0,0 @@
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;
}

View file

@ -143,7 +143,7 @@
</div>
<footer class="footer">
<div class="container">
<p class="text-muted">Copyright &copy; 2022-<%= new Date().getFullYear() %>
<p class="text-muted">Copyright &copy; 2022<%= new Date().getFullYear() > 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>

View file

@ -1,49 +1 @@
# Gregorian Calendar
![demo](demo.png)
## 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/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 KiB

View file

@ -60,7 +60,6 @@
<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>
@ -120,20 +119,6 @@
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>
@ -160,7 +145,8 @@
</div>
<footer class="footer">
<div class="container">
<p class="text-muted">Copyright &copy; 2022-<%= new Date().getFullYear() %>
<p class="text-muted">Copyright &copy; 2022<%= new Date().getFullYear() > 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>
@ -168,9 +154,9 @@
</div>
</footer>
<script>
(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)
(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)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-102581070-4', 'auto');

View file

@ -2,7 +2,7 @@ import React from 'react';
import {Calendar} from './Calendar';
import {JulianMonth} from '@common/gregorian';
import {DayChanger} from '@common/ui/DayChanger';
import {jdnGregorian} from '@common/gregorian';
import {jdnJulian} from '@common/julian';
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] = jdnGregorian(todayJDN);
const [year, month] = jdnJulian(todayJDN);
return {year, month};
}

View file

@ -8,12 +8,10 @@ import {
JulianDay,
JulianMonth,
monthName,
jdnWeekDay,
weekdayNames,
} from '@common/gregorian';
import {formatHaab, formatTzolkin, jdnHaab, jdnTzolkin} from '@common/mayan';
import {jdnLongCount} from '@common/longCount';
import {jdnJulian} from '@common/julian';
import {jdnJulian, julianJDN, julianMonthDays} from '@common/julian';
import {frDateFormat, frEndJD, frStartJD, jdnFrench} from '@common/french';
import {useMobileTooltipProps} from '@common/ui/MobileTooltip';
import {MonthBasedCalendar} from '@common/ui/MonthBasedCalendar';
@ -42,16 +40,8 @@ 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">
@ -65,7 +55,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[jdnWeekDay(jdn)]}</div>
<div className="Day-weekday">{weekdayNames[(jdn + 1) % 7]}</div>
<DayDetail jdn={jdn}/>
</div>;
}
@ -73,7 +63,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 = jdnWeekDay(firstJDN);
const firstWeekday = (firstJDN + 1) % 7;
const daysTotal = gregorianMonthDays(year, month);
return <div className="Month">
<div className="Month-weekdayHead">{decadeHeads}</div>

View file

@ -2,12 +2,10 @@
![demo](demo.png)
[See it live!][0]
## What is this?
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
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
in most countries.
Julius Caesar (with aid of Sosigenes of Alexandria) introduced this calendar to
@ -30,8 +28,5 @@ 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://jcal.qt.ax/
[1]: https://en.wikipedia.org/wiki/Julian_calendar
[2]: https://en.wikipedia.org/wiki/Gregorian_calendar
[0]: https://en.wikipedia.org/wiki/Julian_calendar
[1]: https://en.wikipedia.org/wiki/Gregorian_calendar

View file

@ -120,7 +120,8 @@
</div>
<footer class="footer">
<div class="container">
<p class="text-muted">Copyright &copy; 2022-<%= new Date().getFullYear() %>
<p class="text-muted">Copyright &copy; 2022<%= new Date().getFullYear() > 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>

View file

@ -1,6 +1,6 @@
import React from 'react';
import './Calendar.scss';
import {formatJG, jdnGregorian, JulianDay, JulianMonth, monthName, jdnWeekDay, weekdayNames} from '@common/gregorian';
import {formatJG, jdnGregorian, JulianDay, JulianMonth, monthName, 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[jdnWeekDay(jdn)]}</div>
<div className="Day-weekday">{weekdayNames[(jdn + 1) % 7]}</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 = jdnWeekDay(firstJDN);
const firstWeekday = (firstJDN + 1) % 7;
const daysTotal = julianMonthDays(year, month);
return <div className="Month">
<div className="Month-weekdayHead">{decadeHeads}</div>