JulianDate.js 40 KB

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