JulianDate.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. import binarySearch from "./binarySearch.js";
  2. import defaultValue from "./defaultValue.js";
  3. import defined from "./defined.js";
  4. import DeveloperError from "./DeveloperError.js";
  5. import GregorianDate from "./GregorianDate.js";
  6. import isLeapYear from "./isLeapYear.js";
  7. import LeapSecond from "./LeapSecond.js";
  8. import TimeConstants from "./TimeConstants.js";
  9. import TimeStandard from "./TimeStandard.js";
  10. const gregorianDateScratch = new GregorianDate();
  11. const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  12. const daysInLeapFeburary = 29;
  13. function compareLeapSecondDates(leapSecond, dateToFind) {
  14. return JulianDate.compare(leapSecond.julianDate, dateToFind.julianDate);
  15. }
  16. // we don't really need a leap second instance, anything with a julianDate property will do
  17. const binarySearchScratchLeapSecond = new LeapSecond();
  18. function convertUtcToTai(julianDate) {
  19. //Even though julianDate is in UTC, we'll treat it as TAI and
  20. //search the leap second table for it.
  21. binarySearchScratchLeapSecond.julianDate = julianDate;
  22. const leapSeconds = JulianDate.leapSeconds;
  23. let index = binarySearch(
  24. leapSeconds,
  25. binarySearchScratchLeapSecond,
  26. compareLeapSecondDates
  27. );
  28. if (index < 0) {
  29. index = ~index;
  30. }
  31. if (index >= leapSeconds.length) {
  32. index = leapSeconds.length - 1;
  33. }
  34. let offset = leapSeconds[index].offset;
  35. if (index > 0) {
  36. //Now we have the index of the closest leap second that comes on or after our UTC time.
  37. //However, if the difference between the UTC date being converted and the TAI
  38. //defined leap second is greater than the offset, we are off by one and need to use
  39. //the previous leap second.
  40. const difference = JulianDate.secondsDifference(
  41. leapSeconds[index].julianDate,
  42. julianDate
  43. );
  44. if (difference > offset) {
  45. index--;
  46. offset = leapSeconds[index].offset;
  47. }
  48. }
  49. JulianDate.addSeconds(julianDate, offset, julianDate);
  50. }
  51. function convertTaiToUtc(julianDate, result) {
  52. binarySearchScratchLeapSecond.julianDate = julianDate;
  53. const leapSeconds = JulianDate.leapSeconds;
  54. let index = binarySearch(
  55. leapSeconds,
  56. binarySearchScratchLeapSecond,
  57. compareLeapSecondDates
  58. );
  59. if (index < 0) {
  60. index = ~index;
  61. }
  62. //All times before our first leap second get the first offset.
  63. if (index === 0) {
  64. return JulianDate.addSeconds(julianDate, -leapSeconds[0].offset, result);
  65. }
  66. //All times after our leap second get the last offset.
  67. if (index >= leapSeconds.length) {
  68. return JulianDate.addSeconds(
  69. julianDate,
  70. -leapSeconds[index - 1].offset,
  71. result
  72. );
  73. }
  74. //Compute the difference between the found leap second and the time we are converting.
  75. const difference = JulianDate.secondsDifference(
  76. leapSeconds[index].julianDate,
  77. julianDate
  78. );
  79. if (difference === 0) {
  80. //The date is in our leap second table.
  81. return JulianDate.addSeconds(
  82. julianDate,
  83. -leapSeconds[index].offset,
  84. result
  85. );
  86. }
  87. if (difference <= 1.0) {
  88. //The requested date is during the moment of a leap second, then we cannot convert to UTC
  89. return undefined;
  90. }
  91. //The time is in between two leap seconds, index is the leap second after the date
  92. //we're converting, so we subtract one to get the correct LeapSecond instance.
  93. return JulianDate.addSeconds(
  94. julianDate,
  95. -leapSeconds[--index].offset,
  96. result
  97. );
  98. }
  99. function setComponents(wholeDays, secondsOfDay, julianDate) {
  100. const extraDays = (secondsOfDay / TimeConstants.SECONDS_PER_DAY) | 0;
  101. wholeDays += extraDays;
  102. secondsOfDay -= TimeConstants.SECONDS_PER_DAY * extraDays;
  103. if (secondsOfDay < 0) {
  104. wholeDays--;
  105. secondsOfDay += TimeConstants.SECONDS_PER_DAY;
  106. }
  107. julianDate.dayNumber = wholeDays;
  108. julianDate.secondsOfDay = secondsOfDay;
  109. return julianDate;
  110. }
  111. function computeJulianDateComponents(
  112. year,
  113. month,
  114. day,
  115. hour,
  116. minute,
  117. second,
  118. millisecond
  119. ) {
  120. // Algorithm from page 604 of the Explanatory Supplement to the
  121. // Astronomical Almanac (Seidelmann 1992).
  122. const a = ((month - 14) / 12) | 0;
  123. const b = year + 4800 + a;
  124. let dayNumber =
  125. (((1461 * b) / 4) | 0) +
  126. (((367 * (month - 2 - 12 * a)) / 12) | 0) -
  127. (((3 * (((b + 100) / 100) | 0)) / 4) | 0) +
  128. day -
  129. 32075;
  130. // JulianDates are noon-based
  131. hour = hour - 12;
  132. if (hour < 0) {
  133. hour += 24;
  134. }
  135. const secondsOfDay =
  136. second +
  137. (hour * TimeConstants.SECONDS_PER_HOUR +
  138. minute * TimeConstants.SECONDS_PER_MINUTE +
  139. millisecond * TimeConstants.SECONDS_PER_MILLISECOND);
  140. if (secondsOfDay >= 43200.0) {
  141. dayNumber -= 1;
  142. }
  143. return [dayNumber, secondsOfDay];
  144. }
  145. //Regular expressions used for ISO8601 date parsing.
  146. //YYYY
  147. const matchCalendarYear = /^(\d{4})$/;
  148. //YYYY-MM (YYYYMM is invalid)
  149. const matchCalendarMonth = /^(\d{4})-(\d{2})$/;
  150. //YYYY-DDD or YYYYDDD
  151. const matchOrdinalDate = /^(\d{4})-?(\d{3})$/;
  152. //YYYY-Www or YYYYWww or YYYY-Www-D or YYYYWwwD
  153. const matchWeekDate = /^(\d{4})-?W(\d{2})-?(\d{1})?$/;
  154. //YYYY-MM-DD or YYYYMMDD
  155. const matchCalendarDate = /^(\d{4})-?(\d{2})-?(\d{2})$/;
  156. // Match utc offset
  157. const utcOffset = /([Z+\-])?(\d{2})?:?(\d{2})?$/;
  158. // Match hours HH or HH.xxxxx
  159. const matchHours = /^(\d{2})(\.\d+)?/.source + utcOffset.source;
  160. // Match hours/minutes HH:MM HHMM.xxxxx
  161. const matchHoursMinutes = /^(\d{2}):?(\d{2})(\.\d+)?/.source + utcOffset.source;
  162. // Match hours/minutes HH:MM:SS HHMMSS.xxxxx
  163. const matchHoursMinutesSeconds =
  164. /^(\d{2}):?(\d{2}):?(\d{2})(\.\d+)?/.source + utcOffset.source;
  165. const iso8601ErrorMessage = "Invalid ISO 8601 date.";
  166. /**
  167. * Represents an astronomical Julian date, which is the number of days since noon on January 1, -4712 (4713 BC).
  168. * For increased precision, this class stores the whole number part of the date and the seconds
  169. * part of the date in separate components. In order to be safe for arithmetic and represent
  170. * leap seconds, the date is always stored in the International Atomic Time standard
  171. * {@link TimeStandard.TAI}.
  172. * @alias JulianDate
  173. * @constructor
  174. *
  175. * @param {number} [julianDayNumber=0.0] The Julian Day Number representing the number of whole days. Fractional days will also be handled correctly.
  176. * @param {number} [secondsOfDay=0.0] The number of seconds into the current Julian Day Number. Fractional seconds, negative seconds and seconds greater than a day will be handled correctly.
  177. * @param {TimeStandard} [timeStandard=TimeStandard.UTC] The time standard in which the first two parameters are defined.
  178. */
  179. function JulianDate(julianDayNumber, secondsOfDay, timeStandard) {
  180. /**
  181. * Gets or sets the number of whole days.
  182. * @type {number}
  183. */
  184. this.dayNumber = undefined;
  185. /**
  186. * Gets or sets the number of seconds into the current day.
  187. * @type {number}
  188. */
  189. this.secondsOfDay = undefined;
  190. julianDayNumber = defaultValue(julianDayNumber, 0.0);
  191. secondsOfDay = defaultValue(secondsOfDay, 0.0);
  192. timeStandard = defaultValue(timeStandard, TimeStandard.UTC);
  193. //If julianDayNumber is fractional, make it an integer and add the number of seconds the fraction represented.
  194. const wholeDays = julianDayNumber | 0;
  195. secondsOfDay =
  196. secondsOfDay +
  197. (julianDayNumber - wholeDays) * TimeConstants.SECONDS_PER_DAY;
  198. setComponents(wholeDays, secondsOfDay, this);
  199. if (timeStandard === TimeStandard.UTC) {
  200. convertUtcToTai(this);
  201. }
  202. }
  203. /**
  204. * Creates a new instance from a GregorianDate.
  205. *
  206. * @param {GregorianDate} date A GregorianDate.
  207. * @param {JulianDate} [result] An existing instance to use for the result.
  208. * @returns {JulianDate} The modified result parameter or a new instance if none was provided.
  209. *
  210. * @exception {DeveloperError} date must be a valid GregorianDate.
  211. */
  212. JulianDate.fromGregorianDate = function (date, result) {
  213. //>>includeStart('debug', pragmas.debug);
  214. if (!(date instanceof GregorianDate)) {
  215. throw new DeveloperError("date must be a valid GregorianDate.");
  216. }
  217. //>>includeEnd('debug');
  218. const components = computeJulianDateComponents(
  219. date.year,
  220. date.month,
  221. date.day,
  222. date.hour,
  223. date.minute,
  224. date.second,
  225. date.millisecond
  226. );
  227. if (!defined(result)) {
  228. return new JulianDate(components[0], components[1], TimeStandard.UTC);
  229. }
  230. setComponents(components[0], components[1], result);
  231. convertUtcToTai(result);
  232. return result;
  233. };
  234. /**
  235. * Creates a new instance from a JavaScript Date.
  236. *
  237. * @param {Date} date A JavaScript Date.
  238. * @param {JulianDate} [result] An existing instance to use for the result.
  239. * @returns {JulianDate} The modified result parameter or a new instance if none was provided.
  240. *
  241. * @exception {DeveloperError} date must be a valid JavaScript Date.
  242. */
  243. JulianDate.fromDate = function (date, result) {
  244. //>>includeStart('debug', pragmas.debug);
  245. if (!(date instanceof Date) || isNaN(date.getTime())) {
  246. throw new DeveloperError("date must be a valid JavaScript Date.");
  247. }
  248. //>>includeEnd('debug');
  249. const components = computeJulianDateComponents(
  250. date.getUTCFullYear(),
  251. date.getUTCMonth() + 1,
  252. date.getUTCDate(),
  253. date.getUTCHours(),
  254. date.getUTCMinutes(),
  255. date.getUTCSeconds(),
  256. date.getUTCMilliseconds()
  257. );
  258. if (!defined(result)) {
  259. return new JulianDate(components[0], components[1], TimeStandard.UTC);
  260. }
  261. setComponents(components[0], components[1], result);
  262. convertUtcToTai(result);
  263. return result;
  264. };
  265. /**
  266. * Creates a new instance from a from an {@link http://en.wikipedia.org/wiki/ISO_8601|ISO 8601} date.
  267. * This method is superior to <code>Date.parse</code> because it will handle all valid formats defined by the ISO 8601
  268. * specification, including leap seconds and sub-millisecond times, which discarded by most JavaScript implementations.
  269. *
  270. * @param {string} iso8601String An ISO 8601 date.
  271. * @param {JulianDate} [result] An existing instance to use for the result.
  272. * @returns {JulianDate} The modified result parameter or a new instance if none was provided.
  273. *
  274. * @exception {DeveloperError} Invalid ISO 8601 date.
  275. */
  276. JulianDate.fromIso8601 = function (iso8601String, result) {
  277. //>>includeStart('debug', pragmas.debug);
  278. if (typeof iso8601String !== "string") {
  279. throw new DeveloperError(iso8601ErrorMessage);
  280. }
  281. //>>includeEnd('debug');
  282. //Comma and decimal point both indicate a fractional number according to ISO 8601,
  283. //start out by blanket replacing , with . which is the only valid such symbol in JS.
  284. iso8601String = iso8601String.replace(",", ".");
  285. //Split the string into its date and time components, denoted by a mandatory T
  286. let tokens = iso8601String.split("T");
  287. let year;
  288. let month = 1;
  289. let day = 1;
  290. let hour = 0;
  291. let minute = 0;
  292. let second = 0;
  293. let millisecond = 0;
  294. //Lacking a time is okay, but a missing date is illegal.
  295. const date = tokens[0];
  296. const time = tokens[1];
  297. let tmp;
  298. let inLeapYear;
  299. //>>includeStart('debug', pragmas.debug);
  300. if (!defined(date)) {
  301. throw new DeveloperError(iso8601ErrorMessage);
  302. }
  303. let dashCount;
  304. //>>includeEnd('debug');
  305. //First match the date against possible regular expressions.
  306. tokens = date.match(matchCalendarDate);
  307. if (tokens !== null) {
  308. //>>includeStart('debug', pragmas.debug);
  309. dashCount = date.split("-").length - 1;
  310. if (dashCount > 0 && dashCount !== 2) {
  311. throw new DeveloperError(iso8601ErrorMessage);
  312. }
  313. //>>includeEnd('debug');
  314. year = +tokens[1];
  315. month = +tokens[2];
  316. day = +tokens[3];
  317. } else {
  318. tokens = date.match(matchCalendarMonth);
  319. if (tokens !== null) {
  320. year = +tokens[1];
  321. month = +tokens[2];
  322. } else {
  323. tokens = date.match(matchCalendarYear);
  324. if (tokens !== null) {
  325. year = +tokens[1];
  326. } else {
  327. //Not a year/month/day so it must be an ordinal date.
  328. let dayOfYear;
  329. tokens = date.match(matchOrdinalDate);
  330. if (tokens !== null) {
  331. year = +tokens[1];
  332. dayOfYear = +tokens[2];
  333. inLeapYear = isLeapYear(year);
  334. //This validation is only applicable for this format.
  335. //>>includeStart('debug', pragmas.debug);
  336. if (
  337. dayOfYear < 1 ||
  338. (inLeapYear && dayOfYear > 366) ||
  339. (!inLeapYear && dayOfYear > 365)
  340. ) {
  341. throw new DeveloperError(iso8601ErrorMessage);
  342. }
  343. //>>includeEnd('debug')
  344. } else {
  345. tokens = date.match(matchWeekDate);
  346. if (tokens !== null) {
  347. //ISO week date to ordinal date from
  348. //http://en.wikipedia.org/w/index.php?title=ISO_week_date&oldid=474176775
  349. year = +tokens[1];
  350. const weekNumber = +tokens[2];
  351. const dayOfWeek = +tokens[3] || 0;
  352. //>>includeStart('debug', pragmas.debug);
  353. dashCount = date.split("-").length - 1;
  354. if (
  355. dashCount > 0 &&
  356. ((!defined(tokens[3]) && dashCount !== 1) ||
  357. (defined(tokens[3]) && dashCount !== 2))
  358. ) {
  359. throw new DeveloperError(iso8601ErrorMessage);
  360. }
  361. //>>includeEnd('debug')
  362. const january4 = new Date(Date.UTC(year, 0, 4));
  363. dayOfYear = weekNumber * 7 + dayOfWeek - january4.getUTCDay() - 3;
  364. } else {
  365. //None of our regular expressions succeeded in parsing the date properly.
  366. //>>includeStart('debug', pragmas.debug);
  367. throw new DeveloperError(iso8601ErrorMessage);
  368. //>>includeEnd('debug')
  369. }
  370. }
  371. //Split an ordinal date into month/day.
  372. tmp = new Date(Date.UTC(year, 0, 1));
  373. tmp.setUTCDate(dayOfYear);
  374. month = tmp.getUTCMonth() + 1;
  375. day = tmp.getUTCDate();
  376. }
  377. }
  378. }
  379. //Now that we have all of the date components, validate them to make sure nothing is out of range.
  380. inLeapYear = isLeapYear(year);
  381. //>>includeStart('debug', pragmas.debug);
  382. if (
  383. month < 1 ||
  384. month > 12 ||
  385. day < 1 ||
  386. ((month !== 2 || !inLeapYear) && day > daysInMonth[month - 1]) ||
  387. (inLeapYear && month === 2 && day > daysInLeapFeburary)
  388. ) {
  389. throw new DeveloperError(iso8601ErrorMessage);
  390. }
  391. //>>includeEnd('debug')
  392. //Now move onto the time string, which is much simpler.
  393. //If no time is specified, it is considered the beginning of the day, UTC to match Javascript's implementation.
  394. let offsetIndex;
  395. if (defined(time)) {
  396. tokens = time.match(matchHoursMinutesSeconds);
  397. if (tokens !== null) {
  398. //>>includeStart('debug', pragmas.debug);
  399. dashCount = time.split(":").length - 1;
  400. if (dashCount > 0 && dashCount !== 2 && dashCount !== 3) {
  401. throw new DeveloperError(iso8601ErrorMessage);
  402. }
  403. //>>includeEnd('debug')
  404. hour = +tokens[1];
  405. minute = +tokens[2];
  406. second = +tokens[3];
  407. millisecond = +(tokens[4] || 0) * 1000.0;
  408. offsetIndex = 5;
  409. } else {
  410. tokens = time.match(matchHoursMinutes);
  411. if (tokens !== null) {
  412. //>>includeStart('debug', pragmas.debug);
  413. dashCount = time.split(":").length - 1;
  414. if (dashCount > 2) {
  415. throw new DeveloperError(iso8601ErrorMessage);
  416. }
  417. //>>includeEnd('debug')
  418. hour = +tokens[1];
  419. minute = +tokens[2];
  420. second = +(tokens[3] || 0) * 60.0;
  421. offsetIndex = 4;
  422. } else {
  423. tokens = time.match(matchHours);
  424. if (tokens !== null) {
  425. hour = +tokens[1];
  426. minute = +(tokens[2] || 0) * 60.0;
  427. offsetIndex = 3;
  428. } else {
  429. //>>includeStart('debug', pragmas.debug);
  430. throw new DeveloperError(iso8601ErrorMessage);
  431. //>>includeEnd('debug')
  432. }
  433. }
  434. }
  435. //Validate that all values are in proper range. Minutes and hours have special cases at 60 and 24.
  436. //>>includeStart('debug', pragmas.debug);
  437. if (
  438. minute >= 60 ||
  439. second >= 61 ||
  440. hour > 24 ||
  441. (hour === 24 && (minute > 0 || second > 0 || millisecond > 0))
  442. ) {
  443. throw new DeveloperError(iso8601ErrorMessage);
  444. }
  445. //>>includeEnd('debug');
  446. //Check the UTC offset value, if no value exists, use local time
  447. //a Z indicates UTC, + or - are offsets.
  448. const offset = tokens[offsetIndex];
  449. const offsetHours = +tokens[offsetIndex + 1];
  450. const offsetMinutes = +(tokens[offsetIndex + 2] || 0);
  451. switch (offset) {
  452. case "+":
  453. hour = hour - offsetHours;
  454. minute = minute - offsetMinutes;
  455. break;
  456. case "-":
  457. hour = hour + offsetHours;
  458. minute = minute + offsetMinutes;
  459. break;
  460. case "Z":
  461. break;
  462. default:
  463. minute =
  464. minute +
  465. new Date(
  466. Date.UTC(year, month - 1, day, hour, minute)
  467. ).getTimezoneOffset();
  468. break;
  469. }
  470. }
  471. //ISO8601 denotes a leap second by any time having a seconds component of 60 seconds.
  472. //If that's the case, we need to temporarily subtract a second in order to build a UTC date.
  473. //Then we add it back in after converting to TAI.
  474. const isLeapSecond = second === 60;
  475. if (isLeapSecond) {
  476. second--;
  477. }
  478. //Even if we successfully parsed the string into its components, after applying UTC offset or
  479. //special cases like 24:00:00 denoting midnight, we need to normalize the data appropriately.
  480. //milliseconds can never be greater than 1000, and seconds can't be above 60, so we start with minutes
  481. while (minute >= 60) {
  482. minute -= 60;
  483. hour++;
  484. }
  485. while (hour >= 24) {
  486. hour -= 24;
  487. day++;
  488. }
  489. tmp = inLeapYear && month === 2 ? daysInLeapFeburary : daysInMonth[month - 1];
  490. while (day > tmp) {
  491. day -= tmp;
  492. month++;
  493. if (month > 12) {
  494. month -= 12;
  495. year++;
  496. }
  497. tmp =
  498. inLeapYear && month === 2 ? daysInLeapFeburary : daysInMonth[month - 1];
  499. }
  500. //If UTC offset is at the beginning/end of the day, minutes can be negative.
  501. while (minute < 0) {
  502. minute += 60;
  503. hour--;
  504. }
  505. while (hour < 0) {
  506. hour += 24;
  507. day--;
  508. }
  509. while (day < 1) {
  510. month--;
  511. if (month < 1) {
  512. month += 12;
  513. year--;
  514. }
  515. tmp =
  516. inLeapYear && month === 2 ? daysInLeapFeburary : daysInMonth[month - 1];
  517. day += tmp;
  518. }
  519. //Now create the JulianDate components from the Gregorian date and actually create our instance.
  520. const components = computeJulianDateComponents(
  521. year,
  522. month,
  523. day,
  524. hour,
  525. minute,
  526. second,
  527. millisecond
  528. );
  529. if (!defined(result)) {
  530. result = new JulianDate(components[0], components[1], TimeStandard.UTC);
  531. } else {
  532. setComponents(components[0], components[1], result);
  533. convertUtcToTai(result);
  534. }
  535. //If we were on a leap second, add it back.
  536. if (isLeapSecond) {
  537. JulianDate.addSeconds(result, 1, result);
  538. }
  539. return result;
  540. };
  541. /**
  542. * Creates a new instance that represents the current system time.
  543. * This is equivalent to calling <code>JulianDate.fromDate(new Date());</code>.
  544. *
  545. * @param {JulianDate} [result] An existing instance to use for the result.
  546. * @returns {JulianDate} The modified result parameter or a new instance if none was provided.
  547. */
  548. JulianDate.now = function (result) {
  549. return JulianDate.fromDate(new Date(), result);
  550. };
  551. const toGregorianDateScratch = new JulianDate(0, 0, TimeStandard.TAI);
  552. /**
  553. * Creates a {@link GregorianDate} from the provided instance.
  554. *
  555. * @param {JulianDate} julianDate The date to be converted.
  556. * @param {GregorianDate} [result] An existing instance to use for the result.
  557. * @returns {GregorianDate} The modified result parameter or a new instance if none was provided.
  558. */
  559. JulianDate.toGregorianDate = function (julianDate, result) {
  560. //>>includeStart('debug', pragmas.debug);
  561. if (!defined(julianDate)) {
  562. throw new DeveloperError("julianDate is required.");
  563. }
  564. //>>includeEnd('debug');
  565. let isLeapSecond = false;
  566. let thisUtc = convertTaiToUtc(julianDate, toGregorianDateScratch);
  567. if (!defined(thisUtc)) {
  568. //Conversion to UTC will fail if we are during a leap second.
  569. //If that's the case, subtract a second and convert again.
  570. //JavaScript doesn't support leap seconds, so this results in second 59 being repeated twice.
  571. JulianDate.addSeconds(julianDate, -1, toGregorianDateScratch);
  572. thisUtc = convertTaiToUtc(toGregorianDateScratch, toGregorianDateScratch);
  573. isLeapSecond = true;
  574. }
  575. let julianDayNumber = thisUtc.dayNumber;
  576. const secondsOfDay = thisUtc.secondsOfDay;
  577. if (secondsOfDay >= 43200.0) {
  578. julianDayNumber += 1;
  579. }
  580. // Algorithm from page 604 of the Explanatory Supplement to the
  581. // Astronomical Almanac (Seidelmann 1992).
  582. let L = (julianDayNumber + 68569) | 0;
  583. const N = ((4 * L) / 146097) | 0;
  584. L = (L - (((146097 * N + 3) / 4) | 0)) | 0;
  585. const I = ((4000 * (L + 1)) / 1461001) | 0;
  586. L = (L - (((1461 * I) / 4) | 0) + 31) | 0;
  587. const J = ((80 * L) / 2447) | 0;
  588. const day = (L - (((2447 * J) / 80) | 0)) | 0;
  589. L = (J / 11) | 0;
  590. const month = (J + 2 - 12 * L) | 0;
  591. const year = (100 * (N - 49) + I + L) | 0;
  592. let hour = (secondsOfDay / TimeConstants.SECONDS_PER_HOUR) | 0;
  593. let remainingSeconds = secondsOfDay - hour * TimeConstants.SECONDS_PER_HOUR;
  594. const minute = (remainingSeconds / TimeConstants.SECONDS_PER_MINUTE) | 0;
  595. remainingSeconds =
  596. remainingSeconds - minute * TimeConstants.SECONDS_PER_MINUTE;
  597. let second = remainingSeconds | 0;
  598. const millisecond =
  599. (remainingSeconds - second) / TimeConstants.SECONDS_PER_MILLISECOND;
  600. // JulianDates are noon-based
  601. hour += 12;
  602. if (hour > 23) {
  603. hour -= 24;
  604. }
  605. //If we were on a leap second, add it back.
  606. if (isLeapSecond) {
  607. second += 1;
  608. }
  609. if (!defined(result)) {
  610. return new GregorianDate(
  611. year,
  612. month,
  613. day,
  614. hour,
  615. minute,
  616. second,
  617. millisecond,
  618. isLeapSecond
  619. );
  620. }
  621. result.year = year;
  622. result.month = month;
  623. result.day = day;
  624. result.hour = hour;
  625. result.minute = minute;
  626. result.second = second;
  627. result.millisecond = millisecond;
  628. result.isLeapSecond = isLeapSecond;
  629. return result;
  630. };
  631. /**
  632. * Creates a JavaScript Date from the provided instance.
  633. * Since JavaScript dates are only accurate to the nearest millisecond and
  634. * cannot represent a leap second, consider using {@link JulianDate.toGregorianDate} instead.
  635. * If the provided JulianDate is during a leap second, the previous second is used.
  636. *
  637. * @param {JulianDate} julianDate The date to be converted.
  638. * @returns {Date} A new instance representing the provided date.
  639. */
  640. JulianDate.toDate = function (julianDate) {
  641. //>>includeStart('debug', pragmas.debug);
  642. if (!defined(julianDate)) {
  643. throw new DeveloperError("julianDate is required.");
  644. }
  645. //>>includeEnd('debug');
  646. const gDate = JulianDate.toGregorianDate(julianDate, gregorianDateScratch);
  647. let second = gDate.second;
  648. if (gDate.isLeapSecond) {
  649. second -= 1;
  650. }
  651. return new Date(
  652. Date.UTC(
  653. gDate.year,
  654. gDate.month - 1,
  655. gDate.day,
  656. gDate.hour,
  657. gDate.minute,
  658. second,
  659. gDate.millisecond
  660. )
  661. );
  662. };
  663. /**
  664. * Creates an ISO8601 representation of the provided date.
  665. *
  666. * @param {JulianDate} julianDate The date to be converted.
  667. * @param {number} [precision] The number of fractional digits used to represent the seconds component. By default, the most precise representation is used.
  668. * @returns {string} The ISO8601 representation of the provided date.
  669. */
  670. JulianDate.toIso8601 = function (julianDate, precision) {
  671. //>>includeStart('debug', pragmas.debug);
  672. if (!defined(julianDate)) {
  673. throw new DeveloperError("julianDate is required.");
  674. }
  675. //>>includeEnd('debug');
  676. const gDate = JulianDate.toGregorianDate(julianDate, gregorianDateScratch);
  677. let year = gDate.year;
  678. let month = gDate.month;
  679. let day = gDate.day;
  680. let hour = gDate.hour;
  681. const minute = gDate.minute;
  682. const second = gDate.second;
  683. const millisecond = gDate.millisecond;
  684. // special case - Iso8601.MAXIMUM_VALUE produces a string which we can't parse unless we adjust.
  685. // 10000-01-01T00:00:00 is the same instant as 9999-12-31T24:00:00
  686. if (
  687. year === 10000 &&
  688. month === 1 &&
  689. day === 1 &&
  690. hour === 0 &&
  691. minute === 0 &&
  692. second === 0 &&
  693. millisecond === 0
  694. ) {
  695. year = 9999;
  696. month = 12;
  697. day = 31;
  698. hour = 24;
  699. }
  700. let millisecondStr;
  701. if (!defined(precision) && millisecond !== 0) {
  702. //Forces milliseconds into a number with at least 3 digits to whatever the default toString() precision is.
  703. millisecondStr = (millisecond * 0.01).toString().replace(".", "");
  704. return `${year.toString().padStart(4, "0")}-${month
  705. .toString()
  706. .padStart(2, "0")}-${day
  707. .toString()
  708. .padStart(2, "0")}T${hour
  709. .toString()
  710. .padStart(2, "0")}:${minute
  711. .toString()
  712. .padStart(2, "0")}:${second
  713. .toString()
  714. .padStart(2, "0")}.${millisecondStr}Z`;
  715. }
  716. //Precision is either 0 or milliseconds is 0 with undefined precision, in either case, leave off milliseconds entirely
  717. if (!defined(precision) || precision === 0) {
  718. return `${year.toString().padStart(4, "0")}-${month
  719. .toString()
  720. .padStart(2, "0")}-${day
  721. .toString()
  722. .padStart(2, "0")}T${hour
  723. .toString()
  724. .padStart(2, "0")}:${minute
  725. .toString()
  726. .padStart(2, "0")}:${second.toString().padStart(2, "0")}Z`;
  727. }
  728. //Forces milliseconds into a number with at least 3 digits to whatever the specified precision is.
  729. millisecondStr = (millisecond * 0.01)
  730. .toFixed(precision)
  731. .replace(".", "")
  732. .slice(0, precision);
  733. return `${year.toString().padStart(4, "0")}-${month
  734. .toString()
  735. .padStart(2, "0")}-${day
  736. .toString()
  737. .padStart(2, "0")}T${hour
  738. .toString()
  739. .padStart(2, "0")}:${minute
  740. .toString()
  741. .padStart(2, "0")}:${second
  742. .toString()
  743. .padStart(2, "0")}.${millisecondStr}Z`;
  744. };
  745. /**
  746. * Duplicates a JulianDate instance.
  747. *
  748. * @param {JulianDate} julianDate The date to duplicate.
  749. * @param {JulianDate} [result] An existing instance to use for the result.
  750. * @returns {JulianDate} The modified result parameter or a new instance if none was provided. Returns undefined if julianDate is undefined.
  751. */
  752. JulianDate.clone = function (julianDate, result) {
  753. if (!defined(julianDate)) {
  754. return undefined;
  755. }
  756. if (!defined(result)) {
  757. return new JulianDate(
  758. julianDate.dayNumber,
  759. julianDate.secondsOfDay,
  760. TimeStandard.TAI
  761. );
  762. }
  763. result.dayNumber = julianDate.dayNumber;
  764. result.secondsOfDay = julianDate.secondsOfDay;
  765. return result;
  766. };
  767. /**
  768. * Compares two instances.
  769. *
  770. * @param {JulianDate} left The first instance.
  771. * @param {JulianDate} right The second instance.
  772. * @returns {number} A negative value if left is less than right, a positive value if left is greater than right, or zero if left and right are equal.
  773. */
  774. JulianDate.compare = function (left, right) {
  775. //>>includeStart('debug', pragmas.debug);
  776. if (!defined(left)) {
  777. throw new DeveloperError("left is required.");
  778. }
  779. if (!defined(right)) {
  780. throw new DeveloperError("right is required.");
  781. }
  782. //>>includeEnd('debug');
  783. const julianDayNumberDifference = left.dayNumber - right.dayNumber;
  784. if (julianDayNumberDifference !== 0) {
  785. return julianDayNumberDifference;
  786. }
  787. return left.secondsOfDay - right.secondsOfDay;
  788. };
  789. /**
  790. * Compares two instances and returns <code>true</code> if they are equal, <code>false</code> otherwise.
  791. *
  792. * @param {JulianDate} [left] The first instance.
  793. * @param {JulianDate} [right] The second instance.
  794. * @returns {boolean} <code>true</code> if the dates are equal; otherwise, <code>false</code>.
  795. */
  796. JulianDate.equals = function (left, right) {
  797. return (
  798. left === right ||
  799. (defined(left) &&
  800. defined(right) &&
  801. left.dayNumber === right.dayNumber &&
  802. left.secondsOfDay === right.secondsOfDay)
  803. );
  804. };
  805. /**
  806. * Compares two instances and returns <code>true</code> if they are within <code>epsilon</code> seconds of
  807. * each other. That is, in order for the dates to be considered equal (and for
  808. * this function to return <code>true</code>), the absolute value of the difference between them, in
  809. * seconds, must be less than <code>epsilon</code>.
  810. *
  811. * @param {JulianDate} [left] The first instance.
  812. * @param {JulianDate} [right] The second instance.
  813. * @param {number} [epsilon=0] The maximum number of seconds that should separate the two instances.
  814. * @returns {boolean} <code>true</code> if the two dates are within <code>epsilon</code> seconds of each other; otherwise <code>false</code>.
  815. */
  816. JulianDate.equalsEpsilon = function (left, right, epsilon) {
  817. epsilon = defaultValue(epsilon, 0);
  818. return (
  819. left === right ||
  820. (defined(left) &&
  821. defined(right) &&
  822. Math.abs(JulianDate.secondsDifference(left, right)) <= epsilon)
  823. );
  824. };
  825. /**
  826. * Computes the total number of whole and fractional days represented by the provided instance.
  827. *
  828. * @param {JulianDate} julianDate The date.
  829. * @returns {number} The Julian date as single floating point number.
  830. */
  831. JulianDate.totalDays = function (julianDate) {
  832. //>>includeStart('debug', pragmas.debug);
  833. if (!defined(julianDate)) {
  834. throw new DeveloperError("julianDate is required.");
  835. }
  836. //>>includeEnd('debug');
  837. return (
  838. julianDate.dayNumber +
  839. julianDate.secondsOfDay / TimeConstants.SECONDS_PER_DAY
  840. );
  841. };
  842. /**
  843. * Computes the difference in seconds between the provided instance.
  844. *
  845. * @param {JulianDate} left The first instance.
  846. * @param {JulianDate} right The second instance.
  847. * @returns {number} The difference, in seconds, when subtracting <code>right</code> from <code>left</code>.
  848. */
  849. JulianDate.secondsDifference = function (left, right) {
  850. //>>includeStart('debug', pragmas.debug);
  851. if (!defined(left)) {
  852. throw new DeveloperError("left is required.");
  853. }
  854. if (!defined(right)) {
  855. throw new DeveloperError("right is required.");
  856. }
  857. //>>includeEnd('debug');
  858. const dayDifference =
  859. (left.dayNumber - right.dayNumber) * TimeConstants.SECONDS_PER_DAY;
  860. return dayDifference + (left.secondsOfDay - right.secondsOfDay);
  861. };
  862. /**
  863. * Computes the difference in days between the provided instance.
  864. *
  865. * @param {JulianDate} left The first instance.
  866. * @param {JulianDate} right The second instance.
  867. * @returns {number} The difference, in days, when subtracting <code>right</code> from <code>left</code>.
  868. */
  869. JulianDate.daysDifference = function (left, right) {
  870. //>>includeStart('debug', pragmas.debug);
  871. if (!defined(left)) {
  872. throw new DeveloperError("left is required.");
  873. }
  874. if (!defined(right)) {
  875. throw new DeveloperError("right is required.");
  876. }
  877. //>>includeEnd('debug');
  878. const dayDifference = left.dayNumber - right.dayNumber;
  879. const secondDifference =
  880. (left.secondsOfDay - right.secondsOfDay) / TimeConstants.SECONDS_PER_DAY;
  881. return dayDifference + secondDifference;
  882. };
  883. /**
  884. * Computes the number of seconds the provided instance is ahead of UTC.
  885. *
  886. * @param {JulianDate} julianDate The date.
  887. * @returns {number} The number of seconds the provided instance is ahead of UTC
  888. */
  889. JulianDate.computeTaiMinusUtc = function (julianDate) {
  890. binarySearchScratchLeapSecond.julianDate = julianDate;
  891. const leapSeconds = JulianDate.leapSeconds;
  892. let index = binarySearch(
  893. leapSeconds,
  894. binarySearchScratchLeapSecond,
  895. compareLeapSecondDates
  896. );
  897. if (index < 0) {
  898. index = ~index;
  899. --index;
  900. if (index < 0) {
  901. index = 0;
  902. }
  903. }
  904. return leapSeconds[index].offset;
  905. };
  906. /**
  907. * Adds the provided number of seconds to the provided date instance.
  908. *
  909. * @param {JulianDate} julianDate The date.
  910. * @param {number} seconds The number of seconds to add or subtract.
  911. * @param {JulianDate} result An existing instance to use for the result.
  912. * @returns {JulianDate} The modified result parameter.
  913. */
  914. JulianDate.addSeconds = function (julianDate, seconds, result) {
  915. //>>includeStart('debug', pragmas.debug);
  916. if (!defined(julianDate)) {
  917. throw new DeveloperError("julianDate is required.");
  918. }
  919. if (!defined(seconds)) {
  920. throw new DeveloperError("seconds is required.");
  921. }
  922. if (!defined(result)) {
  923. throw new DeveloperError("result is required.");
  924. }
  925. //>>includeEnd('debug');
  926. return setComponents(
  927. julianDate.dayNumber,
  928. julianDate.secondsOfDay + seconds,
  929. result
  930. );
  931. };
  932. /**
  933. * Adds the provided number of minutes to the provided date instance.
  934. *
  935. * @param {JulianDate} julianDate The date.
  936. * @param {number} minutes The number of minutes to add or subtract.
  937. * @param {JulianDate} result An existing instance to use for the result.
  938. * @returns {JulianDate} The modified result parameter.
  939. */
  940. JulianDate.addMinutes = function (julianDate, minutes, result) {
  941. //>>includeStart('debug', pragmas.debug);
  942. if (!defined(julianDate)) {
  943. throw new DeveloperError("julianDate is required.");
  944. }
  945. if (!defined(minutes)) {
  946. throw new DeveloperError("minutes is required.");
  947. }
  948. if (!defined(result)) {
  949. throw new DeveloperError("result is required.");
  950. }
  951. //>>includeEnd('debug');
  952. const newSecondsOfDay =
  953. julianDate.secondsOfDay + minutes * TimeConstants.SECONDS_PER_MINUTE;
  954. return setComponents(julianDate.dayNumber, newSecondsOfDay, result);
  955. };
  956. /**
  957. * Adds the provided number of hours to the provided date instance.
  958. *
  959. * @param {JulianDate} julianDate The date.
  960. * @param {number} hours The number of hours to add or subtract.
  961. * @param {JulianDate} result An existing instance to use for the result.
  962. * @returns {JulianDate} The modified result parameter.
  963. */
  964. JulianDate.addHours = function (julianDate, hours, result) {
  965. //>>includeStart('debug', pragmas.debug);
  966. if (!defined(julianDate)) {
  967. throw new DeveloperError("julianDate is required.");
  968. }
  969. if (!defined(hours)) {
  970. throw new DeveloperError("hours is required.");
  971. }
  972. if (!defined(result)) {
  973. throw new DeveloperError("result is required.");
  974. }
  975. //>>includeEnd('debug');
  976. const newSecondsOfDay =
  977. julianDate.secondsOfDay + hours * TimeConstants.SECONDS_PER_HOUR;
  978. return setComponents(julianDate.dayNumber, newSecondsOfDay, result);
  979. };
  980. /**
  981. * Adds the provided number of days to the provided date instance.
  982. *
  983. * @param {JulianDate} julianDate The date.
  984. * @param {number} days The number of days to add or subtract.
  985. * @param {JulianDate} result An existing instance to use for the result.
  986. * @returns {JulianDate} The modified result parameter.
  987. */
  988. JulianDate.addDays = function (julianDate, days, result) {
  989. //>>includeStart('debug', pragmas.debug);
  990. if (!defined(julianDate)) {
  991. throw new DeveloperError("julianDate is required.");
  992. }
  993. if (!defined(days)) {
  994. throw new DeveloperError("days is required.");
  995. }
  996. if (!defined(result)) {
  997. throw new DeveloperError("result is required.");
  998. }
  999. //>>includeEnd('debug');
  1000. const newJulianDayNumber = julianDate.dayNumber + days;
  1001. return setComponents(newJulianDayNumber, julianDate.secondsOfDay, result);
  1002. };
  1003. /**
  1004. * Compares the provided instances and returns <code>true</code> if <code>left</code> is earlier than <code>right</code>, <code>false</code> otherwise.
  1005. *
  1006. * @param {JulianDate} left The first instance.
  1007. * @param {JulianDate} right The second instance.
  1008. * @returns {boolean} <code>true</code> if <code>left</code> is earlier than <code>right</code>, <code>false</code> otherwise.
  1009. */
  1010. JulianDate.lessThan = function (left, right) {
  1011. return JulianDate.compare(left, right) < 0;
  1012. };
  1013. /**
  1014. * Compares the provided instances and returns <code>true</code> if <code>left</code> is earlier than or equal to <code>right</code>, <code>false</code> otherwise.
  1015. *
  1016. * @param {JulianDate} left The first instance.
  1017. * @param {JulianDate} right The second instance.
  1018. * @returns {boolean} <code>true</code> if <code>left</code> is earlier than or equal to <code>right</code>, <code>false</code> otherwise.
  1019. */
  1020. JulianDate.lessThanOrEquals = function (left, right) {
  1021. return JulianDate.compare(left, right) <= 0;
  1022. };
  1023. /**
  1024. * Compares the provided instances and returns <code>true</code> if <code>left</code> is later than <code>right</code>, <code>false</code> otherwise.
  1025. *
  1026. * @param {JulianDate} left The first instance.
  1027. * @param {JulianDate} right The second instance.
  1028. * @returns {boolean} <code>true</code> if <code>left</code> is later than <code>right</code>, <code>false</code> otherwise.
  1029. */
  1030. JulianDate.greaterThan = function (left, right) {
  1031. return JulianDate.compare(left, right) > 0;
  1032. };
  1033. /**
  1034. * Compares the provided instances and returns <code>true</code> if <code>left</code> is later than or equal to <code>right</code>, <code>false</code> otherwise.
  1035. *
  1036. * @param {JulianDate} left The first instance.
  1037. * @param {JulianDate} right The second instance.
  1038. * @returns {boolean} <code>true</code> if <code>left</code> is later than or equal to <code>right</code>, <code>false</code> otherwise.
  1039. */
  1040. JulianDate.greaterThanOrEquals = function (left, right) {
  1041. return JulianDate.compare(left, right) >= 0;
  1042. };
  1043. /**
  1044. * Duplicates this instance.
  1045. *
  1046. * @param {JulianDate} [result] An existing instance to use for the result.
  1047. * @returns {JulianDate} The modified result parameter or a new instance if none was provided.
  1048. */
  1049. JulianDate.prototype.clone = function (result) {
  1050. return JulianDate.clone(this, result);
  1051. };
  1052. /**
  1053. * Compares this and the provided instance and returns <code>true</code> if they are equal, <code>false</code> otherwise.
  1054. *
  1055. * @param {JulianDate} [right] The second instance.
  1056. * @returns {boolean} <code>true</code> if the dates are equal; otherwise, <code>false</code>.
  1057. */
  1058. JulianDate.prototype.equals = function (right) {
  1059. return JulianDate.equals(this, right);
  1060. };
  1061. /**
  1062. * Compares this and the provided instance and returns <code>true</code> if they are within <code>epsilon</code> seconds of
  1063. * each other. That is, in order for the dates to be considered equal (and for
  1064. * this function to return <code>true</code>), the absolute value of the difference between them, in
  1065. * seconds, must be less than <code>epsilon</code>.
  1066. *
  1067. * @param {JulianDate} [right] The second instance.
  1068. * @param {number} [epsilon=0] The maximum number of seconds that should separate the two instances.
  1069. * @returns {boolean} <code>true</code> if the two dates are within <code>epsilon</code> seconds of each other; otherwise <code>false</code>.
  1070. */
  1071. JulianDate.prototype.equalsEpsilon = function (right, epsilon) {
  1072. return JulianDate.equalsEpsilon(this, right, epsilon);
  1073. };
  1074. /**
  1075. * Creates a string representing this date in ISO8601 format.
  1076. *
  1077. * @returns {string} A string representing this date in ISO8601 format.
  1078. */
  1079. JulianDate.prototype.toString = function () {
  1080. return JulianDate.toIso8601(this);
  1081. };
  1082. /**
  1083. * Gets or sets the list of leap seconds used throughout Cesium.
  1084. * @memberof JulianDate
  1085. * @type {LeapSecond[]}
  1086. */
  1087. JulianDate.leapSeconds = [
  1088. new LeapSecond(new JulianDate(2441317, 43210.0, TimeStandard.TAI), 10), // January 1, 1972 00:00:00 UTC
  1089. new LeapSecond(new JulianDate(2441499, 43211.0, TimeStandard.TAI), 11), // July 1, 1972 00:00:00 UTC
  1090. new LeapSecond(new JulianDate(2441683, 43212.0, TimeStandard.TAI), 12), // January 1, 1973 00:00:00 UTC
  1091. new LeapSecond(new JulianDate(2442048, 43213.0, TimeStandard.TAI), 13), // January 1, 1974 00:00:00 UTC
  1092. new LeapSecond(new JulianDate(2442413, 43214.0, TimeStandard.TAI), 14), // January 1, 1975 00:00:00 UTC
  1093. new LeapSecond(new JulianDate(2442778, 43215.0, TimeStandard.TAI), 15), // January 1, 1976 00:00:00 UTC
  1094. new LeapSecond(new JulianDate(2443144, 43216.0, TimeStandard.TAI), 16), // January 1, 1977 00:00:00 UTC
  1095. new LeapSecond(new JulianDate(2443509, 43217.0, TimeStandard.TAI), 17), // January 1, 1978 00:00:00 UTC
  1096. new LeapSecond(new JulianDate(2443874, 43218.0, TimeStandard.TAI), 18), // January 1, 1979 00:00:00 UTC
  1097. new LeapSecond(new JulianDate(2444239, 43219.0, TimeStandard.TAI), 19), // January 1, 1980 00:00:00 UTC
  1098. new LeapSecond(new JulianDate(2444786, 43220.0, TimeStandard.TAI), 20), // July 1, 1981 00:00:00 UTC
  1099. new LeapSecond(new JulianDate(2445151, 43221.0, TimeStandard.TAI), 21), // July 1, 1982 00:00:00 UTC
  1100. new LeapSecond(new JulianDate(2445516, 43222.0, TimeStandard.TAI), 22), // July 1, 1983 00:00:00 UTC
  1101. new LeapSecond(new JulianDate(2446247, 43223.0, TimeStandard.TAI), 23), // July 1, 1985 00:00:00 UTC
  1102. new LeapSecond(new JulianDate(2447161, 43224.0, TimeStandard.TAI), 24), // January 1, 1988 00:00:00 UTC
  1103. new LeapSecond(new JulianDate(2447892, 43225.0, TimeStandard.TAI), 25), // January 1, 1990 00:00:00 UTC
  1104. new LeapSecond(new JulianDate(2448257, 43226.0, TimeStandard.TAI), 26), // January 1, 1991 00:00:00 UTC
  1105. new LeapSecond(new JulianDate(2448804, 43227.0, TimeStandard.TAI), 27), // July 1, 1992 00:00:00 UTC
  1106. new LeapSecond(new JulianDate(2449169, 43228.0, TimeStandard.TAI), 28), // July 1, 1993 00:00:00 UTC
  1107. new LeapSecond(new JulianDate(2449534, 43229.0, TimeStandard.TAI), 29), // July 1, 1994 00:00:00 UTC
  1108. new LeapSecond(new JulianDate(2450083, 43230.0, TimeStandard.TAI), 30), // January 1, 1996 00:00:00 UTC
  1109. new LeapSecond(new JulianDate(2450630, 43231.0, TimeStandard.TAI), 31), // July 1, 1997 00:00:00 UTC
  1110. new LeapSecond(new JulianDate(2451179, 43232.0, TimeStandard.TAI), 32), // January 1, 1999 00:00:00 UTC
  1111. new LeapSecond(new JulianDate(2453736, 43233.0, TimeStandard.TAI), 33), // January 1, 2006 00:00:00 UTC
  1112. new LeapSecond(new JulianDate(2454832, 43234.0, TimeStandard.TAI), 34), // January 1, 2009 00:00:00 UTC
  1113. new LeapSecond(new JulianDate(2456109, 43235.0, TimeStandard.TAI), 35), // July 1, 2012 00:00:00 UTC
  1114. new LeapSecond(new JulianDate(2457204, 43236.0, TimeStandard.TAI), 36), // July 1, 2015 00:00:00 UTC
  1115. new LeapSecond(new JulianDate(2457754, 43237.0, TimeStandard.TAI), 37), // January 1, 2017 00:00:00 UTC
  1116. ];
  1117. export default JulianDate;