UNPKG

timezonecomplete

Version:

DateTime, TimeZone, Duration and Period library aimed at providing a consistent and complete date-time interface, away from the original JavaScript Date class.

1,100 lines 45.1 kB
/** * Copyright(c) 2014 ABB Switzerland Ltd. * * Date+time+timezone representation */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isDateTime = exports.DateTime = exports.now = exports.nowUtc = exports.nowLocal = void 0; var assert_1 = require("./assert"); var basics = require("./basics"); var basics_1 = require("./basics"); var duration_1 = require("./duration"); var error_1 = require("./error"); var format = require("./format"); var javascript_1 = require("./javascript"); var math = require("./math"); var parseFuncs = require("./parse"); var timesource_1 = require("./timesource"); var timezone_1 = require("./timezone"); var tz_database_1 = require("./tz-database"); /** * Current date+time in local time * @throws nothing */ function nowLocal() { return DateTime.nowLocal(); } exports.nowLocal = nowLocal; /** * Current date+time in UTC time * @throws timezonecomplete.NotFound.Zone if the UTC time zone doesn't exist in the time zone database */ function nowUtc() { return DateTime.nowUtc(); } exports.nowUtc = nowUtc; /** * Current date+time in the given time zone * @param timeZone The desired time zone (optional, defaults to UTC). * @throws timezonecomplete.NotFound.Zone if the UTC time zone doesn't exist in the time zone database */ function now(timeZone) { if (timeZone === void 0) { timeZone = timezone_1.TimeZone.utc(); } return DateTime.now(timeZone); } exports.now = now; /** * * @param localTime * @param fromZone * @throws nothing */ function convertToUtc(localTime, fromZone) { if (fromZone) { var offset = fromZone.offsetForZone(localTime); return new basics_1.TimeStruct(localTime.unixMillis - offset * 60000); } else { return localTime.clone(); } } /** * * @param utcTime * @param toZone * @throws nothing */ function convertFromUtc(utcTime, toZone) { /* istanbul ignore else */ if (toZone) { var offset = toZone.offsetForUtc(utcTime); return toZone.normalizeZoneTime(new basics_1.TimeStruct(utcTime.unixMillis + offset * 60000)); } else { return utcTime.clone(); } } /** * DateTime class which is time zone-aware * and which can be mocked for testing purposes. */ var DateTime = /** @class */ (function () { /** * Constructor implementation, @see overrides */ function DateTime(a1, a2, a3, h, m, s, ms, timeZone) { /** * Allow not using instanceof */ this.kind = "DateTime"; switch (typeof (a1)) { case "number": { if (typeof a2 !== "number") { (0, assert_1.default)(a3 === undefined && h === undefined && m === undefined && s === undefined && ms === undefined && timeZone === undefined, "Argument.A3", "for unix timestamp datetime constructor, third through 8th argument must be undefined"); (0, assert_1.default)(a2 === undefined || a2 === null || isTimeZone(a2), "Argument.TimeZone", "DateTime.DateTime(): second arg should be a TimeZone object."); // unix timestamp constructor this._zone = (typeof (a2) === "object" && isTimeZone(a2) ? a2 : undefined); var unixMillis = (0, error_1.convertError)("Argument.UnixMillis", function () { return math.roundSym(a1); }); if (this._zone) { this._zoneDate = this._zone.normalizeZoneTime(new basics_1.TimeStruct(unixMillis)); } else { this._zoneDate = new basics_1.TimeStruct(unixMillis); } } else { // year month day constructor (0, assert_1.default)(typeof (a2) === "number", "Argument.Year", "DateTime.DateTime(): Expect month to be a number."); (0, assert_1.default)(typeof (a3) === "number", "Argument.Month", "DateTime.DateTime(): Expect day to be a number."); (0, assert_1.default)(timeZone === undefined || timeZone === null || isTimeZone(timeZone), "Argument.TimeZone", "DateTime.DateTime(): eighth arg should be a TimeZone object."); var year_1 = a1; var month_1 = a2; var day_1 = a3; var hour_1 = (typeof (h) === "number" ? h : 0); var minute_1 = (typeof (m) === "number" ? m : 0); var second_1 = (typeof (s) === "number" ? s : 0); var milli_1 = (typeof (ms) === "number" ? ms : 0); year_1 = (0, error_1.convertError)("Argument.Year", function () { return math.roundSym(year_1); }); month_1 = (0, error_1.convertError)("Argument.Month", function () { return math.roundSym(month_1); }); day_1 = (0, error_1.convertError)("Argument.Day", function () { return math.roundSym(day_1); }); hour_1 = (0, error_1.convertError)("Argument.Hour", function () { return math.roundSym(hour_1); }); minute_1 = (0, error_1.convertError)("Argument.Minute", function () { return math.roundSym(minute_1); }); second_1 = (0, error_1.convertError)("Argument.Second", function () { return math.roundSym(second_1); }); milli_1 = (0, error_1.convertError)("Argument.Milli", function () { return math.roundSym(milli_1); }); var tm = new basics_1.TimeStruct({ year: year_1, month: month_1, day: day_1, hour: hour_1, minute: minute_1, second: second_1, milli: milli_1 }); this._zone = (typeof (timeZone) === "object" && isTimeZone(timeZone) ? timeZone : undefined); // normalize local time (remove non-existing local time) if (this._zone) { this._zoneDate = this._zone.normalizeZoneTime(tm); } else { this._zoneDate = tm; } } } break; case "string": { if (typeof a2 === "string") { (0, assert_1.default)(h === undefined && m === undefined && s === undefined && ms === undefined && timeZone === undefined, "Argument.A4", "first two arguments are a string, therefore the fourth through 8th argument must be undefined"); (0, assert_1.default)(a3 === undefined || a3 === null || isTimeZone(a3), "Argument.TimeZone", "DateTime.DateTime(): third arg should be a TimeZone object."); // format string given var dateString = a1; var formatString = a2; var zone = void 0; if (typeof a3 === "object" && isTimeZone(a3)) { zone = (a3); } var parsed = parseFuncs.parse(dateString, formatString, zone); this._zoneDate = parsed.time; this._zone = parsed.zone; } else { (0, assert_1.default)(a3 === undefined && h === undefined && m === undefined && s === undefined && ms === undefined && timeZone === undefined, "Argument.A3", "first arguments is a string and the second is not, therefore the third through 8th argument must be undefined"); (0, assert_1.default)(a2 === undefined || a2 === null || isTimeZone(a2), "Argument.TimeZone", "DateTime.DateTime(): second arg should be a TimeZone object."); var givenString = a1.trim(); var ss = DateTime._splitDateFromTimeZone(givenString); (0, assert_1.default)(ss.length === 2, "Argument.S", "Invalid date string given: \"" + a1 + "\""); if (isTimeZone(a2)) { this._zone = (a2); } else { this._zone = (ss[1].trim() ? timezone_1.TimeZone.zone(ss[1]) : undefined); } // use our own ISO parsing because that it platform independent // (free of Date quirks) this._zoneDate = basics_1.TimeStruct.fromString(ss[0]); if (this._zone) { this._zoneDate = this._zone.normalizeZoneTime(this._zoneDate); } } } break; case "object": { if (a1 instanceof Date) { (0, assert_1.default)(h === undefined && m === undefined && s === undefined && ms === undefined && timeZone === undefined, "Argument.A4", "first argument is a Date, therefore the fourth through 8th argument must be undefined"); (0, assert_1.default)(typeof (a2) === "number" && (a2 === javascript_1.DateFunctions.Get || a2 === javascript_1.DateFunctions.GetUTC), "Argument.GetFuncs", "DateTime.DateTime(): for a Date object a DateFunctions must be passed as second argument"); (0, assert_1.default)(a3 === undefined || a3 === null || isTimeZone(a3), "Argument.TimeZone", "DateTime.DateTime(): third arg should be a TimeZone object."); var d = (a1); var dk = (a2); this._zone = (a3 ? a3 : undefined); this._zoneDate = basics_1.TimeStruct.fromDate(d, dk); if (this._zone) { this._zoneDate = this._zone.normalizeZoneTime(this._zoneDate); } } else { // a1 instanceof TimeStruct (0, assert_1.default)(a3 === undefined && h === undefined && m === undefined && s === undefined && ms === undefined && timeZone === undefined, "Argument.A3", "first argument is a TimeStruct, therefore the third through 8th argument must be undefined"); (0, assert_1.default)(a2 === undefined || a2 === null || isTimeZone(a2), "Argument.TimeZone", "expect a TimeZone as second argument"); this._zoneDate = a1.clone(); this._zone = (a2 ? a2 : undefined); } } break; case "undefined": { (0, assert_1.default)(a2 === undefined && a3 === undefined && h === undefined && m === undefined && s === undefined && ms === undefined && timeZone === undefined, "Argument.A2", "first argument is undefined, therefore the rest must also be undefined"); // nothing given, make local datetime this._zone = timezone_1.TimeZone.local(); this._utcDate = basics_1.TimeStruct.fromDate(DateTime.timeSource.now(), javascript_1.DateFunctions.GetUTC); } break; /* istanbul ignore next */ default: /* istanbul ignore next */ throw (0, error_1.error)("Argument.A1", "DateTime.DateTime(): unexpected first argument type."); } } Object.defineProperty(DateTime.prototype, "utcDate", { /** * UTC timestamp (lazily calculated) * @throws nothing */ get: function () { if (!this._utcDate) { this._utcDate = convertToUtc(this._zoneDate, this._zone); } return this._utcDate; }, set: function (value) { this._utcDate = value; this._zoneDate = undefined; }, enumerable: false, configurable: true }); Object.defineProperty(DateTime.prototype, "zoneDate", { /** * Local timestamp (lazily calculated) * @throws nothing */ get: function () { if (!this._zoneDate) { this._zoneDate = convertFromUtc(this._utcDate, this._zone); } return this._zoneDate; }, set: function (value) { this._zoneDate = value; this._utcDate = undefined; }, enumerable: false, configurable: true }); /** * Current date+time in local time * @throws nothing */ DateTime.nowLocal = function () { var n = DateTime.timeSource.now(); return new DateTime(n, javascript_1.DateFunctions.Get, timezone_1.TimeZone.local()); }; /** * Current date+time in UTC time * @throws timezonecomplete.NotFound.Zone if the UTC time zone doesn't exist in the time zone database */ DateTime.nowUtc = function () { return new DateTime(DateTime.timeSource.now(), javascript_1.DateFunctions.GetUTC, timezone_1.TimeZone.utc()); }; /** * Current date+time in the given time zone * @param timeZone The desired time zone (optional, defaults to UTC). * @throws timezonecomplete.NotFound.Zone if the UTC time zone doesn't exist in the time zone database */ DateTime.now = function (timeZone) { if (timeZone === void 0) { timeZone = timezone_1.TimeZone.utc(); } return new DateTime(DateTime.timeSource.now(), javascript_1.DateFunctions.GetUTC, timezone_1.TimeZone.utc()).toZone(timeZone); }; /** * Create a DateTime from a Lotus 123 / Microsoft Excel date-time value * i.e. a double representing days since 1-1-1900 where 1900 is incorrectly seen as leap year * Does not work for dates < 1900 * @param n excel date/time number * @param timeZone Time zone to assume that the excel value is in * @returns a DateTime * @throws timezonecomplete.Argument.N if n is not a finite number * @throws timezonecomplete.Argument.TimeZone if the given time zone is invalid */ DateTime.fromExcel = function (n, timeZone) { (0, assert_1.default)(Number.isFinite(n), "Argument.N", "invalid number"); var unixTimestamp = Math.round((n - 25569) * 24 * 60 * 60 * 1000); return new DateTime(unixTimestamp, timeZone); }; /** * Check whether a given date exists in the given time zone. * E.g. 2015-02-29 returns false (not a leap year) * and 2015-03-29T02:30:00 returns false (daylight saving time missing hour) * and 2015-04-31 returns false (April has 30 days). * By default, pre-1970 dates also return false since the time zone database does not contain accurate info * before that. You can change that with the allowPre1970 flag. * * @param allowPre1970 (optional, default false): return true for pre-1970 dates * @throws nothing */ DateTime.exists = function (year, month, day, hour, minute, second, millisecond, zone, allowPre1970) { if (month === void 0) { month = 1; } if (day === void 0) { day = 1; } if (hour === void 0) { hour = 0; } if (minute === void 0) { minute = 0; } if (second === void 0) { second = 0; } if (millisecond === void 0) { millisecond = 0; } if (allowPre1970 === void 0) { allowPre1970 = false; } if (!isFinite(year) || !isFinite(month) || !isFinite(day) || !isFinite(hour) || !isFinite(minute) || !isFinite(second) || !isFinite(millisecond)) { return false; } if (!allowPre1970 && year < 1970) { return false; } try { var dt = new DateTime(year, month, day, hour, minute, second, millisecond, zone); return (year === dt.year() && month === dt.month() && day === dt.day() && hour === dt.hour() && minute === dt.minute() && second === dt.second() && millisecond === dt.millisecond()); } catch (e) { return false; } }; /** * @return a copy of this object * @throws nothing */ DateTime.prototype.clone = function () { return new DateTime(this.zoneDate, this._zone); }; /** * @return The time zone that the date is in. May be undefined for unaware dates. * @throws nothing */ DateTime.prototype.zone = function () { return this._zone; }; /** * Zone name abbreviation at this time * @param dstDependent (default true) set to false for a DST-agnostic abbreviation * @return The abbreviation * @throws nothing */ DateTime.prototype.zoneAbbreviation = function (dstDependent) { if (dstDependent === void 0) { dstDependent = true; } if (this._zone) { return this._zone.abbreviationForUtc(this.utcDate, dstDependent); } else { return ""; } }; /** * @return the offset including DST w.r.t. UTC in minutes. Returns 0 for unaware dates and for UTC dates. * @throws nothing */ DateTime.prototype.offset = function () { return Math.round((this.zoneDate.unixMillis - this.utcDate.unixMillis) / 60000); }; /** * @return the offset including DST w.r.t. UTC as a Duration. * @throws nothing */ DateTime.prototype.offsetDuration = function () { return duration_1.Duration.milliseconds(Math.round(this.zoneDate.unixMillis - this.utcDate.unixMillis)); }; /** * @return the standard offset WITHOUT DST w.r.t. UTC as a Duration. * @throws nothing */ DateTime.prototype.standardOffsetDuration = function () { if (this._zone) { return duration_1.Duration.minutes(this._zone.standardOffsetForUtc(this.utcDate)); } return duration_1.Duration.minutes(0); }; /** * @return The full year e.g. 2014 * @throws nothing */ DateTime.prototype.year = function () { return this.zoneDate.components.year; }; /** * @return The month 1-12 (note this deviates from JavaScript Date) * @throws nothing */ DateTime.prototype.month = function () { return this.zoneDate.components.month; }; /** * @return The day of the month 1-31 * @throws nothing */ DateTime.prototype.day = function () { return this.zoneDate.components.day; }; /** * @return The hour 0-23 * @throws nothing */ DateTime.prototype.hour = function () { return this.zoneDate.components.hour; }; /** * @return the minutes 0-59 * @throws nothing */ DateTime.prototype.minute = function () { return this.zoneDate.components.minute; }; /** * @return the seconds 0-59 * @throws nothing */ DateTime.prototype.second = function () { return this.zoneDate.components.second; }; /** * @return the milliseconds 0-999 * @throws nothing */ DateTime.prototype.millisecond = function () { return this.zoneDate.components.milli; }; /** * @return the day-of-week (the enum values correspond to JavaScript * week day numbers) * @throws nothing */ DateTime.prototype.weekDay = function () { return basics.weekDayNoLeapSecs(this.zoneDate.unixMillis); }; /** * Returns the day number within the year: Jan 1st has number 0, * Jan 2nd has number 1 etc. * * @return the day-of-year [0-366] * @throws nothing */ DateTime.prototype.dayOfYear = function () { return this.zoneDate.yearDay(); }; /** * The ISO 8601 week number. Week 1 is the week * that has January 4th in it, and it starts on Monday. * See https://en.wikipedia.org/wiki/ISO_week_date * * @return Week number [1-53] * @throws nothing */ DateTime.prototype.weekNumber = function () { return basics.weekNumber(this.year(), this.month(), this.day()); }; /** * The week of this month. There is no official standard for this, * but we assume the same rules for the weekNumber (i.e. * week 1 is the week that has the 4th day of the month in it) * * @return Week number [1-5] * @throws nothing */ DateTime.prototype.weekOfMonth = function () { return basics.weekOfMonth(this.year(), this.month(), this.day()); }; /** * Returns the number of seconds that have passed on the current day * Does not consider leap seconds * * @return seconds [0-86399] * @throws nothing */ DateTime.prototype.secondOfDay = function () { return basics.secondOfDay(this.hour(), this.minute(), this.second()); }; /** * @return Milliseconds since 1970-01-01T00:00:00.000Z * @throws nothing */ DateTime.prototype.unixUtcMillis = function () { return this.utcDate.unixMillis; }; /** * @return The full year e.g. 2014 * @throws nothing */ DateTime.prototype.utcYear = function () { return this.utcDate.components.year; }; /** * @return The UTC month 1-12 (note this deviates from JavaScript Date) * @throws nothing */ DateTime.prototype.utcMonth = function () { return this.utcDate.components.month; }; /** * @return The UTC day of the month 1-31 * @throws nothing */ DateTime.prototype.utcDay = function () { return this.utcDate.components.day; }; /** * @return The UTC hour 0-23 * @throws nothing */ DateTime.prototype.utcHour = function () { return this.utcDate.components.hour; }; /** * @return The UTC minutes 0-59 * @throws nothing */ DateTime.prototype.utcMinute = function () { return this.utcDate.components.minute; }; /** * @return The UTC seconds 0-59 * @throws nothing */ DateTime.prototype.utcSecond = function () { return this.utcDate.components.second; }; /** * Returns the UTC day number within the year: Jan 1st has number 0, * Jan 2nd has number 1 etc. * * @return the day-of-year [0-366] * @throws nothing */ DateTime.prototype.utcDayOfYear = function () { return basics.dayOfYear(this.utcYear(), this.utcMonth(), this.utcDay()); }; /** * @return The UTC milliseconds 0-999 * @throws nothing */ DateTime.prototype.utcMillisecond = function () { return this.utcDate.components.milli; }; /** * @return the UTC day-of-week (the enum values correspond to JavaScript * week day numbers) * @throws nothing */ DateTime.prototype.utcWeekDay = function () { return basics.weekDayNoLeapSecs(this.utcDate.unixMillis); }; /** * The ISO 8601 UTC week number. Week 1 is the week * that has January 4th in it, and it starts on Monday. * See https://en.wikipedia.org/wiki/ISO_week_date * * @return Week number [1-53] * @throws nothing */ DateTime.prototype.utcWeekNumber = function () { return basics.weekNumber(this.utcYear(), this.utcMonth(), this.utcDay()); }; /** * The week of this month. There is no official standard for this, * but we assume the same rules for the weekNumber (i.e. * week 1 is the week that has the 4th day of the month in it) * * @return Week number [1-5] * @throws nothing */ DateTime.prototype.utcWeekOfMonth = function () { return basics.weekOfMonth(this.utcYear(), this.utcMonth(), this.utcDay()); }; /** * Returns the number of seconds that have passed on the current day * Does not consider leap seconds * * @return seconds [0-86399] * @throws nothing */ DateTime.prototype.utcSecondOfDay = function () { return basics.secondOfDay(this.utcHour(), this.utcMinute(), this.utcSecond()); }; /** * Returns a new DateTime which is the date+time reinterpreted as * in the new zone. So e.g. 08:00 America/Chicago can be set to 08:00 Europe/Brussels. * No conversion is done, the value is just assumed to be in a different zone. * Works for naive and aware dates. The new zone may be null. * * @param zone The new time zone * @return A new DateTime with the original timestamp and the new zone. * @throws nothing */ DateTime.prototype.withZone = function (zone) { return new DateTime(this.year(), this.month(), this.day(), this.hour(), this.minute(), this.second(), this.millisecond(), zone); }; /** * Convert this date to the given time zone (in-place). * @return this (for chaining) * @throws timezonecomplete.UnawareToAwareConversion if you try to convert a datetime without a zone to a datetime with a zone */ DateTime.prototype.convert = function (zone) { if (zone) { if (!this._zone) { // if-statement satisfies the compiler return (0, error_1.throwError)("UnawareToAwareConversion", "DateTime.toZone(): Cannot convert unaware date to an aware date"); } else if (this._zone.equals(zone)) { this._zone = zone; // still assign, because zones may be equal but not identical (UTC/GMT/+00) } else { if (!this._utcDate) { this._utcDate = convertToUtc(this._zoneDate, this._zone); // cause zone -> utc conversion } this._zone = zone; this._zoneDate = undefined; } } else { if (!this._zone) { return this; } if (!this._zoneDate) { this._zoneDate = convertFromUtc(this._utcDate, this._zone); } this._zone = undefined; this._utcDate = undefined; // cause later zone -> utc conversion } return this; }; /** * Returns this date converted to the given time zone. * Unaware dates can only be converted to unaware dates (clone) * Converting an unaware date to an aware date throws an exception. Use the constructor * if you really need to do that. * * @param zone The new time zone. This may be null or undefined to create unaware date. * @return The converted date * @throws timezonecomplete.UnawareToAwareConversion if you try to convert a naive datetime to an aware one. */ DateTime.prototype.toZone = function (zone) { if (zone) { (0, assert_1.default)(this._zone, "UnawareToAwareConversion", "DateTime.toZone(): Cannot convert unaware date to an aware date"); var result = new DateTime(); result.utcDate = this.utcDate; result._zone = zone; return result; } else { return new DateTime(this.zoneDate, undefined); } }; /** * Convert to JavaScript date with the zone time in the getX() methods. * Unless the timezone is local, the Date.getUTCX() methods will NOT be correct. * This is because Date calculates getUTCX() from getX() applying local time zone. * @throws nothing */ DateTime.prototype.toDate = function () { return new Date(this.year(), this.month() - 1, this.day(), this.hour(), this.minute(), this.second(), this.millisecond()); }; /** * Create an Excel timestamp for this datetime converted to the given zone. * Does not work for dates < 1900 * @param timeZone Optional. Zone to convert to, default the zone the datetime is already in. * @return an Excel date/time number i.e. days since 1-1-1900 where 1900 is incorrectly seen as leap year * @throws timezonecomplete.UnawareToAwareConversion if you try to convert a naive datetime to an aware one. */ DateTime.prototype.toExcel = function (timeZone) { var dt = this; if (timeZone && (!this._zone || !timeZone.equals(this._zone))) { dt = this.toZone(timeZone); } var offsetMillis = dt.offset() * 60 * 1000; var unixTimestamp = dt.unixUtcMillis(); return this._unixTimeStampToExcel(unixTimestamp + offsetMillis); }; /** * Create an Excel timestamp for this datetime converted to UTC * Does not work for dates < 1900 * @return an Excel date/time number i.e. days since 1-1-1900 where 1900 is incorrectly seen as leap year * @throws nothing */ DateTime.prototype.toUtcExcel = function () { var unixTimestamp = this.unixUtcMillis(); return this._unixTimeStampToExcel(unixTimestamp); }; /** * * @param n * @throws nothing */ DateTime.prototype._unixTimeStampToExcel = function (n) { var result = ((n) / (24 * 60 * 60 * 1000)) + 25569; // round to nearest millisecond var msecs = result / (1 / 86400000); return Math.round(msecs) * (1 / 86400000); }; /** * Implementation. */ DateTime.prototype.add = function (a1, unit) { var amount; var u; if (typeof (a1) === "object") { var duration = (a1); amount = duration.amount(); u = duration.unit(); } else { amount = (a1); u = unit; } var utcTm = this._addToTimeStruct(this.utcDate, amount, u); return new DateTime(utcTm, timezone_1.TimeZone.utc()).toZone(this._zone); }; DateTime.prototype.addLocal = function (a1, unit) { var amount; var u; if (typeof (a1) === "object") { var duration = (a1); amount = duration.amount(); u = duration.unit(); } else { amount = (a1); u = unit; } var localTm = this._addToTimeStruct(this.zoneDate, amount, u); if (this._zone) { var direction = (amount >= 0 ? tz_database_1.NormalizeOption.Up : tz_database_1.NormalizeOption.Down); var normalized = this._zone.normalizeZoneTime(localTm, direction); return new DateTime(normalized, this._zone); } else { return new DateTime(localTm, undefined); } }; /** * Add an amount of time to the given time struct. Note: does not normalize. * Keeps lower unit fields the same where possible, clamps day to end-of-month if * necessary. * @throws Argument.Amount if amount is not finite or if it's not an integer and you're adding months or years * @throws Argument.Unit for invalid time unit */ DateTime.prototype._addToTimeStruct = function (tm, amount, unit) { (0, assert_1.default)(Number.isFinite(amount), "Argument.Amount", "amount must be a finite number"); var year; var month; var day; var hour; var minute; var second; var milli; switch (unit) { case basics_1.TimeUnit.Millisecond: return new basics_1.TimeStruct(math.roundSym(tm.unixMillis + amount)); case basics_1.TimeUnit.Second: return new basics_1.TimeStruct(math.roundSym(tm.unixMillis + amount * 1000)); case basics_1.TimeUnit.Minute: // todo more intelligent approach needed when implementing leap seconds return new basics_1.TimeStruct(math.roundSym(tm.unixMillis + amount * 60000)); case basics_1.TimeUnit.Hour: // todo more intelligent approach needed when implementing leap seconds return new basics_1.TimeStruct(math.roundSym(tm.unixMillis + amount * 3600000)); case basics_1.TimeUnit.Day: // todo more intelligent approach needed when implementing leap seconds return new basics_1.TimeStruct(math.roundSym(tm.unixMillis + amount * 86400000)); case basics_1.TimeUnit.Week: // todo more intelligent approach needed when implementing leap seconds return new basics_1.TimeStruct(math.roundSym(tm.unixMillis + amount * 7 * 86400000)); case basics_1.TimeUnit.Month: { (0, assert_1.default)(math.isInt(amount), "Argument.Amount", "Cannot add/sub a non-integer amount of months"); // keep the day-of-month the same (clamp to end-of-month) if (amount >= 0) { year = tm.components.year + Math.ceil((amount - (12 - tm.components.month)) / 12); month = 1 + math.positiveModulo((tm.components.month - 1 + Math.floor(amount)), 12); } else { year = tm.components.year + Math.floor((amount + (tm.components.month - 1)) / 12); month = 1 + math.positiveModulo((tm.components.month - 1 + Math.ceil(amount)), 12); } day = Math.min(tm.components.day, basics.daysInMonth(year, month)); hour = tm.components.hour; minute = tm.components.minute; second = tm.components.second; milli = tm.components.milli; return new basics_1.TimeStruct({ year: year, month: month, day: day, hour: hour, minute: minute, second: second, milli: milli }); } case basics_1.TimeUnit.Year: { (0, assert_1.default)(math.isInt(amount), "Argument.Amount", "Cannot add/sub a non-integer amount of years"); year = tm.components.year + amount; month = tm.components.month; day = Math.min(tm.components.day, basics.daysInMonth(year, month)); hour = tm.components.hour; minute = tm.components.minute; second = tm.components.second; milli = tm.components.milli; return new basics_1.TimeStruct({ year: year, month: month, day: day, hour: hour, minute: minute, second: second, milli: milli }); } /* istanbul ignore next */ default: /* istanbul ignore next */ return (0, error_1.throwError)("Argument.Unit", "invalid time unit"); } }; DateTime.prototype.sub = function (a1, unit) { if (typeof a1 === "number") { var amount = a1; return this.add(-1 * amount, unit); } else { var duration = a1; return this.add(duration.multiply(-1)); } }; DateTime.prototype.subLocal = function (a1, unit) { if (typeof a1 === "number") { return this.addLocal(-1 * a1, unit); } else { return this.addLocal(a1.multiply(-1)); } }; /** * Time difference between two DateTimes * @return this - other * @throws nothing */ DateTime.prototype.diff = function (other) { return new duration_1.Duration(this.utcDate.unixMillis - other.utcDate.unixMillis); }; /** * Chops off the time part, yields the same date at 00:00:00.000 * @return a new DateTime * @throws nothing */ DateTime.prototype.startOfDay = function () { return new DateTime(this.year(), this.month(), this.day(), 0, 0, 0, 0, this.zone()); }; /** * Returns the first day of the month at 00:00:00 * @return a new DateTime * @throws nothing */ DateTime.prototype.startOfMonth = function () { return new DateTime(this.year(), this.month(), 1, 0, 0, 0, 0, this.zone()); }; /** * Returns the first day of the year at 00:00:00 * @return a new DateTime * @throws nothing */ DateTime.prototype.startOfYear = function () { return new DateTime(this.year(), 1, 1, 0, 0, 0, 0, this.zone()); }; /** * @return True iff (this < other) * @throws nothing */ DateTime.prototype.lessThan = function (other) { return this.utcDate.unixMillis < other.utcDate.unixMillis; }; /** * @return True iff (this <= other) * @throws nothing */ DateTime.prototype.lessEqual = function (other) { return this.utcDate.unixMillis <= other.utcDate.unixMillis; }; /** * @return True iff this and other represent the same moment in time in UTC * @throws nothing */ DateTime.prototype.equals = function (other) { return this.utcDate.equals(other.utcDate); }; /** * @return True iff this and other represent the same time and the same zone * @throws nothing */ DateTime.prototype.identical = function (other) { return !!(this.zoneDate.equals(other.zoneDate) && (!this._zone) === (!other._zone) && ((!this._zone && !other._zone) || (this._zone && other._zone && this._zone.identical(other._zone)))); }; /** * @return True iff this > other * @throws nothing */ DateTime.prototype.greaterThan = function (other) { return this.utcDate.unixMillis > other.utcDate.unixMillis; }; /** * @return True iff this >= other * @throws nothing */ DateTime.prototype.greaterEqual = function (other) { return this.utcDate.unixMillis >= other.utcDate.unixMillis; }; /** * @return The minimum of this and other * @throws nothing */ DateTime.prototype.min = function (other) { if (this.lessThan(other)) { return this.clone(); } return other.clone(); }; /** * @return The maximum of this and other * @throws nothing */ DateTime.prototype.max = function (other) { if (this.greaterThan(other)) { return this.clone(); } return other.clone(); }; /** * Proper ISO 8601 format string with any IANA zone converted to ISO offset * E.g. "2014-01-01T23:15:33+01:00" for Europe/Amsterdam * Unaware dates have no zone information at the end. * @throws nothing */ DateTime.prototype.toIsoString = function () { var s = this.zoneDate.toString(); if (this._zone) { return s + timezone_1.TimeZone.offsetToString(this.offset()); // convert IANA name to offset } else { return s; // no zone present } }; /** * Convert to UTC and then return ISO string ending in 'Z'. This is equivalent to Date#toISOString() * e.g. "2014-01-01T23:15:33 Europe/Amsterdam" becomes "2014-01-01T22:15:33Z". * Unaware dates are assumed to be in UTC * @throws timezonecomplete.NotFound.Zone if the UTC time zone doesn't exist in the time zone database */ DateTime.prototype.toUtcIsoString = function () { if (this._zone) { return this.toZone(timezone_1.TimeZone.utc()).format("yyyy-MM-ddTHH:mm:ss.SSSZZZZZ"); } else { return this.withZone(timezone_1.TimeZone.utc()).format("yyyy-MM-ddTHH:mm:ss.SSSZZZZZ"); } }; /** * Return a string representation of the DateTime according to the * specified format. See LDML.md for supported formats. * * @param formatString The format specification (e.g. "dd/MM/yyyy HH:mm:ss") * @param locale Optional, non-english format month names etc. * @return The string representation of this DateTime * @throws timezonecomplete.Argument.FormatString for invalid format pattern */ DateTime.prototype.format = function (formatString, locale) { return format.format(this.zoneDate, this.utcDate, this._zone, formatString, locale); }; /** * Parse a date in a given format * @param s the string to parse * @param format the format the string is in. See LDML.md for supported formats. * @param zone Optional, the zone to add (if no zone is given in the string) * @param locale Optional, different settings for constants like 'AM' etc * @param allowTrailing Allow trailing characters in the source string * @throws timezonecomplete.ParseError if the given dateTimeString is wrong or not according to the pattern * @throws timezonecomplete.Argument.FormatString if the given format string is invalid */ DateTime.parse = function (s, format, zone, locale, allowTrailing) { var parsed = parseFuncs.parse(s, format, zone, allowTrailing || false, locale); try { return new DateTime(parsed.time, parsed.zone); } catch (e) { if (!(0, error_1.errorIs)(e, "InvalidTimeZoneData")) { e = (0, error_1.error)("ParseError", e.message); } throw e; } }; /** * Modified ISO 8601 format string with IANA name if applicable. * E.g. "2014-01-01T23:15:33.000 Europe/Amsterdam" * @throws nothing */ DateTime.prototype.toString = function () { var s = this.zoneDate.toString(); if (this._zone) { if (this._zone.kind() !== timezone_1.TimeZoneKind.Offset) { return s + " " + this._zone.toString(); // separate IANA name or "localtime" with a space } else { return s + this._zone.toString(); // do not separate ISO zone } } else { return s; // no zone present } }; /** * The valueOf() method returns the primitive value of the specified object. * @throws nothing */ DateTime.prototype.valueOf = function () { return this.unixUtcMillis(); }; /** * Modified ISO 8601 format string in UTC without time zone info * @throws nothing */ DateTime.prototype.toUtcString = function () { return this.utcDate.toString(); }; /** * Split a combined ISO datetime and timezone into datetime and timezone * @throws nothing */ DateTime._splitDateFromTimeZone = function (s) { var trimmed = s.trim(); var result = ["", ""]; var index = trimmed.lastIndexOf("without DST"); if (index > -1) { var result_1 = DateTime._splitDateFromTimeZone(s.slice(0, index - 1)); result_1[1] += " without DST"; return result_1; } index = trimmed.lastIndexOf(" "); if (index > -1) { result[0] = trimmed.substr(0, index); result[1] = trimmed.substr(index + 1); return result; } index = trimmed.lastIndexOf("Z"); if (index > -1) { result[0] = trimmed.substr(0, index); result[1] = trimmed.substr(index, 1); return result; } index = trimmed.lastIndexOf("+"); if (index > -1) { result[0] = trimmed.substr(0, index); result[1] = trimmed.substr(index); return result; } index = trimmed.lastIndexOf("-"); if (index < 8) { index = -1; // any "-" we found was a date separator } if (index > -1) { result[0] = trimmed.substr(0, index); result[1] = trimmed.substr(index); return result; } result[0] = trimmed; return result; }; /** * Actual time source in use. Setting this property allows to * fake time in tests. DateTime.nowLocal() and DateTime.nowUtc() * use this property for obtaining the current time. */ DateTime.timeSource = new timesource_1.RealTimeSource(); return DateTime; }()); exports.DateTime = DateTime; /** * Checks whether `a` is similar to a TimeZone without using the instanceof operator. * It checks for the availability of the functions used in the DateTime implementation * @param a the object to check * @returns a is TimeZone-like * @throws nothing */ function isTimeZone(a) { if (a && typeof a === "object") { if (typeof a.normalizeZoneTime === "function" && typeof a.abbreviationForUtc === "function" && typeof a.standardOffsetForUtc === "function" && typeof a.identical === "function" && typeof a.equals === "function" && typeof a.kind === "function" && typeof a.clone === "function") { return true; } } return false; } /** * Checks if a given object is of type DateTime. Note that it does not work for sub classes. However, use this to be robust * against different versions of the library in one process instead of instanceof * @param value Value to check * @throws nothing */ function isDateTime(value) { return typeof value === "object" && value !== null && value.kind === "DateTime"; } exports.isDateTime = isDateTime; //# sourceMappingURL=datetime.js.map