Home Reference Source

lib6/temporal-types.js

/**
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import * as util from './internal/temporal-util';
import { assertNumberOrInteger, assertString, assertValidDate } from './internal/util';
import { newError } from './error';
import Integer, { int, toNumber, isInt } from './integer';
const IDENTIFIER_PROPERTY_ATTRIBUTES = {
    value: true,
    enumerable: false,
    configurable: false,
    writable: false
};
const DURATION_IDENTIFIER_PROPERTY = '__isDuration__';
const LOCAL_TIME_IDENTIFIER_PROPERTY = '__isLocalTime__';
const TIME_IDENTIFIER_PROPERTY = '__isTime__';
const DATE_IDENTIFIER_PROPERTY = '__isDate__';
const LOCAL_DATE_TIME_IDENTIFIER_PROPERTY = '__isLocalDateTime__';
const DATE_TIME_IDENTIFIER_PROPERTY = '__isDateTime__';
/**
 * Represents an ISO 8601 duration. Contains both date-based values (years, months, days) and time-based values (seconds, nanoseconds).
 * Created `Duration` objects are frozen with `Object.freeze()` in constructor and thus immutable.
 */
export class Duration {
    /**
     * @constructor
     * @param {NumberOrInteger} months - The number of months for the new duration.
     * @param {NumberOrInteger} days - The number of days for the new duration.
     * @param {NumberOrInteger} seconds - The number of seconds for the new duration.
     * @param {NumberOrInteger} nanoseconds - The number of nanoseconds for the new duration.
     */
    constructor(months, days, seconds, nanoseconds) {
        /**
         * The number of months.
         * @type {NumberOrInteger}
         */
        this.months = assertNumberOrInteger(months, 'Months');
        /**
         * The number of days.
         * @type {NumberOrInteger}
         */
        this.days = assertNumberOrInteger(days, 'Days');
        assertNumberOrInteger(seconds, 'Seconds');
        assertNumberOrInteger(nanoseconds, 'Nanoseconds');
        /**
         * The number of seconds.
         * @type {NumberOrInteger}
         */
        this.seconds = util.normalizeSecondsForDuration(seconds, nanoseconds);
        if (typeof seconds === 'number' && isInt(this.seconds)) {
            this.seconds = this.seconds.toNumber();
        }
        if (typeof seconds === 'bigint' && isInt(this.seconds)) {
            this.seconds = this.seconds.toBigInt();
        }
        /**
         * The number of nanoseconds.
         * @type {NumberOrInteger}
         */
        this.nanoseconds = util.normalizeNanosecondsForDuration(nanoseconds);
        if (typeof nanoseconds === 'number' && isInt(this.nanoseconds)) {
            this.nanoseconds = this.nanoseconds.toNumber();
        }
        if (typeof nanoseconds === 'bigint' && isInt(this.nanoseconds)) {
            this.nanoseconds = this.nanoseconds.toBigInt();
        }
        Object.freeze(this);
    }
    /**
     * Creates a {@link Duration} from an ISO 8601 string
     * NOTE: Bolt transmits durations as 4 integers: Months, Days, Seconds and Nanoseconds. Parsing strings like P1.5M4.3D will throw an error. P0.5Y can be sent as it can be simplified as 6 months.
     *
     * @param {string} str The string to convert
     * @returns {Duration}
     */
    static fromString(str) {
        var _a, _b, _c, _d;
        if (str.slice(-1).toUpperCase() === 'T') {
            throw newError(`Duration string '${str}' ends with 'T', time delimiter must be excluded if Duration contains no time components`);
        }
        const matches = String(str.replace(/,/g, '.')).match(new RegExp(/^([-|+]?)[P|p](?:(-?\d*\.?\d*)[Y|y])?(?:(-?\d*\.?\d*)[M|m])?(?:(-?\d*\.?\d*)[W|w])?(?:(-?\d*\.?\d*)[D|d])?/.source + // Date portion
            /([T|t](?:(-?\d*\.?\d*)[H|h])?(?:(-?\d*\.?\d*)[M|m])?(?:(-)?(\d*)(\.\d*)?[S|s])?)?$/.source // Time portion
        ));
        if (matches === null) {
            throw newError(`Duration could not be parsed from string: ${str}`);
        }
        if (matches[2] == null && matches[3] == null && matches[4] == null && matches[5] == null &&
            matches[7] == null && matches[8] == null && matches[10] == null && matches[11] == null) {
            throw newError(`Duration could not be parsed from string: ${str}`);
        }
        const negativeDuration = matches[1] === '-' ? -1 : 1;
        const months = negativeDuration * parseTemporalFloat(matches[2], 'Years') * 12 + parseTemporalFloat(matches[3], 'Months');
        const days = negativeDuration * parseTemporalFloat(matches[4], 'Weeks') * 7 + parseTemporalFloat(matches[5], 'Days');
        const hoursAndMinutes = (parseTemporalFloat(matches[7], 'Hours') * 3600 + parseTemporalFloat(matches[8], 'Minutes') * 60);
        const seconds = parseTemporalInt(((_a = matches[9]) !== null && _a !== void 0 ? _a : '') + ((_b = matches[10]) !== null && _b !== void 0 ? _b : '0'));
        const nanos = negativeDuration * parseDurationNanos(((_c = matches[9]) !== null && _c !== void 0 ? _c : '') + '0' + ((_d = matches[11]) !== null && _d !== void 0 ? _d : ''));
        checkDurationFieldIsInt(months, 'Months');
        checkDurationFieldIsInt(days, 'Days');
        checkDurationFieldIsInt(hoursAndMinutes, 'Seconds');
        checkDurationFieldIsInt(nanos, 'Nanoseconds');
        return new Duration(int(months), int(days), int(BigInt(negativeDuration) * (BigInt(hoursAndMinutes) + seconds.toBigInt())), int(nanos));
    }
    /**
     * @ignore
     */
    toString() {
        return util.durationToIsoString(this.months, this.days, this.seconds, this.nanoseconds);
    }
}
Object.defineProperty(Duration.prototype, DURATION_IDENTIFIER_PROPERTY, IDENTIFIER_PROPERTY_ATTRIBUTES);
/**
 * Test if given object is an instance of {@link Duration} class.
 * @param {Object} obj the object to test.
 * @return {boolean} `true` if given object is a {@link Duration}, `false` otherwise.
 */
export function isDuration(obj) {
    return hasIdentifierProperty(obj, DURATION_IDENTIFIER_PROPERTY);
}
/**
 * Represents an instant capturing the time of day, but not the date, nor the timezone.
 * Created {@link LocalTime} objects are frozen with `Object.freeze()` in constructor and thus immutable.
 */
export class LocalTime {
    /**
     * @constructor
     * @param {NumberOrInteger} hour - The hour for the new local time.
     * @param {NumberOrInteger} minute - The minute for the new local time.
     * @param {NumberOrInteger} second - The second for the new local time.
     * @param {NumberOrInteger} nanosecond - The nanosecond for the new local time.
     */
    constructor(hour, minute, second, nanosecond) {
        /**
         * The hour.
         * @type {NumberOrInteger}
         */
        this.hour = util.assertValidHour(hour);
        /**
         * The minute.
         * @type {NumberOrInteger}
         */
        this.minute = util.assertValidMinute(minute);
        /**
         * The second.
         * @type {NumberOrInteger}
         */
        this.second = util.assertValidSecond(second);
        /**
         * The nanosecond.
         * @type {NumberOrInteger}
         */
        this.nanosecond = util.assertValidNanosecond(nanosecond);
        Object.freeze(this);
    }
    /**
     * Create a {@link LocalTime} object from the given standard JavaScript `Date` and optional nanoseconds.
     * Year, month, day and time zone offset components of the given date are ignored.
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @param {NumberOrInteger|undefined} nanosecond - The optional amount of nanoseconds.
     * @return {LocalTime<number>} New LocalTime.
     */
    static fromStandardDate(standardDate, nanosecond) {
        verifyStandardDateAndNanos(standardDate, nanosecond);
        const totalNanoseconds = util.totalNanoseconds(standardDate, nanosecond);
        return new LocalTime(standardDate.getHours(), standardDate.getMinutes(), standardDate.getSeconds(), totalNanoseconds instanceof Integer
            ? totalNanoseconds.toInt()
            : typeof totalNanoseconds === 'bigint'
                ? int(totalNanoseconds).toInt()
                : totalNanoseconds);
    }
    /**
     * @ignore
     */
    toString() {
        return util.timeToIsoString(this.hour, this.minute, this.second, this.nanosecond);
    }
    /**
     * Creates a {@link LocalTime} from an ISO 8601 string
     *
     * @param {string} str The string to convert
     * @returns {LocalTime}
     */
    static fromString(str) {
        const values = String(str.replace(/,/g, '.')).match(/^[T|t]?(\d{2})(?::?(\d{2}))?(?::?(\d{2}))?(\.\d+)?$/);
        if (values === null) {
            throw newError('LocalTime could not be parsed from string');
        }
        const [hours, minutes, seconds, nanoseconds] = handleTimeDecimals(values[1], values[2], values[3], values[4]);
        return new LocalTime(hours, minutes, seconds, nanoseconds);
    }
}
Object.defineProperty(LocalTime.prototype, LOCAL_TIME_IDENTIFIER_PROPERTY, IDENTIFIER_PROPERTY_ATTRIBUTES);
/**
 * Test if given object is an instance of {@link LocalTime} class.
 * @param {Object} obj the object to test.
 * @return {boolean} `true` if given object is a {@link LocalTime}, `false` otherwise.
 */
export function isLocalTime(obj) {
    return hasIdentifierProperty(obj, LOCAL_TIME_IDENTIFIER_PROPERTY);
}
/**
 * Represents an instant capturing the time of day, and the timezone offset in seconds, but not the date.
 * Created {@link Time} objects are frozen with `Object.freeze()` in constructor and thus immutable.
 */
export class Time {
    /**
     * @constructor
     * @param {NumberOrInteger} hour - The hour for the new local time.
     * @param {NumberOrInteger} minute - The minute for the new local time.
     * @param {NumberOrInteger} second - The second for the new local time.
     * @param {NumberOrInteger} nanosecond - The nanosecond for the new local time.
     * @param {NumberOrInteger} timeZoneOffsetSeconds - The time zone offset in seconds. Value represents the difference, in seconds, from UTC to local time.
     * This is different from standard JavaScript `Date.getTimezoneOffset()` which is the difference, in minutes, from local time to UTC.
     */
    constructor(hour, minute, second, nanosecond, timeZoneOffsetSeconds) {
        /**
         * The hour.
         * @type {NumberOrInteger}
         */
        this.hour = util.assertValidHour(hour);
        /**
         * The minute.
         * @type {NumberOrInteger}
         */
        this.minute = util.assertValidMinute(minute);
        /**
         * The second.
         * @type {NumberOrInteger}
         */
        this.second = util.assertValidSecond(second);
        /**
         * The nanosecond.
         * @type {NumberOrInteger}
         */
        this.nanosecond = util.assertValidNanosecond(nanosecond);
        /**
         * The time zone offset in seconds.
         * @type {NumberOrInteger}
         */
        this.timeZoneOffsetSeconds = assertNumberOrInteger(timeZoneOffsetSeconds, 'Time zone offset in seconds');
        Object.freeze(this);
    }
    /**
     * Create a {@link Time} object from the given standard JavaScript `Date` and optional nanoseconds.
     * Year, month and day components of the given date are ignored.
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @param {NumberOrInteger|undefined} nanosecond - The optional amount of nanoseconds.
     * @return {Time<number>} New Time.
     */
    static fromStandardDate(standardDate, nanosecond) {
        verifyStandardDateAndNanos(standardDate, nanosecond);
        return new Time(standardDate.getHours(), standardDate.getMinutes(), standardDate.getSeconds(), toNumber(util.totalNanoseconds(standardDate, nanosecond)), util.timeZoneOffsetInSeconds(standardDate));
    }
    /**
     * @ignore
     */
    toString() {
        return (util.timeToIsoString(this.hour, this.minute, this.second, this.nanosecond) + util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds));
    }
    /**
     * Creates a {@link Time} from an ISO 8601 string.
     *
     * @param {string} str The string to convert
     * @returns {Time}
     */
    static fromString(str) {
        const values = String(str.replace(/,/g, '.')).match(/^[T|t]?(\d{2})(?::?(\d{2}))?(?::?(\d{2}))?(\.\d+)?([Z|z]$|\+|-)(\d{2})?(?::?(\d{2}))?(?::?(\d{2}))?$/);
        if (values === null) {
            throw newError('Time could not be parsed from string');
        }
        const [hours, minutes, seconds, nanoseconds] = handleTimeDecimals(values[1], values[2], values[3], values[4]);
        if (values[5] === 'Z') {
            return new Time(hours, minutes, seconds, nanoseconds, int(0));
        }
        if (values[6] == null) {
            throw newError('Error parsing timezone offset.');
        }
        return new Time(hours, minutes, seconds, nanoseconds, int((values[5] === '+' ? 1 : -1) * (parseTemporalInt(values[6]).toInt() * 3600 + // timezone offset hours
            parseTemporalInt(values[7]).toInt() * 60 + // timezone offset minutes
            parseTemporalInt(values[8]).toInt() // timezone offset seconds
        )));
    }
}
Object.defineProperty(Time.prototype, TIME_IDENTIFIER_PROPERTY, IDENTIFIER_PROPERTY_ATTRIBUTES);
/**
 * Test if given object is an instance of {@link Time} class.
 * @param {Object} obj the object to test.
 * @return {boolean} `true` if given object is a {@link Time}, `false` otherwise.
 */
export function isTime(obj) {
    return hasIdentifierProperty(obj, TIME_IDENTIFIER_PROPERTY);
}
/**
 * Represents an instant capturing the date, but not the time, nor the timezone.
 * Created {@link Date} objects are frozen with `Object.freeze()` in constructor and thus immutable.
 */
export class Date {
    /**
     * @constructor
     * @param {NumberOrInteger} year - The year for the new local date.
     * @param {NumberOrInteger} month - The month for the new local date.
     * @param {NumberOrInteger} day - The day for the new local date.
     */
    constructor(year, month, day) {
        /**
         * The year.
         * @type {NumberOrInteger}
         */
        this.year = util.assertValidYear(year);
        /**
         * The month.
         * @type {NumberOrInteger}
         */
        this.month = util.assertValidMonth(month);
        /**
         * The day.
         * @type {NumberOrInteger}
         */
        this.day = util.assertValidDay(day);
        Object.freeze(this);
    }
    /**
     * Create a {@link Date} object from the given standard JavaScript `Date`.
     * Hour, minute, second and millisecond components of the given date are ignored.
     *
     * NOTE: the function {@link toStandardDate} and {@link fromStandardDate} are not inverses of one another. {@link fromStandardDate} takes the Day, Month and Year in local time from the supplied JavaScript Date object, while {@link toStandardDate} creates a new JavaScript Date object at midnight UTC.
     *
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @return {Date} New Date.
     * @deprecated use {@link fromStandardDateLocal} which is a drop in replacement, or {@link fromStandardDateUTC} which takes the Year, Month and Date from UTC rather than Local time
     */
    static fromStandardDate(standardDate) {
        return this.fromStandardDateLocal(standardDate);
    }
    /**
     * Create a {@link Date} object from the given standard JavaScript `Date` using the Year, Month and Date in Local Time.
     * Hour, minute, second and millisecond components of the given date are ignored.
     *
     * NOTE: this function and {@link toStandardDate} are not inverses of one another.
     * This takes the Day, Month and Year in local time from the supplied JavaScript Date object, while {@link toStandardDate} creates a new JavaScript Date object at midnight UTC.
     * For a more global approach, use {@link fromStandardDateUTC}, which reads the date in UTC time.
     *
     * @example
     * fromStandardDateLocal(new Date("2010-10-10T00:00:00")) // Will create a date at 2010-10-10 as JS Dates are created at local time by default
     * fromStandardDateLocal(new Date("2010-10-10T00:00:00Z")) // This may cause issues as this date is created at UTC with the trailing "Z"
     *
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @return {Date} New Date.
     */
    static fromStandardDateLocal(standardDate) {
        verifyStandardDateAndNanos(standardDate);
        return new Date(standardDate.getFullYear(), standardDate.getMonth() + 1, standardDate.getDate());
    }
    /**
     * Create a {@link Date} object from the given standard JavaScript `Date` using the Year, Month and Date in UTC time.
     * Hour, minute, second and millisecond components of the given date are ignored.
     *
     * @example
     * fromStandardDateUTC(new Date("2010-10-10T00:00:00")) // This may cause issues as JS Dates are created at local time by default
     * fromStandardDateUTC(new Date("2010-10-10T00:00:00Z")) // Will create a date at 2010-10-10 as this date is created at UTC with the trailing "Z"
     *
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @return {Date} New Date.
     */
    static fromStandardDateUTC(standardDate) {
        verifyStandardDateAndNanos(standardDate);
        return new Date(standardDate.getUTCFullYear(), standardDate.getUTCMonth() + 1, standardDate.getUTCDate());
    }
    /**
     * Convert date to standard JavaScript `Date`.
     *
     * The time component of the returned `Date` is set to midnight
     * and the time zone is set to UTC.
     *
     * @returns {StandardDate} Standard JavaScript `Date` at `00:00:00.000` UTC.
     */
    toStandardDate() {
        return util.isoStringToStandardDate(this.toString());
    }
    /**
     * @ignore
     */
    toString() {
        return util.dateToIsoString(this.year, this.month, this.day);
    }
    /**
     * Creates a {@link Date} from an ISO 8601 string
     *
     * @param {string} str The string to convert
     * @returns {Date}
     */
    static fromString(str) {
        const values = String(str.replace(/,/g, '.')).match(/^([+|-]\d{5,}|\d{4})-(\d{2})-(\d{2})$/);
        if (values === null) {
            throw newError('Date could not be parsed from string. Expects date in format \'YYYY-MM-DD\' or \'[+|-]YYYYY-MM-DD\'');
        }
        return new Date(parseTemporalInt(values[1]), // years
        parseTemporalInt(values[2]), // months
        parseTemporalInt(values[3]) // days
        );
    }
}
Object.defineProperty(Date.prototype, DATE_IDENTIFIER_PROPERTY, IDENTIFIER_PROPERTY_ATTRIBUTES);
/**
 * Test if given object is an instance of {@link Date} class.
 * @param {Object} obj - The object to test.
 * @return {boolean} `true` if given object is a {@link Date}, `false` otherwise.
 */
export function isDate(obj) {
    return hasIdentifierProperty(obj, DATE_IDENTIFIER_PROPERTY);
}
/**
 * Represents an instant capturing the date and the time, but not the timezone.
 * Created {@link LocalDateTime} objects are frozen with `Object.freeze()` in constructor and thus immutable.
 */
export class LocalDateTime {
    /**
     * @constructor
     * @param {NumberOrInteger} year - The year for the new local date.
     * @param {NumberOrInteger} month - The month for the new local date.
     * @param {NumberOrInteger} day - The day for the new local date.
     * @param {NumberOrInteger} hour - The hour for the new local time.
     * @param {NumberOrInteger} minute - The minute for the new local time.
     * @param {NumberOrInteger} second - The second for the new local time.
     * @param {NumberOrInteger} nanosecond - The nanosecond for the new local time.
     */
    constructor(year, month, day, hour, minute, second, nanosecond) {
        /**
         * The year.
         * @type {NumberOrInteger}
         */
        this.year = util.assertValidYear(year);
        /**
         * The month.
         * @type {NumberOrInteger}
         */
        this.month = util.assertValidMonth(month);
        /**
         * The day.
         * @type {NumberOrInteger}
         */
        this.day = util.assertValidDay(day);
        /**
         * The hour.
         * @type {NumberOrInteger}
         */
        this.hour = util.assertValidHour(hour);
        /**
         * The minute.
         * @type {NumberOrInteger}
         */
        this.minute = util.assertValidMinute(minute);
        /**
         * The second.
         * @type {NumberOrInteger}
         */
        this.second = util.assertValidSecond(second);
        /**
         * The nanosecond.
         * @type {NumberOrInteger}
         */
        this.nanosecond = util.assertValidNanosecond(nanosecond);
        Object.freeze(this);
    }
    /**
     * Create a {@link LocalDateTime} object from the given standard JavaScript `Date` and optional nanoseconds.
     * Time zone offset component of the given date is ignored.
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @param {NumberOrInteger|undefined} nanosecond - The optional amount of nanoseconds.
     * @return {LocalDateTime} New LocalDateTime.
     */
    static fromStandardDate(standardDate, nanosecond) {
        verifyStandardDateAndNanos(standardDate, nanosecond);
        return new LocalDateTime(standardDate.getFullYear(), standardDate.getMonth() + 1, standardDate.getDate(), standardDate.getHours(), standardDate.getMinutes(), standardDate.getSeconds(), toNumber(util.totalNanoseconds(standardDate, nanosecond)));
    }
    /**
     * Convert date to standard JavaScript `Date`.
     *
     * @returns {StandardDate} Standard JavaScript `Date` at the local timezone
     */
    toStandardDate() {
        return util.isoStringToStandardDate(this.toString());
    }
    /**
     * @ignore
     */
    toString() {
        return localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond);
    }
    /**
     * Creates a {@link LocalDateTime} from an ISO 8601 string
     *
     * @param {string} str The string to convert
     * @returns {LocalDateTime}
     */
    static fromString(str) {
        const values = String(str.replace(/,/g, '.')).match(/^([+|-]\d{5,}|\d{4})-(\d{2})-(\d{2})[T|t](\d{2})(?::?(\d{2}))?(?::?(\d{2}))?(\.\d+)?$/);
        if (values === null) {
            throw newError('LocalDateTime could not be parsed from string');
        }
        const [hours, minutes, seconds, nanoseconds] = handleTimeDecimals(values[4], values[5], values[6], values[7]);
        return new LocalDateTime(parseTemporalInt(values[1]), // years
        parseTemporalInt(values[2]), // months
        parseTemporalInt(values[3]), // days
        hours, minutes, seconds, nanoseconds);
    }
}
Object.defineProperty(LocalDateTime.prototype, LOCAL_DATE_TIME_IDENTIFIER_PROPERTY, IDENTIFIER_PROPERTY_ATTRIBUTES);
/**
 * Test if given object is an instance of {@link LocalDateTime} class.
 * @param {Object} obj - The object to test.
 * @return {boolean} `true` if given object is a {@link LocalDateTime}, `false` otherwise.
 */
export function isLocalDateTime(obj) {
    return hasIdentifierProperty(obj, LOCAL_DATE_TIME_IDENTIFIER_PROPERTY);
}
/**
 * Represents an instant capturing the date, the time and the timezone identifier.
 * Created {@ DateTime} objects are frozen with `Object.freeze()` in constructor and thus immutable.
 */
export class DateTime {
    /**
     * @constructor
     * @param {NumberOrInteger} year - The year for the new date-time.
     * @param {NumberOrInteger} month - The month for the new date-time.
     * @param {NumberOrInteger} day - The day for the new date-time.
     * @param {NumberOrInteger} hour - The hour for the new date-time.
     * @param {NumberOrInteger} minute - The minute for the new date-time.
     * @param {NumberOrInteger} second - The second for the new date-time.
     * @param {NumberOrInteger} nanosecond - The nanosecond for the new date-time.
     * @param {NumberOrInteger} timeZoneOffsetSeconds - The time zone offset in seconds. Either this argument or `timeZoneId` should be defined.
     * Value represents the difference, in seconds, from UTC to local time.
     * This is different from standard JavaScript `Date.getTimezoneOffset()` which is the difference, in minutes, from local time to UTC.
     * @param {string|null} timeZoneId - The time zone id for the new date-time. Either this argument or `timeZoneOffsetSeconds` should be defined.
     */
    constructor(year, month, day, hour, minute, second, nanosecond, timeZoneOffsetSeconds, timeZoneId) {
        /**
         * The year.
         * @type {NumberOrInteger}
         */
        this.year = util.assertValidYear(year);
        /**
         * The month.
         * @type {NumberOrInteger}
         */
        this.month = util.assertValidMonth(month);
        /**
         * The day.
         * @type {NumberOrInteger}
         */
        this.day = util.assertValidDay(day);
        /**
         * The hour.
         * @type {NumberOrInteger}
         */
        this.hour = util.assertValidHour(hour);
        /**
         * The minute.
         * @type {NumberOrInteger}
         */
        this.minute = util.assertValidMinute(minute);
        /**
         * The second.
         * @type {NumberOrInteger}
         */
        this.second = util.assertValidSecond(second);
        /**
         * The nanosecond.
         * @type {NumberOrInteger}
         */
        this.nanosecond = util.assertValidNanosecond(nanosecond);
        const [offset, id] = verifyTimeZoneArguments(timeZoneOffsetSeconds, timeZoneId);
        /**
         * The time zone offset in seconds.
         *
         * *Either this or {@link timeZoneId} is defined.*
         *
         * @type {NumberOrInteger}
         */
        this.timeZoneOffsetSeconds = offset;
        /**
         * The time zone id.
         *
         * *Either this or {@link timeZoneOffsetSeconds} is defined.*
         *
         * @type {string}
         */
        this.timeZoneId = id !== null && id !== void 0 ? id : undefined;
        Object.freeze(this);
    }
    /**
     * Create a {@link DateTime} object from the given standard JavaScript `Date` and optional nanoseconds.
     * @param {global.Date} standardDate - The standard JavaScript date to convert.
     * @param {NumberOrInteger|undefined} nanosecond - The optional amount of nanoseconds.
     * @return {DateTime} New DateTime.
     */
    static fromStandardDate(standardDate, nanosecond) {
        verifyStandardDateAndNanos(standardDate, nanosecond);
        return new DateTime(standardDate.getFullYear(), standardDate.getMonth() + 1, standardDate.getDate(), standardDate.getHours(), standardDate.getMinutes(), standardDate.getSeconds(), toNumber(util.totalNanoseconds(standardDate, nanosecond)), util.timeZoneOffsetInSeconds(standardDate), null /* no time zone id */);
    }
    /**
     * Convert date to standard JavaScript `Date`.
     *
     * @returns {StandardDate} Standard JavaScript `Date` at the defined time zone offset
     * @throws {Error} If the time zone offset is not defined in the object.
     */
    toStandardDate() {
        return util.toStandardDate(this._toUTC());
    }
    /**
     * @ignore
     */
    toString() {
        var _a;
        const localDateTimeStr = localDateTimeToString(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond);
        const timeOffset = this.timeZoneOffsetSeconds != null
            ? util.timeZoneOffsetToIsoString((_a = this.timeZoneOffsetSeconds) !== null && _a !== void 0 ? _a : 0)
            : '';
        const timeZoneStr = this.timeZoneId != null
            ? `[${this.timeZoneId}]`
            : '';
        return localDateTimeStr + timeOffset + timeZoneStr;
    }
    /**
     * Creates a {@link DateTime} from an ISO 8601 string
     *
     * @param {string} str The string to convert
     * @returns {DateTime}
     */
    static fromString(str) {
        const values = String(str.replace(/,/g, '.')).match(new RegExp(/^([+|-]\d{5,}|\d{4})-(\d{2})-(\d{2})[T|t](\d{2})(?::?(\d{2}))?(?::?(\d{2}))?(\.\d+)?/.source + // DateTime
            /([Z|z]|\+|-)?(?:(\d{2})(?::?(\d{2}))?(?::?(\d{2}))?)?(?:\[([^\]]*)\])?$/.source // Timezone
        ));
        if (values === null) {
            throw newError('DateTime could not be parsed from string');
        }
        const [hours, minutes, seconds, nanoseconds] = handleTimeDecimals(values[4], values[5], values[6], values[7]);
        if (values[8] === 'Z') {
            if (values[9] != null) {
                throw newError('DateTime could not be parsed from string, can not parse string with offset after Z');
            }
            return new DateTime(parseTemporalInt(values[1]), // years
            parseTemporalInt(values[2]), // months
            parseTemporalInt(values[3]), // days
            hours, minutes, seconds, nanoseconds, int(0), values[12]);
        }
        if (values[8] === '+' || values[8] === '-') {
            if (values[9] == null) {
                throw newError(`DateTime could not be parsed from string, could not parse an offset after ${values[8]} sign.`);
            }
            return new DateTime(parseTemporalInt(values[1]), // years
            parseTemporalInt(values[2]), // months
            parseTemporalInt(values[3]), // days
            hours, minutes, seconds, nanoseconds, int((values[8] === '+' ? 1 : -1) * (parseInt(values[9]) * 3600 + parseInt(values[10]) * 60 + parseInt('0' + values[11]))), values[12]);
        }
        if (values[12] !== undefined) {
            return new DateTime(parseTemporalInt(values[1]), // years
            parseTemporalInt(values[2]), // months
            parseTemporalInt(values[3]), // days
            hours, minutes, seconds, nanoseconds, undefined, values[12]);
        }
        throw newError('DateTime could not be parsed from string, provided string needs either timezoneId or offset');
    }
    /**
     * @private
     * @returns {number}
     */
    _toUTC() {
        var _a;
        if (this.timeZoneOffsetSeconds === undefined) {
            throw new Error('Requires DateTime created with time zone offset');
        }
        const epochSecond = util.localDateTimeToEpochSecond(this.year, this.month, this.day, this.hour, this.minute, this.second, this.nanosecond);
        const utcSecond = epochSecond.subtract((_a = this.timeZoneOffsetSeconds) !== null && _a !== void 0 ? _a : 0);
        return int(utcSecond)
            .multiply(1000)
            .add(int(this.nanosecond).div(1000000))
            .toNumber();
    }
}
Object.defineProperty(DateTime.prototype, DATE_TIME_IDENTIFIER_PROPERTY, IDENTIFIER_PROPERTY_ATTRIBUTES);
/**
 * Test if given object is an instance of {@link DateTime} class.
 * @param {Object} obj - The object to test.
 * @return {boolean} `true` if given object is a {@link DateTime}, `false` otherwise.
 */
export function isDateTime(obj) {
    return hasIdentifierProperty(obj, DATE_TIME_IDENTIFIER_PROPERTY);
}
function hasIdentifierProperty(obj, property) {
    return obj != null && obj[property] === true;
}
function localDateTimeToString(year, month, day, hour, minute, second, nanosecond) {
    return (util.dateToIsoString(year, month, day) +
        'T' +
        util.timeToIsoString(hour, minute, second, nanosecond));
}
/**
 * @private
 * @param {NumberOrInteger} timeZoneOffsetSeconds
 * @param {string | null } timeZoneId
 * @returns {Array<NumberOrInteger | undefined | null, string | undefined | null>}
 */
function verifyTimeZoneArguments(timeZoneOffsetSeconds, timeZoneId) {
    const offsetDefined = timeZoneOffsetSeconds !== null && timeZoneOffsetSeconds !== undefined;
    const idDefined = timeZoneId !== null && timeZoneId !== undefined && timeZoneId !== '';
    if (!offsetDefined && !idDefined) {
        throw newError(
        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
        `Unable to create DateTime without either time zone offset or id. Please specify either of them. Given offset: ${timeZoneOffsetSeconds} and id: ${timeZoneId}`);
    }
    const result = [undefined, undefined];
    if (offsetDefined) {
        assertNumberOrInteger(timeZoneOffsetSeconds, 'Time zone offset in seconds');
        result[0] = timeZoneOffsetSeconds;
    }
    if (idDefined) {
        assertString(timeZoneId, 'Time zone ID');
        util.assertValidZoneId('Time zone ID', timeZoneId);
        result[1] = timeZoneId;
    }
    return result;
}
/**
 * @private
 * @param {StandardDate} standardDate
 * @param {NumberOrInteger} nanosecond
 * @returns {void}
 */
function verifyStandardDateAndNanos(standardDate, nanosecond) {
    assertValidDate(standardDate, 'Standard date');
    if (nanosecond !== null && nanosecond !== undefined) {
        assertNumberOrInteger(nanosecond, 'Nanosecond');
    }
}
function parseTemporalFloat(str, field) {
    if (str === undefined || str.length === 0) {
        return 0;
    }
    else {
        const value = parseFloat(str);
        if (isNaN(value)) {
            throw newError(`Failed to parse temporal field ${field}. Got string: ${str}.`);
        }
        return value;
    }
}
function parseTemporalInt(str) {
    if (str === undefined || str.length === 0) {
        return int(0);
    }
    return int(str);
}
function handleTimeDecimals(hourString, minuteString, secondString, decimalString) {
    let hours;
    let minutes;
    let seconds;
    let nanoseconds;
    const decimalNumber = decimalString !== undefined ? parseFloat('0' + decimalString) : 0;
    if (minuteString === undefined || minuteString === '') {
        hours = parseTemporalInt(hourString);
        minutes = int(decimalNumber * 60);
        seconds = int((decimalNumber * 3600) % 60);
        nanoseconds = int((decimalNumber * 3600 * Math.pow(10, 9)) % Math.pow(10, 9));
    }
    else if (secondString === undefined || secondString === '') {
        hours = parseTemporalInt(hourString);
        minutes = parseTemporalInt(minuteString);
        seconds = int((decimalNumber * 60) % 60);
        nanoseconds = int((decimalNumber * 60 * (Math.pow(10, 9))) % Math.pow(10, 9));
    }
    else {
        hours = parseTemporalInt(hourString);
        minutes = parseTemporalInt(minuteString);
        seconds = parseTemporalInt(secondString);
        nanoseconds = int(decimalNumber * Math.pow(10, 9));
    }
    return [hours, minutes, seconds, nanoseconds];
}
function parseDurationNanos(str) {
    return Math.round(parseTemporalFloat(str, 'nanoseconds') * Math.pow(10, 9));
}
function checkDurationFieldIsInt(value, field) {
    if (!Number.isInteger(value)) {
        throw newError(`Parsing Duration field: ${field} resulted in a non-integer value. Bolt protocol can only send durations which can be simplified to integer values of months, days, seconds and nanoseconds.`);
    }
}