@tubular/time
Version:
Date/time, IANA timezones, leap seconds, TAI/UTC conversions, calendar with settable Julian/Gregorian switchover
919 lines • 37.6 kB
JavaScript
import { div_rd, div_tt0, floor, mod } from '@tubular/math';
import { isArray, isNumber, isObject, isString, padLeft } from '@tubular/util';
import { DAY_MSEC, HOUR_MSEC, MINUTE_MSEC, syncDateAndTime } from './common';
export var CalendarType;
(function (CalendarType) {
CalendarType[CalendarType["PURE_GREGORIAN"] = 0] = "PURE_GREGORIAN";
CalendarType[CalendarType["PURE_JULIAN"] = 1] = "PURE_JULIAN";
})(CalendarType || (CalendarType = {}));
export const GREGORIAN_CHANGE_MIN_YEAR = 300;
export const GREGORIAN_CHANGE_MAX_YEAR = 3900;
export const SUNDAY = 0;
export const MONDAY = 1;
export const TUESDAY = 2;
export const WEDNESDAY = 3;
export const THURSDAY = 4;
export const FRIDAY = 5;
export const SATURDAY = 6;
// noinspection JSUnusedGlobalSymbols
export var DayOfWeek;
(function (DayOfWeek) {
DayOfWeek[DayOfWeek["SUNDAY"] = 0] = "SUNDAY";
DayOfWeek[DayOfWeek["MONDAY"] = 1] = "MONDAY";
DayOfWeek[DayOfWeek["TUESDAY"] = 2] = "TUESDAY";
DayOfWeek[DayOfWeek["WEDNESDAY"] = 3] = "WEDNESDAY";
DayOfWeek[DayOfWeek["THURSDAY"] = 4] = "THURSDAY";
DayOfWeek[DayOfWeek["FRIDAY"] = 5] = "FRIDAY";
DayOfWeek[DayOfWeek["SATURDAY"] = 6] = "SATURDAY";
})(DayOfWeek || (DayOfWeek = {}));
// noinspection JSUnusedGlobalSymbols
export var Month;
(function (Month) {
Month[Month["JANUARY"] = 1] = "JANUARY";
Month[Month["FEBRUARY"] = 2] = "FEBRUARY";
Month[Month["MARCH"] = 3] = "MARCH";
Month[Month["APRIL"] = 4] = "APRIL";
Month[Month["MAY"] = 5] = "MAY";
Month[Month["JUNE"] = 6] = "JUNE";
Month[Month["JULY"] = 7] = "JULY";
Month[Month["AUGUST"] = 8] = "AUGUST";
Month[Month["SEPTEMBER"] = 9] = "SEPTEMBER";
Month[Month["OCTOBER"] = 10] = "OCTOBER";
Month[Month["NOVEMBER"] = 11] = "NOVEMBER";
Month[Month["DECEMBER"] = 12] = "DECEMBER";
})(Month || (Month = {}));
/**
* Constant for indicating the last occurrence of a particular day of the week (e.g. the last Tuesday) of a given month.
*/
export const LAST = 6;
/** @hidden */
const DISTANT_YEAR_PAST = -9999999;
/** @hidden */
const DISTANT_YEAR_FUTURE = 9999999;
const FIRST_GREGORIAN_DAY_SGC = -141427; // 1582-10-15
function hasYearField(obj) {
return obj.y != null || obj.yw != null || obj.ywl != null ||
obj.year != null || obj.yearByWeek != null || obj.yearByWeekLocale != null;
}
/** @hidden */
export function isGregorianType(obj) {
return isNumber(obj) ||
(isArray(obj) && obj.length === 3 && obj.findIndex(n => !isNumber(n)) < 0) ||
(isString(obj) && /^(g|j|(\d+)-(\d+)-(\d+)|\d{8})$/i.test(obj)) ||
(isObject(obj) && hasYearField(obj));
}
const lockError = new Error('This DateTime instance is locked and immutable');
/** @hidden */
export function handleVariableDateArgs(yearOrDate, month, day, calendar, ignoreJ = false) {
let n;
let j;
let year;
let dy;
if (isNumber(yearOrDate))
year = yearOrDate;
else if (isArray(yearOrDate) && yearOrDate.length >= 3 && isNumber(yearOrDate[0]))
return yearOrDate;
else if (isObject(yearOrDate)) {
syncDateAndTime(yearOrDate);
n = yearOrDate.n;
j = ignoreJ ? undefined : yearOrDate.j;
year = yearOrDate.y;
dy = yearOrDate.dy;
month = yearOrDate.m;
day = yearOrDate.d;
}
if (year != null) {
if (month == null && day == null && dy != null) {
if (calendar === 'g' || j === false)
return handleVariableDateArgs(getDateFromDayNumberGregorian(getDayNumberGregorian(year, 1, 0) + dy));
else if (calendar === 'j' || j === true)
return handleVariableDateArgs(getDateFromDayNumberJulian(getDayNumberJulian(year, 1, 0) + dy));
else if (calendar) {
++calendar.computeWeekValues;
const ymd = handleVariableDateArgs(calendar.getDateFromDayNumber(calendar.getDayNumber(year, 1, 0) + dy));
--calendar.computeWeekValues;
return ymd;
}
else
return handleVariableDateArgs(getDateFromDayNumber_SGC(getDayNumber_SGC(year, 1, 0) + dy));
}
else {
month = month !== null && month !== void 0 ? month : 1;
day = day !== null && day !== void 0 ? day : 1;
}
}
else if (n != null) {
if (calendar === 'g' || j === false)
return handleVariableDateArgs(getDateFromDayNumberGregorian(n));
else if (calendar === 'j' || j === true)
return handleVariableDateArgs(getDateFromDayNumberJulian(n));
else if (calendar) {
++calendar.computeWeekValues;
const ymd = handleVariableDateArgs(calendar.getDateFromDayNumber(n));
--calendar.computeWeekValues;
return ymd;
}
else
return handleVariableDateArgs(getDateFromDayNumber_SGC(n));
}
else
throw new Error('Calendar: Invalid date arguments');
return [year, month, day, j == null ? -1 : +j];
}
/**
* Determine if a given date falls during the Julian calendar or the Gregorian calendar, given the standard
* Gregorian change date of 1582-10-15.
*
* @param {YearOrDate} yearOrDate
* @param {number} month
* @param {number} day
* @returns True if the date is Julian.
*/
export function isJulianCalendarDate_SGC(yearOrDate, month, day) {
let year, j;
[year, month, day, j] = handleVariableDateArgs(yearOrDate, month, day);
return (j === 1 || year < 1582 || (year === 1582 && (month < 10 || month === 10 && day < 15)));
}
/**
* Gets the day number for the given date, relative to 1970-01-01, using the standard Gregorian change date 1582-10-15.
* @param yearOrDate
* @param month
* @param day
* @returns Day number.
*/
export function getDayNumber_SGC(yearOrDate, month, day) {
let year, j;
[year, month, day, j] = handleVariableDateArgs(yearOrDate, month, day);
while (month < 1) {
month += 12;
--year;
}
while (month > 12) {
month -= 12;
++year;
}
if (j === 1 || (j !== 0 && isJulianCalendarDate_SGC(year, month, day)))
return getDayNumberJulian(year, month, day);
else
return getDayNumberGregorian(year, month, day);
}
/**
* Gets the day number for the given Gregorian calendar date, relative to 1970-01-01.
* @param yearOrDate
* @param month
* @param day
* @returns Day number.
*/
export function getDayNumberGregorian(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, 'g');
while (month < 1) {
month += 12;
--year;
}
while (month > 12) {
month -= 12;
++year;
}
return 367 * year - div_rd(7 * (year + div_tt0(month + 9, 12)), 4) - div_tt0(3 * (div_tt0(year + div_tt0(month - 9, 7), 100) + 1), 4) +
div_tt0(275 * month, 9) + day - 719559;
}
/**
* Gets the day number for the given Julian calendar date, relative to 1970-01-01 Gregorian.
* @param yearOrDate
* @param month
* @param day
* @returns Day number.
*/
export function getDayNumberJulian(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, 'j');
while (month < 1) {
month += 12;
--year;
}
while (month > 12) {
month -= 12;
++year;
}
return 367 * year - div_rd(7 * (year + div_tt0(month + 9, 12)), 4) + div_tt0(275 * month, 9) + day - 719561;
}
// noinspection JSUnusedLocalSymbols
/**
* Always returns 1. This function exists only to parallel getFirstDateInMonth, which isn't always 1 when the
* Gregorian change date is not fixed.
* @returns First date of calendar month.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function getFirstDateInMonth_SGC(year, month) {
return 1;
}
/**
* The last date of the given calendar month, using the standard Gregorian change date 1582-10-15, e.g. 31 for
* any January, 28 for non-leap-year February, 29 for leap-year February, etc.
* @param year
* @param month
* @returns Last date of calendar month.
*/
export function getLastDateInMonth_SGC(year, month) {
if (month === 9 || month === 4 || month === 6 || month === 11)
return 30;
else if (month !== 2)
return 31; // Works for pseudo-months 0 and 13 as well.
else if (year % 4 === 0 && (year < 1583 || year % 100 !== 0 || year % 400 === 0))
return 29;
else
return 28;
}
/**
* The last date of the given Gregorian calendar month.
* @param year
* @param month
* @returns Last date of calendar month.
*/
export function getLastDateInMonthGregorian(year, month) {
if (month === 9 || month === 4 || month === 6 || month === 11)
return 30;
else if (month !== 2)
return 31; // Works for pseudo-months 0 and 13 as well.
else if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0))
return 29;
else
return 28;
}
/**
* The last date of the given Gregorian calendar month.
* @param year
* @param month
* @returns Last date of calendar month.
*/
export function getLastDateInMonthJulian(year, month) {
if (month === 9 || month === 4 || month === 6 || month === 11)
return 30;
else if (month !== 2)
return 31; // Works for pseudo-months 0 and 13 as well.
else if (year % 4 === 0)
return 29;
else
return 28;
}
/**
* Returns the number of days in the given calendar month. Since this
* function is for the standard Gregorian change date of 1582-10-15,
* it returns 21 for 1582/10, otherwise it returns the same value as
* [[getLastDateInMonth_SGC]].
* @param year
* @param month
* @returns Total number of days in the given month.
*/
export function getDaysInMonth_SGC(year, month) {
if (year === 1582 && month === 10)
return 21;
else if (month === 9 || month === 4 || month === 6 || month === 11)
return 30;
else if (month !== 2)
return 31; // Works for pseudo-months 0 and 13 as well.
else
return getDayNumber_SGC(year, 3, 1) - getDayNumber_SGC(year, 2, 1);
}
/**
* This typically returns 365, or 366 for a leap year, but for the year
* 1582 it returns 355.
* @param year
* @returns Total number of days in the given year.
*/
export function getDaysInYear_SGC(year) {
return getDayNumber_SGC(year + 1, 1, 1) - getDayNumber_SGC(year, 1, 1);
}
/**
* Get day of week for a given 1970-01-01-based day number.
* @param dayNum 1970-01-01-based day number.
* @return Day of week as 0-6: 0 for Sunday, 1 for Monday... 6 for Saturday.
*/
export function getDayOfWeek(dayNum) {
return mod(dayNum + 4, 7);
}
/**
* Get day of week for a given date, assuming standard Gregorian change.
* @param yearOrDateOrDayNum 1970-01-01-based day number (month and date must be left undefined) - OR -
* YMDDate form y/m/d - OR - [y, m, d].
* @param month
* @param day
* @return Day of week as 0-6: 0 for Sunday, 1 for Monday... 6 for Saturday.
*/
export function getDayOfWeek_SGC(yearOrDateOrDayNum, month, day) {
if (isNumber(yearOrDateOrDayNum) && month == null)
return mod(yearOrDateOrDayNum + 4, 7);
else
return getDayOfWeek(getDayNumber_SGC(yearOrDateOrDayNum, month, day));
}
/**
* Get the date of the index-th day of the week of a given month, e.g. the date of the
* first Wednesday or the third Monday or the last Friday of the month.
* @param year Year.
* @param month Month.
* @param dayOfTheWeek The day of the week (e.g. 0 for Sunday, 2 for Tuesday, 6 for Saturday) for
* which you wish to find the date.
* @param index A value of 1-5, or LAST (6), for the occurrence of the specified day of the week.
* @return 0 if the described day does not exist (e.g. there is no fifth Monday in the given month) or
* the date of the specified day.
*/
export function getDateOfNthWeekdayOfMonth_SGC(year, month, dayOfTheWeek, index) {
const last = (index >= LAST);
const day = 1;
let dayNum = getDayNumber_SGC(year, month, day);
const dayOfWeek = getDayOfWeek(dayNum);
let ymd;
let lastDay = 0;
if (dayOfWeek === dayOfTheWeek && index === 1)
return day;
dayNum += mod(dayOfTheWeek - dayOfWeek, 7);
ymd = getDateFromDayNumber_SGC(dayNum);
while (ymd.m === month) {
lastDay = ymd.d;
if (--index === 0)
return lastDay;
dayNum += 7;
ymd = getDateFromDayNumber_SGC(dayNum);
}
if (last)
return lastDay;
else
return 0;
}
export function getDayOfWeekInMonthCount_SGC(year, month, dayOfTheWeek) {
const firstDay = getDayNumber_SGC(year, month, getDateOfNthWeekdayOfMonth_SGC(year, month, dayOfTheWeek, 1));
const nextMonth = getDayNumber_SGC(year, month + 1, 1);
return div_tt0(nextMonth - firstDay - 1, 7) + 1;
}
export function getDayOnOrAfter_SGC(year, month, dayOfTheWeek, minDate) {
const dayNum = getDayNumber_SGC(year, month, minDate);
const dayOfWeek = getDayOfWeek(dayNum);
const delta = mod(dayOfTheWeek - dayOfWeek, 7);
if (year === 1582 && month === 10) {
const ymd = getDateFromDayNumber_SGC(dayNum + delta);
if (ymd.y !== year || ymd.m !== month)
minDate = 0;
else
minDate = ymd.d;
}
else {
minDate += delta;
if (minDate > getLastDateInMonth_SGC(year, month))
minDate = 0;
}
return minDate;
}
export function getDayOnOrBefore_SGC(year, month, dayOfTheWeek, maxDate) {
const dayNum = getDayNumber_SGC(year, month, maxDate);
const dayOfWeek = getDayOfWeek(dayNum);
const delta = mod(dayOfWeek - dayOfTheWeek, 7);
if (year === 1582 && month === 10) {
const ymd = getDateFromDayNumber_SGC(dayNum - delta);
if (ymd.y !== year || ymd.m !== month)
maxDate = 0;
else
maxDate = ymd.d;
}
else {
maxDate -= delta;
if (maxDate < 0)
maxDate = 0;
}
return maxDate;
}
export function addDaysToDate_SGC(deltaDays, yearOrDate, month, day) {
return getDateFromDayNumber_SGC(getDayNumber_SGC(yearOrDate, month, day) + deltaDays);
}
export function getDateFromDayNumber_SGC(dayNum) {
if (dayNum >= FIRST_GREGORIAN_DAY_SGC)
return getDateFromDayNumberGregorian(dayNum);
else
return getDateFromDayNumberJulian(dayNum);
}
export function getDateFromDayNumberGregorian(dayNum) {
let year;
let month;
let day;
let dayOfYear;
let lastDay;
year = Math.floor((dayNum + 719528) / 365.2425);
while (dayNum < getDayNumberGregorian(year, 1, 1))
--year;
while (dayNum >= getDayNumberGregorian(year + 1, 1, 1))
++year;
day = dayOfYear = dayNum - getDayNumberGregorian(year, 1, 1) + 1;
for (month = 1; day > (lastDay = getLastDateInMonthGregorian(year, month)); ++month)
day -= lastDay;
return syncDateAndTime({ y: year, m: month, d: day, dy: dayOfYear, n: dayNum, j: false });
}
export function getDateFromDayNumberJulian(dayNum) {
let year;
let month;
let day;
let lastDay;
year = Math.floor((dayNum + 719530) / 365.25);
while (dayNum < getDayNumberJulian(year, 1, 1))
--year;
while (dayNum >= getDayNumberJulian(year + 1, 1, 1))
++year;
day = dayNum - getDayNumberJulian(year, 1, 1) + 1;
for (month = 1; day > (lastDay = getLastDateInMonthJulian(year, month)); ++month)
day -= lastDay;
return syncDateAndTime({ y: year, m: month, d: day, n: dayNum, j: true });
}
export function millisFromDateTime_SGC(year, month, day, hour, minute, second, millis) {
millis = millis || 0;
second = second || 0;
return millis +
second * 1000 +
minute * MINUTE_MSEC +
hour * HOUR_MSEC +
getDayNumber_SGC(year, month, day) * DAY_MSEC;
}
export function dateAndTimeFromMillis_SGC(ticks) {
const wallTime = getDateFromDayNumber_SGC(div_rd(ticks, DAY_MSEC));
wallTime.millis = mod(ticks, 1000);
ticks = div_rd(ticks, 1000);
wallTime.sec = mod(ticks, 60);
ticks = div_rd(ticks, 60);
wallTime.min = mod(ticks, 60);
ticks = div_rd(ticks, 60);
wallTime.hrs = mod(ticks, 24);
wallTime.utcOffset = 0;
wallTime.dstOffset = 0;
wallTime.occurrence = 1;
return syncDateAndTime(wallTime);
}
export function isValidDate_SGC(yearOrDate, month, day) {
const [y, m, d, j] = handleVariableDateArgs(yearOrDate, month, day);
let ymd;
const n = getDayNumber_SGC({ y, m, d, j: j < 0 ? null : !!j });
if (j < 0)
ymd = getDateFromDayNumber_SGC(n);
else if (j === 0)
ymd = getDateFromDayNumberGregorian(n);
else
ymd = getDateFromDayNumberJulian(n);
return (y === ymd.y && m === ymd.m && d === ymd.d);
}
export function isValidDateGregorian(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, 'g');
const ymd = getDateFromDayNumberGregorian(getDayNumberGregorian(year, month, day));
return (year === ymd.y && month === ymd.m && day === ymd.d);
}
export function isValidDateJulian(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, 'j');
const ymd = getDateFromDayNumberJulian(getDayNumberJulian(year, month, day));
return (year === ymd.y && month === ymd.m && day === ymd.d);
}
export function getISOFormatDate(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day);
const yyyy = (year < 0 ? '-' : '') + padLeft(Math.abs(year), 4, '0');
const mm = padLeft(month, 2, '0');
const dd = padLeft(day, 2, '0');
return yyyy + '-' + mm + '-' + dd;
}
export function parseISODate(date) {
var _a, _b;
let sign = 1;
date = date.trim();
if (date.startsWith('-')) {
sign = -1;
date = date.substring(1).trim();
}
let $ = /^(\d+)-(\d{1,2}(?=\D))(?:-(\d+))?$/.exec(date);
if (!$)
$ = /^(\d{1,5})$/.exec(date);
if (!$)
$ = /^(\d{4,})(\d\d)(\d\d)$/.exec(date);
if (!$)
throw new Error('Invalid ISO date');
return syncDateAndTime({ y: Number($[1]) * sign, m: Number((_a = $[2]) !== null && _a !== void 0 ? _a : 1), d: Number((_b = $[3]) !== null && _b !== void 0 ? _b : 1) });
}
export class Calendar {
constructor(gcYearOrDateOrType, gcMonth, gcDate) {
this.gcYear = 1582;
this.gcMonth = 10;
this.gcDate = 15;
this.firstGregorianDay = FIRST_GREGORIAN_DAY_SGC;
this.firstDateInGCChangeMonth = 1;
this.lengthOfGCChangeMonth = 21;
this.lastJulianYear = Number.MIN_SAFE_INTEGER;
this.lastJulianMonth = Number.MIN_SAFE_INTEGER;
this.lastJulianDate = 4;
this._locked = false;
this.lock = () => this._lock();
this.computeWeekValues = 0; // To prevent infinite recursion, compute week values only when this is 0.
if (gcYearOrDateOrType === CalendarType.PURE_GREGORIAN)
this.setGregorianChange(DISTANT_YEAR_PAST, 0, 0);
else if (gcYearOrDateOrType === CalendarType.PURE_JULIAN)
this.setGregorianChange(DISTANT_YEAR_FUTURE, 0, 0);
else if (arguments.length === 0 || gcYearOrDateOrType == null)
this.setGregorianChange(1582, 10, 15);
else
this.setGregorianChange(gcYearOrDateOrType, gcMonth, gcDate);
}
_lock(doLock = true) {
this._locked = this._locked || doLock;
return this;
}
get locked() { return this._locked; }
setPureGregorian(pureGregorian) {
if (this.locked)
throw lockError;
if (pureGregorian)
this.setGregorianChange(DISTANT_YEAR_PAST, 0, 0);
else
this.setGregorianChange(1582, 10, 15);
return this;
}
isPureGregorian() {
return (this.gcYear <= DISTANT_YEAR_PAST);
}
setPureJulian(pureJulian) {
if (this.locked)
throw lockError;
if (pureJulian)
this.setGregorianChange(DISTANT_YEAR_FUTURE, 0, 0);
else
this.setGregorianChange(1582, 10, 15);
return this;
}
isPureJulian() {
return (this.gcYear >= DISTANT_YEAR_FUTURE);
}
setGregorianChange(gcYearOrDate, gcMonth, gcDate) {
if (this.locked)
throw lockError;
if (gcYearOrDate === 'g' || gcYearOrDate === 'G') {
this.setPureGregorian(true);
return this;
}
else if (gcYearOrDate === 'j' || gcYearOrDate === 'J') {
this.setPureJulian(true);
return this;
}
else if (isString(gcYearOrDate))
gcYearOrDate = parseISODate(gcYearOrDate);
else if (isObject(gcYearOrDate) && !isArray(gcYearOrDate) && (gcYearOrDate.y == null ||
gcYearOrDate.m == null || gcYearOrDate.d == null || gcYearOrDate.j))
throw new Error('Gregorian change date must be an explicit non-Julian y-m-d date');
let gcYear;
[gcYear, gcMonth, gcDate] = handleVariableDateArgs(gcYearOrDate, gcMonth, gcDate, this);
if (gcYear < GREGORIAN_CHANGE_MIN_YEAR) {
if ((gcMonth !== 0 || gcDate !== 0) && gcYear > DISTANT_YEAR_PAST)
throw new Error('Calendar: Gregorian change year cannot be less than ' + GREGORIAN_CHANGE_MIN_YEAR);
this.firstGregorianDay = Number.MIN_SAFE_INTEGER;
this.gcYear = DISTANT_YEAR_PAST;
}
else if (gcYear > GREGORIAN_CHANGE_MAX_YEAR) {
if ((gcMonth !== 0 || gcDate !== 0) && gcYear < DISTANT_YEAR_FUTURE)
throw new Error('Calendar: Gregorian change year cannot be greater than ' + GREGORIAN_CHANGE_MAX_YEAR);
this.firstGregorianDay = Number.MAX_SAFE_INTEGER;
this.gcYear = DISTANT_YEAR_FUTURE;
}
else if (!isValidDateGregorian(gcYear, gcMonth, gcDate))
throw new Error('Calendar: Invalid Gregorian date: ' + getISOFormatDate(gcYear, gcMonth, gcDate));
this.gcYear = gcYear;
this.gcMonth = gcMonth;
this.gcDate = gcDate;
this.firstGregorianDay = getDayNumberGregorian(gcYear, gcMonth, gcDate);
const lastJDay = getDateFromDayNumberJulian(this.firstGregorianDay - 1);
this.lastJulianDate = lastJDay.d;
this.lengthOfGCChangeMonth = getLastDateInMonthGregorian(gcYear, gcMonth);
if (lastJDay.y === gcYear && lastJDay.m === gcMonth) {
this.lastJulianYear = Number.MIN_SAFE_INTEGER; // Number.MIN_SAFE_INTEGER used to indicate mixed Julian/Gregorian transition month
this.lastJulianMonth = Number.MIN_SAFE_INTEGER;
this.firstDateInGCChangeMonth = 1;
this.lengthOfGCChangeMonth -= gcDate - this.lastJulianDate - 1;
}
else {
this.lastJulianYear = lastJDay.y;
this.lastJulianMonth = lastJDay.m;
this.firstDateInGCChangeMonth = gcDate;
this.lengthOfGCChangeMonth -= gcDate - 1;
}
return this;
}
getGregorianChange() {
return syncDateAndTime({ y: this.gcYear, m: this.gcMonth, d: this.gcDate, n: this.firstGregorianDay, j: false });
}
isJulianCalendarDate(yearOrDate, month, day) {
let year, j;
[year, month, day, j] = handleVariableDateArgs(yearOrDate, month, day, this);
return (j === 1 || year < this.gcYear || (year === this.gcYear && (month < this.gcMonth ||
month === this.gcMonth && day < this.gcDate)));
}
getDayNumber(yearOrDate, month, day) {
var _a, _b, _c;
// Note: month/day can be used internally to pass startOfWeek/minDaysInWeek.
if (isObject(yearOrDate) && !isArray(yearOrDate)) {
syncDateAndTime(yearOrDate);
if (yearOrDate.y == null && (yearOrDate.yw != null || yearOrDate.ywl != null)) {
const localeWeek = (yearOrDate.ywl != null);
const year = (_a = yearOrDate.ywl) !== null && _a !== void 0 ? _a : yearOrDate.yw;
const startOfWeek = (localeWeek && month != null ? month : 1);
const minDaysInWeek = (localeWeek && day != null ? day : 4);
const week = (_b = (localeWeek ? yearOrDate.wl : yearOrDate.w)) !== null && _b !== void 0 ? _b : 1;
const dayOfWeek = (_c = (localeWeek ? yearOrDate.dwl : yearOrDate.dw)) !== null && _c !== void 0 ? _c : 1;
++this.computeWeekValues;
const w = this.getStartDateOfFirstWeekOfYear(year, startOfWeek, minDaysInWeek);
const dayNum = w.n + (week - 1) * 7 + dayOfWeek - 1;
yearOrDate = this.getDateFromDayNumber(dayNum);
--this.computeWeekValues;
}
else if (yearOrDate.y != null && yearOrDate.m == null && yearOrDate.dy != null)
yearOrDate = this.addDaysToDate(yearOrDate.dy - 1, { y: yearOrDate.y, m: 1, d: 1 });
}
let year, j;
[year, month, day, j] = handleVariableDateArgs(yearOrDate, month, day, this);
while (month < 1) {
month += 12;
--year;
}
while (month > 12) {
month -= 12;
++year;
}
if (j < 0) {
if (year === this.lastJulianYear && month === this.lastJulianMonth) {
if (day > this.lastJulianDate)
day = this.lastJulianDate;
}
else if (year === this.gcYear && month === this.gcMonth && (day > this.lastJulianDate ||
(this.lastJulianMonth !== this.gcMonth && this.lastJulianMonth > 0)) && day < this.gcDate) {
day = this.gcDate;
}
}
if (j === 1 || (j !== 0 && this.isJulianCalendarDate(year, month, day)))
return getDayNumberJulian(year, month, day);
else
return getDayNumberGregorian(year, month, day);
}
/** @hidden */
getDateFromDayNumber(dayNum, startingDayOfWeek, minDaysInCalendarYear) {
let result;
if (dayNum >= this.firstGregorianDay)
result = getDateFromDayNumberGregorian(dayNum);
else
result = getDateFromDayNumberJulian(dayNum);
if (this.computeWeekValues === 0)
[result.yw, result.w, result.dw] = this.getYearWeekAndWeekday(result, startingDayOfWeek, minDaysInCalendarYear);
return syncDateAndTime(result);
}
getFirstDateInMonth(year, month) {
if (year === this.gcYear && month === this.gcMonth)
return this.firstDateInGCChangeMonth;
else
return 1;
}
getLastDateInMonth(year, month) {
if (month === 0) {
month = 12;
--year;
}
else if (month === 13) {
month = 1;
++year;
}
if (year === this.lastJulianYear && month === this.lastJulianMonth)
return this.lastJulianDate;
else if (month === 9 || month === 4 || month === 6 || month === 11)
return 30;
else if (month !== 2)
return 31;
else if (year % 4 === 0 && (year < this.gcYear || (year === this.gcYear && this.gcMonth > 2) || year % 100 !== 0 || year % 400 === 0))
return 29;
else
return 28;
}
getDaysInMonth(year, month) {
if (month === 0) {
month = 12;
--year;
}
else if (month === 13) {
month = 1;
++year;
}
if (year === this.gcYear && month === this.gcMonth)
return this.lengthOfGCChangeMonth;
else if (year === this.lastJulianYear && month === this.lastJulianMonth)
return this.lastJulianDate;
else if (month === 9 || month === 4 || month === 6 || month === 11)
return 30;
else if (month !== 2)
return 31;
else
return this.getDayNumber(year, 3, 1) - this.getDayNumber(year, 2, 1);
}
getDaysInYear(year) {
return this.getDayNumber(year + 1, 1, 1) - this.getDayNumber(year, 1, 1);
}
getDayOfWeek(yearOrDateOrDayNum, month, day) {
if (isNumber(yearOrDateOrDayNum) && month == null)
return getDayOfWeek(yearOrDateOrDayNum);
else
return getDayOfWeek(this.getDayNumber(yearOrDateOrDayNum, month, day));
}
/**
* @description Get the date of the index-th day of the week of a given month, e.g. the date of the
* first Wednesday or the third Monday or the last Friday of the month.
*
* @param {number} year - Year.
* @param {number} month - Month.
* @param {number} dayOfTheWeek - The day of the week (e.g. 0 for Sunday, 2 for Tuesday, 6 for Saturday) for
* which you wish to find the date.
* @param {number} index - A value of 1-5, or LAST (6), for the occurrence of the specified day of the week.
*
* @return {number} 0 if the described day does not exist (e.g. there is no fifth Monday in the given month) or
* the date of the specified day.
*/
getDateOfNthWeekdayOfMonth(year, month, dayOfTheWeek, index) {
const last = (index >= LAST);
const day = 1;
let dayNum = this.getDayNumber(year, month, day);
const dayOfWeek = getDayOfWeek(dayNum);
let ymd;
let lastDay = 0;
if (dayOfWeek === dayOfTheWeek && index === 1)
return day;
dayNum += mod(dayOfTheWeek - dayOfWeek, 7);
ymd = this.getDateFromDayNumber(dayNum);
while (ymd.m === month) {
lastDay = ymd.d;
if (--index === 0)
return lastDay;
dayNum += 7;
ymd = this.getDateFromDayNumber(dayNum);
}
if (last)
return lastDay;
else
return 0;
}
getDayOfWeekInMonthCount(year, month, dayOfTheWeek) {
const firstDay = this.getDayNumber(year, month, this.getDateOfNthWeekdayOfMonth(year, month, dayOfTheWeek, 1));
const nextMonth = this.getDayNumber(year, month + 1, 1);
return div_tt0(nextMonth - firstDay - 1, 7) + 1;
}
getDayOfWeekInMonthIndex(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, this);
const firstDay = this.getDayNumber(year, month, 1);
const dayNumber = this.getDayNumber(year, month, day);
return div_rd(dayNumber - firstDay, 7) + 1;
}
getDayOnOrAfter(year, month, dayOfTheWeek, minDate) {
const dayNum = this.getDayNumber(year, month, minDate);
const dayOfWeek = getDayOfWeek(dayNum);
const delta = mod(dayOfTheWeek - dayOfWeek, 7);
if (year === this.gcYear && month === this.gcDate) {
const ymd = this.getDateFromDayNumber(dayNum + delta);
if (ymd.y !== year || ymd.m !== month)
minDate = 0;
else
minDate = ymd.d;
}
else {
minDate += delta;
if (minDate > this.getLastDateInMonth(year, month))
minDate = 0;
}
return minDate;
}
getDayOnOrBefore(year, month, dayOfTheWeek, maxDate) {
const dayNum = this.getDayNumber(year, month, maxDate);
const dayOfWeek = getDayOfWeek(dayNum);
const delta = mod(dayOfWeek - dayOfTheWeek, 7);
if (year === this.gcYear && month === this.gcDate) {
const ymd = this.getDateFromDayNumber(dayNum - delta);
if (ymd.y !== year || ymd.m !== month)
maxDate = 0;
else
maxDate = ymd.d;
}
else {
maxDate -= delta;
if (maxDate < 0)
maxDate = 0;
}
return maxDate;
}
addDaysToDate(deltaDays, yearOrDate, month, day) {
return this.getDateFromDayNumber(this.getDayNumber(yearOrDate, month, day) + deltaDays);
}
getCalendarMonth(year, month, startingDayOfWeek) {
startingDayOfWeek = startingDayOfWeek !== null && startingDayOfWeek !== void 0 ? startingDayOfWeek : SUNDAY;
const dates = [];
let dateOffset;
let dayNum = this.getDayNumber(year, month, this.getFirstDateInMonth(year, month));
let ymd;
let currMonth;
// Step back (if necessary) to the nearest prior day matching the requested starting day of the week.
dateOffset = mod(startingDayOfWeek - getDayOfWeek(dayNum), -7); // First time I recall ever wanting to use a negative modulus.
dayNum += dateOffset; // dateOffset will be 0 or negative
ymd = this.getDateFromDayNumber(dayNum, startingDayOfWeek);
// This loop will fill in a calendar month's full set of dates in such a way as to obtain dates which
// should be shown from previous and subsequent months, while also skipping over Julian-to-Gregorian
// calendar switch-over dates.
do {
dates.push(ymd);
++dayNum;
++dateOffset;
ymd = this.getDateFromDayNumber(dayNum);
currMonth = ymd.m;
// We've reached the end of the calendar when we're at a positive date offset, in a different month
// than the requested month, and the day of week is back to the first day of the week of the calendar.
// The first date to meet these criteria is just past the end of the calendar, and is not added to it.
} while (dateOffset < 1 || currMonth === month || getDayOfWeek(dayNum) !== startingDayOfWeek);
return dates;
}
isValidDate(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, this, true);
const ymd = this.getDateFromDayNumber(this.getDayNumber(year, month, day));
return (year === ymd.y && month === ymd.m && day === ymd.d);
}
normalizeDate(yearOrDate, month, day) {
let year;
[year, month, day] = handleVariableDateArgs(yearOrDate, month, day, this, true);
if (month < 1) {
month += 12;
year -= 1;
}
else if (month > 12) {
month -= 12;
year += 1;
}
if (!this.isValidDate(year, month, day)) {
let d;
if (day < (d = this.getFirstDateInMonth(year, month)))
day = d;
else if (day > (d = this.getLastDateInMonth(year, month)))
day = d;
else {
const range = this.getMissingDateRange(year, month);
if (range != null)
day = range[1] + 1;
else
day = d;
}
}
return syncDateAndTime({ y: year, m: month, d: day });
}
getMissingDateRange(year, month) {
if (year === this.lastJulianYear && month === this.lastJulianMonth) {
const lastDate = getLastDateInMonthJulian(year, month);
if (lastDate > this.lastJulianDate)
return [this.lastJulianDate + 1, lastDate];
}
else if (year === this.gcYear && month === this.gcMonth && this.gcDate > 1 && this.gcDate > this.lastJulianDate + 1)
return [this.lastJulianDate + 1, this.gcDate - 1];
return null;
}
getStartDateOfFirstWeekOfYear(year, startingDayOfWeek = 1, minDaysInCalendarYear = 4) {
let day = 1;
// 7 is a special case, where start week is first full week *after* January 1st.
if (minDaysInCalendarYear === 7) {
++day;
--minDaysInCalendarYear;
}
const daysIntoWeek = mod(this.getDayOfWeek(year, 1, day) - startingDayOfWeek, 7);
return this.addDaysToDate(-daysIntoWeek + (daysIntoWeek > 7 - minDaysInCalendarYear ? 7 : 0), year, 1, day);
}
getWeeksInYear(year, startingDayOfWeek = 1, minDaysInCalendarYear = 4) {
const w1 = this.getStartDateOfFirstWeekOfYear(year, startingDayOfWeek, minDaysInCalendarYear);
const w2 = this.getStartDateOfFirstWeekOfYear(year + 1, startingDayOfWeek, minDaysInCalendarYear);
return (w2.n - w1.n) / 7;
}
getYearWeekAndWeekday(yearOrDate, monthOrSDW, dayOrMDiCY, startingDayOfWeek, minDaysInCalendarYear) {
const [year, month, day] = handleVariableDateArgs(yearOrDate, monthOrSDW, dayOrMDiCY, this, true);
if (isObject(yearOrDate)) {
startingDayOfWeek = monthOrSDW;
minDaysInCalendarYear = dayOrMDiCY;
}
startingDayOfWeek = startingDayOfWeek !== null && startingDayOfWeek !== void 0 ? startingDayOfWeek : 1;
minDaysInCalendarYear = minDaysInCalendarYear !== null && minDaysInCalendarYear !== void 0 ? minDaysInCalendarYear : 4;
++this.computeWeekValues;
let resultYear = year;
let w = this.getStartDateOfFirstWeekOfYear(year, startingDayOfWeek, minDaysInCalendarYear);
const w2 = this.getStartDateOfFirstWeekOfYear(year + 1, startingDayOfWeek, minDaysInCalendarYear);
const dayNum = this.getDayNumber(year, month, day);
if (w.n > dayNum) {
w = this.getStartDateOfFirstWeekOfYear(year - 1, startingDayOfWeek, minDaysInCalendarYear);
--resultYear;
}
else if (w2.n <= dayNum) {
w = w2;
++resultYear;
}
--this.computeWeekValues;
return [resultYear, floor((dayNum - w.n) / 7) + 1, mod(dayNum - w.n, 7) + 1];
}
}
//# sourceMappingURL=calendar.js.map