mirror of
https://github.com/quantum5/qcal.git
synced 2025-04-24 17:51:57 -04:00
Add Julian calendar handling
This commit is contained in:
parent
17229a8c2e
commit
56cef40dba
|
@ -1,4 +1,4 @@
|
||||||
import {gregorianJDN, jdnGregorian} from './gregorian';
|
import {gregorianJDN, jdnDate, jdnGregorian, JulianDay, JulianMonth} from './gregorian';
|
||||||
|
|
||||||
describe('gregorianJDN', () => {
|
describe('gregorianJDN', () => {
|
||||||
it('works', () => {
|
it('works', () => {
|
||||||
|
@ -15,25 +15,55 @@ describe('gregorianJDN', () => {
|
||||||
expect(gregorianJDN(19720, 8, 14)).toBe(8923868);
|
expect(gregorianJDN(19720, 8, 14)).toBe(8923868);
|
||||||
expect(gregorianJDN(7504, 7, 22)).toBe(4462042);
|
expect(gregorianJDN(7504, 7, 22)).toBe(4462042);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('transition to Julian works', () => {
|
||||||
|
expect(gregorianJDN(2000, 1, 1, 2299161)).toBe(2451545);
|
||||||
|
expect(gregorianJDN(1969, 7, 20, 2299161)).toBe(2440423);
|
||||||
|
expect(gregorianJDN(1582, 10, 15, 2299161)).toBe(2299161);
|
||||||
|
expect(gregorianJDN(1582, 10, 4, 2299161)).toBe(2299160);
|
||||||
|
expect(gregorianJDN(1066, 10, 14, 2299161)).toBe(2110701);
|
||||||
|
expect(gregorianJDN(0, 12, 25, 2299161)).toBe(1721417);
|
||||||
|
expect(gregorianJDN(-4712, 1, 1, 2299161)).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('jdnGregorian', () => {
|
describe('jdnGregorian', () => {
|
||||||
it('works', () => {
|
it('works', () => {
|
||||||
expect(jdnGregorian(0)).toEqual(new Date(-4713, 10, 24));
|
function checkJDN(jdn: number, year: number, month: JulianMonth, day: JulianDay) {
|
||||||
expect(jdnGregorian(2299160)).toEqual(new Date(1582, 9, 14));
|
expect(jdnGregorian(jdn)).toEqual([year, month, day]);
|
||||||
expect(jdnGregorian(2299161)).toEqual(new Date(1582, 9, 15));
|
expect(jdnDate(jdn)).toEqual(new Date(year, month - 1, day));
|
||||||
expect(jdnGregorian(2361221)).toEqual(new Date(1752, 8, 13));
|
}
|
||||||
expect(jdnGregorian(2361222)).toEqual(new Date(1752, 8, 14));
|
|
||||||
expect(jdnGregorian(2451545)).toEqual(new Date(2000, 0, 1));
|
checkJDN(0, -4713, 11, 24);
|
||||||
expect(jdnGregorian(-8512316)).toEqual(new Date(-28019, 11, 20));
|
checkJDN(2299160, 1582, 10, 14);
|
||||||
expect(jdnGregorian(-8534852)).toEqual(new Date(-28080, 3, 8));
|
checkJDN(2299161, 1582, 10, 15);
|
||||||
expect(jdnGregorian(2653462)).toEqual(new Date(2552, 9, 30));
|
checkJDN(2361221, 1752, 9, 13);
|
||||||
expect(jdnGregorian(3271156)).toEqual(new Date(4244, 0, 8));
|
checkJDN(2361222, 1752, 9, 14);
|
||||||
expect(jdnGregorian(-666477)).toEqual(new Date(-6537, 1, 23));
|
checkJDN(2451545, 2000, 1, 1);
|
||||||
expect(jdnGregorian(2397854)).toEqual(new Date(1852, 11, 31));
|
checkJDN(-8512316, -28019, 12, 20);
|
||||||
expect(jdnGregorian(-1211235)).toEqual(new Date(-8029, 7, 26));
|
checkJDN(-8534852, -28080, 4, 8);
|
||||||
expect(jdnGregorian(-91680)).toEqual(new Date(-4964, 10, 20));
|
checkJDN(2653462, 2552, 10, 30);
|
||||||
expect(jdnGregorian(-5605876)).toEqual(new Date(-20061, 6, 14));
|
checkJDN(3271156, 4244, 1, 8);
|
||||||
expect(jdnGregorian(-295121)).toEqual(new Date(-5521, 10, 19));
|
checkJDN(-666477, -6537, 2, 23);
|
||||||
|
checkJDN(2397854, 1852, 12, 31);
|
||||||
|
checkJDN(-1211235, -8029, 8, 26);
|
||||||
|
checkJDN(-91680, -4964, 11, 20);
|
||||||
|
checkJDN(-5605876, -20061, 7, 14);
|
||||||
|
checkJDN(-295121, -5521, 11, 19);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transition to Julian works', () => {
|
||||||
|
function checkJDN(jdn: number, year: number, month: JulianMonth, day: JulianDay) {
|
||||||
|
expect(jdnGregorian(jdn, 2299161)).toEqual([year, month, day]);
|
||||||
|
expect(jdnDate(jdn, 2299161)).toEqual(new Date(year, month - 1, day));
|
||||||
|
}
|
||||||
|
|
||||||
|
checkJDN(2451545, 2000, 1, 1);
|
||||||
|
checkJDN(2440423, 1969, 7, 20);
|
||||||
|
checkJDN(2299161, 1582, 10, 15);
|
||||||
|
checkJDN(2299160, 1582, 10, 4);
|
||||||
|
checkJDN(2110701, 1066, 10, 14);
|
||||||
|
checkJDN(1721417, 0, 12, 25);
|
||||||
|
checkJDN(0, -4712, 1, 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +1,69 @@
|
||||||
export function gregorianJDN(year: number, month: number, day: number): number {
|
export type JulianMonth = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||||
|
export type JulianDay =
|
||||||
|
| 1
|
||||||
|
| 2
|
||||||
|
| 3
|
||||||
|
| 4
|
||||||
|
| 5
|
||||||
|
| 6
|
||||||
|
| 7
|
||||||
|
| 8
|
||||||
|
| 9
|
||||||
|
| 10
|
||||||
|
| 11
|
||||||
|
| 12
|
||||||
|
| 13
|
||||||
|
| 14
|
||||||
|
| 15
|
||||||
|
| 16
|
||||||
|
| 17
|
||||||
|
| 18
|
||||||
|
| 19
|
||||||
|
| 20
|
||||||
|
| 21
|
||||||
|
| 22
|
||||||
|
| 23
|
||||||
|
| 24
|
||||||
|
| 25
|
||||||
|
| 26
|
||||||
|
| 27
|
||||||
|
| 28
|
||||||
|
| 29
|
||||||
|
| 30
|
||||||
|
| 31;
|
||||||
|
|
||||||
|
export type JulianDate = [number, JulianMonth, JulianDay];
|
||||||
|
|
||||||
|
export function gregorianJDN(year: number, month: number, day: number, julian_before?: number): number {
|
||||||
const g = year + 4716 - (month <= 2 ? 1 : 0);
|
const g = year + 4716 - (month <= 2 ? 1 : 0);
|
||||||
const f = (month + 9) % 12;
|
const f = (month + 9) % 12;
|
||||||
const e = Math.floor(1461 * g / 4) + day - 1402;
|
const e = Math.floor(1461 * g / 4) + day - 1402;
|
||||||
const J = e + Math.floor((153 * f + 2) / 5);
|
const J = e + Math.floor((153 * f + 2) / 5);
|
||||||
|
|
||||||
|
if (julian_before != null && J < julian_before)
|
||||||
|
return J;
|
||||||
|
|
||||||
const dg = 38 - Math.floor(Math.floor((g + 184) / 100) * 3 / 4);
|
const dg = 38 - Math.floor(Math.floor((g + 184) / 100) * 3 / 4);
|
||||||
return J + dg;
|
return J + dg;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dateJDN(date: Date) {
|
export function dateJDN(date: Date, julian_before?: number) {
|
||||||
return gregorianJDN(date.getFullYear(), date.getMonth() + 1, date.getDate());
|
return gregorianJDN(date.getFullYear(), date.getMonth() + 1, date.getDate(), julian_before);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function jdnGregorian(jdn: number): Date {
|
export function jdnGregorian(jdn: number, julian_before?: number): JulianDate {
|
||||||
const e = 4 * (jdn + 1401 + Math.floor(Math.floor((4 * jdn + 274277) / 146097) * 3 / 4) - 38) + 3;
|
const dg = julian_before == null || jdn >= julian_before ?
|
||||||
|
Math.floor(Math.floor((4 * jdn + 274277) / 146097) * 3 / 4) - 38 :
|
||||||
|
0;
|
||||||
|
const e = 4 * (jdn + 1401 + dg) + 3;
|
||||||
const h = 5 * Math.floor((e % 1461 + 1461) % 1461 / 4) + 2;
|
const h = 5 * Math.floor((e % 1461 + 1461) % 1461 / 4) + 2;
|
||||||
const day = Math.floor((h % 153 + 153) % 153 / 5) + 1;
|
const day = Math.floor((h % 153 + 153) % 153 / 5) + 1;
|
||||||
const month = (Math.floor(h / 153) + 2) % 12 + 1;
|
const month = (Math.floor(h / 153) + 2) % 12 + 1;
|
||||||
const year = Math.floor(e / 1461) - 4716 + Math.floor((14 - month) / 12);
|
const year = Math.floor(e / 1461) - 4716 + Math.floor((14 - month) / 12);
|
||||||
|
return [year, month as JulianMonth, day as JulianDay];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jdnDate(jdn: number, julian_before?: number): Date {
|
||||||
|
const [year, month, day] = jdnGregorian(jdn, julian_before);
|
||||||
return new Date(year, month - 1, day);
|
return new Date(year, month - 1, day);
|
||||||
}
|
}
|
||||||
|
|
25
common/src/julian.test.ts
Normal file
25
common/src/julian.test.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import {jdnJulian, julianJDN} from './julian';
|
||||||
|
|
||||||
|
describe('gregorianJDN', () => {
|
||||||
|
it('works', () => {
|
||||||
|
expect(julianJDN(2000, 1, 1)).toBe(2451558);
|
||||||
|
expect(julianJDN(1969, 7, 20)).toBe(2440436);
|
||||||
|
expect(julianJDN(1582, 10, 15)).toBe(2299171);
|
||||||
|
expect(julianJDN(1582, 10, 4)).toBe(2299160);
|
||||||
|
expect(julianJDN(1066, 10, 14)).toBe(2110701);
|
||||||
|
expect(julianJDN(0, 12, 25)).toBe(1721417);
|
||||||
|
expect(julianJDN(-4712, 1, 1)).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('jdnGregorian', () => {
|
||||||
|
it('works', () => {
|
||||||
|
expect(jdnJulian(2451558)).toEqual([2000, 1, 1]);
|
||||||
|
expect(jdnJulian(2440436)).toEqual([1969, 7, 20]);
|
||||||
|
expect(jdnJulian(2299171)).toEqual([1582, 10, 15]);
|
||||||
|
expect(jdnJulian(2299160)).toEqual([1582, 10, 4]);
|
||||||
|
expect(jdnJulian(2110701)).toEqual([1066, 10, 14]);
|
||||||
|
expect(jdnJulian(1721417)).toEqual([0, 12, 25]);
|
||||||
|
expect(jdnJulian(0)).toEqual([-4712, 1, 1]);
|
||||||
|
});
|
||||||
|
});
|
9
common/src/julian.ts
Normal file
9
common/src/julian.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import {gregorianJDN, jdnGregorian, JulianDate} from './gregorian';
|
||||||
|
|
||||||
|
export function julianJDN(year: number, month: number, day: number): number {
|
||||||
|
return gregorianJDN(year, month, day, Infinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jdnJulian(jdn: number): JulianDate {
|
||||||
|
return jdnGregorian(jdn, Infinity);
|
||||||
|
}
|
|
@ -80,8 +80,8 @@ class App extends React.Component<{}, AppState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
validYear() {
|
validYear() {
|
||||||
return /^-?\d+$/.test(this.state.goYear) && startGregorian.getFullYear() <= +this.state.goYear &&
|
return /^-?\d+$/.test(this.state.goYear) && startGregorian[0] <= +this.state.goYear &&
|
||||||
+this.state.goYear <= endGregorian.getFullYear();
|
+this.state.goYear <= endGregorian[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
validMonth() {
|
validMonth() {
|
||||||
|
@ -112,7 +112,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
|
|
||||||
onDateChange = (todayJDN: number) => {
|
onDateChange = (todayJDN: number) => {
|
||||||
this.setState({todayJDN});
|
this.setState({todayJDN});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <>
|
return <>
|
||||||
|
@ -130,7 +130,7 @@ class App extends React.Component<{}, AppState> {
|
||||||
<span className="input-group-text">Gregorian<span className="hide-small"> Date</span></span>
|
<span className="input-group-text">Gregorian<span className="hide-small"> Date</span></span>
|
||||||
<input type="number" className={`form-control go-year ${this.validYear() ? '' : 'is-invalid'}`}
|
<input type="number" className={`form-control go-year ${this.validYear() ? '' : 'is-invalid'}`}
|
||||||
onChange={this.changeField.bind(this, 'goYear')} value={this.state.goYear}
|
onChange={this.changeField.bind(this, 'goYear')} value={this.state.goYear}
|
||||||
min={startGregorian.getFullYear()} max={endGregorian.getFullYear()}/>
|
min={startGregorian[0]} max={endGregorian[0]}/>
|
||||||
<input type="number" className={`form-control go-month ${this.validMonth() ? '' : 'is-invalid'}`}
|
<input type="number" className={`form-control go-month ${this.validMonth() ? '' : 'is-invalid'}`}
|
||||||
onChange={this.changeField.bind(this, 'goMonth')} value={this.state.goMonth}
|
onChange={this.changeField.bind(this, 'goMonth')} value={this.state.goMonth}
|
||||||
min={1} max={12}/>
|
min={1} max={12}/>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
monthName,
|
monthName,
|
||||||
startYear,
|
startYear,
|
||||||
} from '@common/french';
|
} from '@common/french';
|
||||||
import {jdnGregorian} from '@common/gregorian';
|
import {jdnDate} from '@common/gregorian';
|
||||||
import {jdnLongCount} from '@common/longCount';
|
import {jdnLongCount} from '@common/longCount';
|
||||||
|
|
||||||
type MonthProps = {
|
type MonthProps = {
|
||||||
|
@ -31,7 +31,7 @@ function DecadeName({name}: { name: string }): JSX.Element {
|
||||||
|
|
||||||
function DayDetail({jdn}: { jdn: number }): JSX.Element {
|
function DayDetail({jdn}: { jdn: number }): JSX.Element {
|
||||||
return <div className="DayDetail">
|
return <div className="DayDetail">
|
||||||
<div className="DayDetail-gregorian">{jdnGregorian(jdn).toDateString()}</div>
|
<div className="DayDetail-gregorian">{jdnDate(jdn).toDateString()}</div>
|
||||||
<div className="DayDetail-lc">{jdnLongCount(jdn)}</div>
|
<div className="DayDetail-lc">{jdnLongCount(jdn)}</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue