|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import {AnyCalendarDate} from '../types'; |
|
import {CalendarDate} from '../CalendarDate'; |
|
import {GregorianCalendar} from './GregorianCalendar'; |
|
import {Mutable} from '../utils'; |
|
|
|
const ERA_START_DATES = [[1868, 9, 8], [1912, 7, 30], [1926, 12, 25], [1989, 1, 8], [2019, 5, 1]]; |
|
const ERA_END_DATES = [[1912, 7, 29], [1926, 12, 24], [1989, 1, 7], [2019, 4, 30]]; |
|
const ERA_ADDENDS = [1867, 1911, 1925, 1988, 2018]; |
|
const ERA_NAMES = ['meiji', 'taisho', 'showa', 'heisei', 'reiwa']; |
|
|
|
function findEraFromGregorianDate(date: AnyCalendarDate) { |
|
const idx = ERA_START_DATES.findIndex(([year, month, day]) => { |
|
if (date.year < year) { |
|
return true; |
|
} |
|
|
|
if (date.year === year && date.month < month) { |
|
return true; |
|
} |
|
|
|
if (date.year === year && date.month === month && date.day < day) { |
|
return true; |
|
} |
|
|
|
return false; |
|
}); |
|
|
|
if (idx === -1) { |
|
return ERA_START_DATES.length - 1; |
|
} |
|
|
|
if (idx === 0) { |
|
return 0; |
|
} |
|
|
|
return idx - 1; |
|
} |
|
|
|
function toGregorian(date: AnyCalendarDate) { |
|
let eraAddend = ERA_ADDENDS[ERA_NAMES.indexOf(date.era)]; |
|
if (!eraAddend) { |
|
throw new Error('Unknown era: ' + date.era); |
|
} |
|
|
|
return new CalendarDate( |
|
date.year + eraAddend, |
|
date.month, |
|
date.day |
|
); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
export class JapaneseCalendar extends GregorianCalendar { |
|
identifier = 'japanese'; |
|
|
|
fromJulianDay(jd: number): CalendarDate { |
|
let date = super.fromJulianDay(jd); |
|
let era = findEraFromGregorianDate(date); |
|
|
|
return new CalendarDate( |
|
this, |
|
ERA_NAMES[era], |
|
date.year - ERA_ADDENDS[era], |
|
date.month, |
|
date.day |
|
); |
|
} |
|
|
|
toJulianDay(date: AnyCalendarDate) { |
|
return super.toJulianDay(toGregorian(date)); |
|
} |
|
|
|
balanceDate(date: Mutable<AnyCalendarDate>) { |
|
let gregorianDate = toGregorian(date); |
|
let era = findEraFromGregorianDate(gregorianDate); |
|
|
|
if (ERA_NAMES[era] !== date.era) { |
|
date.era = ERA_NAMES[era]; |
|
date.year = gregorianDate.year - ERA_ADDENDS[era]; |
|
} |
|
|
|
|
|
this.constrainDate(date); |
|
} |
|
|
|
constrainDate(date: Mutable<AnyCalendarDate>) { |
|
let idx = ERA_NAMES.indexOf(date.era); |
|
let end = ERA_END_DATES[idx]; |
|
if (end != null) { |
|
let [endYear, endMonth, endDay] = end; |
|
|
|
|
|
|
|
let maxYear = endYear - ERA_ADDENDS[idx]; |
|
date.year = Math.max(1, Math.min(maxYear, date.year)); |
|
if (date.year === maxYear) { |
|
date.month = Math.min(endMonth, date.month); |
|
|
|
if (date.month === endMonth) { |
|
date.day = Math.min(endDay, date.day); |
|
} |
|
} |
|
} |
|
|
|
if (date.year === 1 && idx >= 0) { |
|
let [, startMonth, startDay] = ERA_START_DATES[idx]; |
|
date.month = Math.max(startMonth, date.month); |
|
|
|
if (date.month === startMonth) { |
|
date.day = Math.max(startDay, date.day); |
|
} |
|
} |
|
} |
|
|
|
getEras() { |
|
return ERA_NAMES; |
|
} |
|
|
|
getYearsInEra(date: AnyCalendarDate): number { |
|
|
|
let era = ERA_NAMES.indexOf(date.era); |
|
let cur = ERA_START_DATES[era]; |
|
let next = ERA_START_DATES[era + 1]; |
|
if (next == null) { |
|
|
|
return 9999 - cur[0] + 1; |
|
} |
|
|
|
let years = next[0] - cur[0]; |
|
|
|
if (date.month < next[1] || (date.month === next[1] && date.day < next[2])) { |
|
years++; |
|
} |
|
|
|
return years; |
|
} |
|
|
|
getDaysInMonth(date: AnyCalendarDate): number { |
|
return super.getDaysInMonth(toGregorian(date)); |
|
} |
|
|
|
getMinimumMonthInYear(date: AnyCalendarDate): number { |
|
let start = getMinimums(date); |
|
return start ? start[1] : 1; |
|
} |
|
|
|
getMinimumDayInMonth(date: AnyCalendarDate): number { |
|
let start = getMinimums(date); |
|
return start && date.month === start[1] ? start[2] : 1; |
|
} |
|
} |
|
|
|
function getMinimums(date: AnyCalendarDate) { |
|
if (date.year === 1) { |
|
let idx = ERA_NAMES.indexOf(date.era); |
|
return ERA_START_DATES[idx]; |
|
} |
|
} |
|
|