'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var base = require('./base.cjs'); var sexagesimal = require('./sexagesimal.cjs'); var deltat = require('./deltat.cjs'); /** * @copyright 2013 Sonia Keys * @copyright 2016 commenthol * @license MIT * @module julian */ const int = Math.trunc; /** 1582-10-05 Julian Date is 1st Gregorian Date (1582-10-15) */ const GREGORIAN0JD = 2299160.5; const DAYS_OF_YEAR = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; const SECS_OF_DAY = 86400; // 24 * 60 * 60 /** * Base class for CalendarJulian and CalendarGregorian * Respects the start of the Gregorian Calendar at `GREGORIAN0JD` */ class Calendar { /** * @param {number|Date} [year] - If `Date` is given then year, month, day is taken from that. Shortcut to `new Calendar().fromDate(date)` * @param {number} [month] * @param {number} [day] */ constructor (year, month = 1, day = 1) { if (year instanceof Date) { this.fromDate(year); } else { this.year = year; this.month = month; this.day = day; } } getDate () { return { year: this.year, month: this.month, day: Math.floor(this.day) } } getTime () { const t = new sexagesimal["default"].Time(this.day * SECS_OF_DAY); const [neg, h, m, _s] = t.toHMS(); // eslint-disable-line no-unused-vars let [s, ms] = base["default"].modf(_s); ms = Math.trunc(ms * 1000); return { hour: h % 24, minute: m, second: s, millisecond: ms } } toISOString () { const { year, month, day } = this.getDate(); const { hour, minute, second, millisecond } = this.getTime(); return `${pad(year, 4)}-${pad(month)}-${pad(day)}T` + `${pad(hour)}:${pad(minute)}:${pad(second)}.${pad(millisecond, 3)}Z` } isGregorian () { return isCalendarGregorian(this.year, this.month, this.day) } /** * Note: Take care for dates < GREGORIAN0JD as `date` is always within the * proleptic Gregorian Calender * @param {Date} date - proleptic Gregorian date */ fromDate (date) { this.year = date.getUTCFullYear(); this.month = date.getUTCMonth() + 1; const day = date.getUTCDate(); const hour = date.getUTCHours(); const minute = date.getUTCMinutes(); const second = date.getUTCSeconds(); const ms = date.getMilliseconds(); this.day = day + (hour + ((minute + ((second + ms / 1000) / 60)) / 60)) / 24; return this } /** * Note: Take care for dates < GREGORIAN0JD as `date` is always within the * proleptic Gregorian Calender * @returns {Date} proleptic Gregorian date */ toDate () { const [day, fhour] = base["default"].modf(this.day); const [hour, fminute] = base["default"].modf(fhour * 24); const [minute, fsecond] = base["default"].modf(fminute * 60); const [second, fms] = base["default"].modf(fsecond * 60); const date = new Date(Date.UTC( this.year, this.month - 1, day, hour, minute, second, Math.round(fms * 1000) )); date.setUTCFullYear(this.year); return date } /** * converts a calendar date to decimal year * @returns {number} decimal year */ toYear () { const [d, f] = base["default"].modf(this.day); // eslint-disable-line no-unused-vars const n = this.dayOfYear() - 1 + f; const days = this.isLeapYear() ? 366 : 365; const decYear = this.year + (n / days); return decYear } /** * converts a decimal year to a calendar date * @param {number} year - decimal year */ fromYear (year) { const [y, f] = base["default"].modf(year); this.year = y; const days = this.isLeapYear() ? 366 : 365; const dayOfYear = base["default"].round(f * days, 5); let m = 12; while (m > 0 && DAYS_OF_YEAR[m] > dayOfYear) { m--; } this.month = m; this.day = 1 + dayOfYear - DAYS_OF_YEAR[this.month]; return this } isLeapYear () { if (this.isGregorian()) { return LeapYearGregorian(this.year) } else { return LeapYearJulian(this.year) } } toJD () { return CalendarToJD(this.year, this.month, this.day, !this.isGregorian()) } fromJD (jd) { const isJulian = !isJDCalendarGregorian(jd); const { year, month, day } = JDToCalendar(jd, isJulian); this.year = year; this.month = month; this.day = day; return this } fromJDE (jde) { this.fromJD(jde); const dT = deltat["default"].deltaT(this.toYear()); // in seconds this.day -= dT / 86400; return this } toJDE () { const dT = deltat["default"].deltaT(this.toYear()); // in seconds this.day += dT / 86400; return this.toJD() } /** * set date to midnight UTC */ midnight () { this.day = Math.floor(this.day); return this } /** * set date to noon UTC */ noon () { this.day = Math.floor(this.day) + 0.5; return this } /** * @param {Boolean} td - if `true` calendar instance is in TD; date gets converted to UT * true - `UT = TD - ΔT` * false - `TD = UT + ΔT` */ deltaT (td) { const dT = deltat["default"].deltaT(this.toYear()); // in seconds if (td) { this.day -= dT / 86400; } else { this.day += dT / 86400; } return this } dayOfWeek () { return DayOfWeek(this.toJD()) } dayOfYear () { if (this.isGregorian()) { return DayOfYearGregorian(this.year, this.month, this.day) } else { return DayOfYearJulian(this.year, this.month, this.day) } } } class CalendarJulian extends Calendar { toJD () { return CalendarJulianToJD(this.year, this.month, this.day) } fromJD (jd) { const { year, month, day } = JDToCalendarJulian(jd); this.year = year; this.month = month; this.day = day; return this } isLeapYear () { return LeapYearJulian(this.year) } dayOfYear () { return DayOfYearJulian(this.year, this.month, this.day) } /** * toGregorian converts a Julian calendar date to a year, month, and day * in the Gregorian calendar. * @returns {CalendarGregorian} */ toGregorian () { const jd = this.toJD(); return new CalendarGregorian().fromJD(jd) } } class CalendarGregorian extends Calendar { toJD () { return CalendarGregorianToJD(this.year, this.month, this.day) } fromJD (jd) { const { year, month, day } = JDToCalendarGregorian(jd); this.year = year; this.month = month; this.day = day; return this } isLeapYear () { return LeapYearGregorian(this.year) } dayOfYear () { return DayOfYearGregorian(this.year, this.month, this.day) } /* * toJulian converts a Gregorian calendar date to a year, month, and day * in the Julian calendar. * @returns {CalendarJulian} */ toJulian () { const jd = this.toJD(); return new CalendarJulian().fromJD(jd) } } // ----------------------------------------------------------------------------- /** * base conversion from calendar date to julian day */ function CalendarToJD (y, m, d, isJulian) { let b = 0; if (m < 3) { y--; m += 12; } if (!isJulian) { const a = base["default"].floorDiv(y, 100); b = 2 - a + base["default"].floorDiv(a, 4); } // (7.1) p. 61 const jd = (base["default"].floorDiv(36525 * (int(y + 4716)), 100)) + (base["default"].floorDiv(306 * (m + 1), 10) + b) + d - 1524.5; return jd } /** * CalendarGregorianToJD converts a Gregorian year, month, and day of month * to Julian day. * * Negative years are valid, back to JD 0. The result is not valid for * dates before JD 0. * @param {number} y - year (int) * @param {number} m - month (int) * @param {number} d - day (float) * @returns {number} jd - Julian day (float) */ function CalendarGregorianToJD (y, m, d) { return CalendarToJD(y, m, d, false) } /** * CalendarJulianToJD converts a Julian year, month, and day of month to Julian day. * * Negative years are valid, back to JD 0. The result is not valid for * dates before JD 0. * @param {number} y - year (int) * @param {number} m - month (int) * @param {number} d - day (float) * @returns {number} jd - Julian day (float) */ function CalendarJulianToJD (y, m, d) { return CalendarToJD(y, m, d, true) } /** * LeapYearJulian returns true if year y in the Julian calendar is a leap year. * @param {number} y - year (int) * @returns {boolean} true if leap year in Julian Calendar */ function LeapYearJulian (y) { return y % 4 === 0 } /** * LeapYearGregorian returns true if year y in the Gregorian calendar is a leap year. * @param {number} y - year (int) * @returns {boolean} true if leap year in Gregorian Calendar */ function LeapYearGregorian (y) { return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 } /** * JDToCalendar returns the calendar date for the given jd. * * Note that this function returns a date in either the Julian or Gregorian * Calendar, as appropriate. * @param {number} jd - Julian day (float) * @param {boolean} isJulian - set true for Julian Calendar, otherwise Gregorian is used * @returns {object} `{ (int) year, (int) month, (float) day }` */ function JDToCalendar (jd, isJulian) { const [z, f] = base["default"].modf(jd + 0.5); let a = z; if (!isJulian) { const α = base["default"].floorDiv(z * 100 - 186721625, 3652425); a = z + 1 + α - base["default"].floorDiv(α, 4); } const b = a + 1524; const c = base["default"].floorDiv(b * 100 - 12210, 36525); const d = base["default"].floorDiv(36525 * c, 100); const e = int(base["default"].floorDiv((b - d) * 1e4, 306001)); // compute return values let year; let month; const day = (int(b - d) - base["default"].floorDiv(306001 * e, 1e4)) + f; if (e === 14 || e === 15) { month = e - 13; } else { month = e - 1; } if (month < 3) { year = int(c) - 4715; } else { year = int(c) - 4716; } return { year, month, day } } /** * JDToCalendarGregorian returns the calendar date for the given jd in the Gregorian Calendar. * * @param {number} jd - Julian day (float) * @returns {object} `{ (int) year, (int) month, (float) day }` */ function JDToCalendarGregorian (jd) { return JDToCalendar(jd, false) } /** * JDToCalendarJulian returns the calendar date for the given jd in the Julian Calendar. * * @param {number} jd - Julian day (float) * @returns {object} { (int) year, (int) month, (float) day } */ function JDToCalendarJulian (jd) { return JDToCalendar(jd, true) } /** * isJDCalendarGregorian tests if Julian day `jd` falls into the Gregorian calendar * @param {number} jd - Julian day (float) * @returns {boolean} true for Gregorian, false for Julian calendar */ function isJDCalendarGregorian (jd) { return (jd >= GREGORIAN0JD) } /** * isCalendarGregorian tests if date falls into the Gregorian calendar * @param {number} year - julian/gregorian year * @param {number} [month] - month of julian/gregorian year * @param {number} [day] - day of julian/gregorian year * @returns {boolean} true for Gregorian, false for Julian calendar */ function isCalendarGregorian (year, month = 1, day = 1) { return (year > 1582 || (year === 1582 && month > 10) || (year === 1582 && month === 10 && day >= 15) ) } /** * JDToDate converts a Julian day `jd` to a Date Object (Gregorian Calendar) * * Note: Javascript uses the the ISO-8601 calendar, which is a proleptic Gregorian * calendar, i.e. it acts as if this calendar was always in effect, even before * its year of introduction in 1582. Therefore dates between 1582-10-05 and * 1582-10-14 exists. * * @param {number} jd - Julian day (float) * @returns {Date} */ function JDToDate (jd) { return new CalendarGregorian().fromJD(jd).toDate() } /** * DateToJD converts a proleptic Gregorian Date into a Julian day `jd` * @param {Date} date * @returns {number} jd - Julian day (float) */ function DateToJD (date) { return new CalendarGregorian().fromDate(date).toJD() } /** * JDEToDate converts a Julian ephemeris day `jde` to a Date Object (Gregorian Calendar) * To obtain "Universal Time" (UT) from "Dynamical Time" (TD) the correction ΔT (in seconds) gets applied * ``` * UT = TD - ΔT * ``` * If your use case does not require such accuracy converting `jde` using `JDToDate` is fine. * * Note: Javascript uses the the ISO-8601 calendar, which is a proleptic Gregorian * calendar, i.e. it acts as if this calendar was always in effect, even before * its year of introduction in 1582. Therefore dates between 1582-10-05 and * 1582-10-14 exists. * * @param {number} jde - Julian ephemeris day * @returns {Date} Javascript Date Object */ function JDEToDate (jde) { return new CalendarGregorian().fromJDE(jde).toDate() } /** * DateToJDE converts a Date Object (Gregorian Calendar) to a Julian ephemeris day `jde` * To obtain "Dynamical Time" (TD) from "Universal Time" (UT) the correction ΔT (in seconds) gets applied * ``` * TD = UT + ΔT * ``` * If your use case does not require such accuracy converting `Date` using `DateToJD` is fine. * * @param {Date} date - Javascript Date Object * @returns {number} jde - Julian ephemeris day (float) */ function DateToJDE (date) { return new CalendarGregorian().fromDate(date).toJDE() } /** * converts Modified Julian Day `mjd` to Julian Day `jd` * @param {Number} mjd - Modified Julian Day * @returns {Number} jd - Julian Day */ function MJDToJD (mjd) { return mjd + base["default"].JMod } /** * converts Julian Day `jd` to Modified Julian Day `mjd` * The MJD sometimes appear when mentioning orbital elements of artificial satellites. * Contrary to JD the MJD begins at Greenwich mean midnight. * @param {Number} jd - Julian Day * @returns {Number} mjd - Modified Julian Day MJD */ function JDToMJD (jd) { return jd - base["default"].JMod } /** * DayOfWeek determines the day of the week for a given JD. * * The value returned is an integer in the range 0 to 6, where 0 represents * Sunday. This is the same convention followed in the time package of the * Javascript standard library. * @param {number} jd - Julian day (float) * @returns {number} (int) 0 == sunday; ...; 6 == saturday */ function DayOfWeek (jd) { return int(jd + 1.5) % 7 } /** * DayOfYearGregorian computes the day number within the year of the Gregorian * calendar. * @param {number} y - year (int) * @param {number} m - month (int) * @param {number} d - day (float) * @returns {number} day of year */ function DayOfYearGregorian (y, m, d) { return DayOfYear(y, m, int(d), LeapYearGregorian(y)) } /** * DayOfYearJulian computes the day number within the year of the Julian * calendar. * @param {number} y - year (int) * @param {number} m - month (int) * @param {number} d - day (float) * @returns {number} day of year */ function DayOfYearJulian (y, m, d) { return DayOfYear(y, m, int(d), LeapYearJulian(y)) } /** * DayOfYear computes the day number within the year. * * This form of the function is not specific to the Julian or Gregorian * calendar, but you must tell it whether the year is a leap year. * @param {number} y - year (int) * @param {number} m - month (int) * @param {number} d - day (float) * @param {boolean} leap - set `true` if `y` is leap year * @returns {number} day of year */ function DayOfYear (y, m, d, leap) { let k = 0; if (leap && m > 1) { k = 1; } return k + DAYS_OF_YEAR[m] + int(d) } /** * DayOfYearToCalendar returns the calendar month and day for a given * day of year and leap year status. * @param {number} n - day of year (int) * @param {boolean} leap - set `true` if `y` is leap year * @returns {object} `{ (int) month, (float) day }` */ function DayOfYearToCalendar (n, leap) { let month; let k = 0; if (leap) { k = 1; } for (month = 1; month <= 12; month++) { if (k + DAYS_OF_YEAR[month] > n) { month = month - 1; break } } const day = n - k - DAYS_OF_YEAR[month]; return { month, day } } /** * DayOfYearToCalendarGregorian returns the calendar month and day for a given * day of year. * @param {number} year * @param {number} n - day of year (int) * @returns {CalendarGregorian} { (int) year, (int) month, (float) day } */ function DayOfYearToCalendarGregorian (year, n) { const { month, day } = DayOfYearToCalendar(n, LeapYearGregorian(year)); return new CalendarGregorian(year, month, day) } /** * DayOfYearToCalendarJulian returns the calendar month and day for a given * day of year. * @param {number} year * @param {number} n - day of year (int) * @returns {CalendarJulian} { (int) year, (int) month, (float) day } */ function DayOfYearToCalendarJulian (year, n) { const { month, day } = DayOfYearToCalendar(n, LeapYearJulian(year)); return new CalendarJulian(year, month, day) } function pad (num, len) { len = len || 2; const neg = num < 0 ? '-' : ''; num = Math.abs(num); const padded = ('0000' + num); return neg + padded.substr(padded.length - len, len) } var julian = { GREGORIAN0JD, Calendar, CalendarJulian, CalendarGregorian, CalendarToJD, CalendarGregorianToJD, CalendarJulianToJD, LeapYearJulian, LeapYearGregorian, JDToCalendar, JDToCalendarGregorian, JDToCalendarJulian, isJDCalendarGregorian, isCalendarGregorian, JDToDate, DateToJD, JDEToDate, DateToJDE, MJDToJD, JDToMJD, DayOfWeek, DayOfYearGregorian, DayOfYearJulian, DayOfYear, DayOfYearToCalendar, DayOfYearToCalendarGregorian, DayOfYearToCalendarJulian }; exports.Calendar = Calendar; exports.CalendarGregorian = CalendarGregorian; exports.CalendarGregorianToJD = CalendarGregorianToJD; exports.CalendarJulian = CalendarJulian; exports.CalendarJulianToJD = CalendarJulianToJD; exports.CalendarToJD = CalendarToJD; exports.DateToJD = DateToJD; exports.DateToJDE = DateToJDE; exports.DayOfWeek = DayOfWeek; exports.DayOfYear = DayOfYear; exports.DayOfYearGregorian = DayOfYearGregorian; exports.DayOfYearJulian = DayOfYearJulian; exports.DayOfYearToCalendar = DayOfYearToCalendar; exports.DayOfYearToCalendarGregorian = DayOfYearToCalendarGregorian; exports.DayOfYearToCalendarJulian = DayOfYearToCalendarJulian; exports.GREGORIAN0JD = GREGORIAN0JD; exports.JDEToDate = JDEToDate; exports.JDToCalendar = JDToCalendar; exports.JDToCalendarGregorian = JDToCalendarGregorian; exports.JDToCalendarJulian = JDToCalendarJulian; exports.JDToDate = JDToDate; exports.JDToMJD = JDToMJD; exports.LeapYearGregorian = LeapYearGregorian; exports.LeapYearJulian = LeapYearJulian; exports.MJDToJD = MJDToJD; exports["default"] = julian; exports.isCalendarGregorian = isCalendarGregorian; exports.isJDCalendarGregorian = isJDCalendarGregorian;