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
JavaScript
/**
* 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