Source: types/local-time.js

"use strict";
const Long = require("long");
const util = require("util");
const utils = require("../utils");
const rust = require("../../index");
const { bigintToLong, longToBigint } = require("../new-utils");

/** @module types */

/**
 * @const
 * @private
 * */
const maxNanos = Long.fromString("86399999999999");
/**
 * Nanoseconds in a millisecond
 * @const
 * @private
 * */
const nanoSecInMillis = Long.fromNumber(1000000);
/**
 * Milliseconds in day
 * @const
 * @private
 * */
const millisInDay = 86400000;

/**
 * A time without a time-zone in the ISO-8601 calendar system, such as 10:30:05.
 * LocalTime is an immutable date-time object that represents a time,
 * often viewed as hour-minute-second. Time is represented to nanosecond precision.
 * For example, the value "13:45.30.123456789" can be stored in a LocalTime.
 */
class LocalTime {
    /**
     * @type {rust.LocalTimeWrapper}
     * @private
     */
    #internal;

    /**
     * Creates a new instance of LocalTime.
     * @param {Long} totalNanoseconds Total nanoseconds since midnight.
     */
    constructor(totalNanoseconds) {
        if (!(totalNanoseconds instanceof Long)) {
            throw new TypeError(
                "You must specify a Long value as totalNanoseconds",
            );
        }
        if (
            totalNanoseconds.lessThan(Long.ZERO) ||
            totalNanoseconds.greaterThan(maxNanos)
        ) {
            throw new TypeError("Total nanoseconds out of range");
        }

        this.#internal = rust.LocalTimeWrapper.new(
            longToBigint(totalNanoseconds),
        );
    }

    /**
     * Gets the nanoseconds, a number from 0 to 999 999 999.
     * @readonly
     * @type {Number}
     */
    get nanosecond() {
        return this.#internal.nanosecond;
    }

    set nanosecond(_) {
        throw new SyntaxError("LocalTime nanosecond is read-only");
    }

    /**
     * Gets the seconds, a number from 0 to 59.
     * @readonly
     * @type {Number}
     */
    get second() {
        return this.#internal.second;
    }

    set second(_) {
        throw new SyntaxError("LocalTime second is read-only");
    }

    /**
     * Gets the minutes, a number from 0 to 59.
     * @readonly
     * @type {Number}
     */
    get minute() {
        return this.#internal.minute;
    }

    set minute(_) {
        throw new SyntaxError("LocalTime minute is read-only");
    }

    /**
     * Gets the hours, a number from 0 to 24.
     * @readonly
     * @type {Number}
     */
    get hour() {
        return this.#internal.hour;
    }

    set hour(_) {
        throw new SyntaxError("LocalTime hour is read-only");
    }

    /**
     * Parses a string representation and returns a new LocalDate.
     * @param {string} value
     * accepted format: hh:MM:ss.ns
     * @returns {LocalTime}
     */
    static fromString(value) {
        if (typeof value !== "string") {
            throw new TypeError(
                `Argument type invalid: ${util.inspect(value)}, expected string type`,
            );
        }
        // This regex accepts the time in the format hh:MM:ss.ns
        const regex = /^(?:[01]?\d|2[0-4]):[0-5]?\d:[0-5]?\d(?:\.\d{1,9})?$/;

        if (!regex.test(value)) {
            throw new TypeError(`Argument format is invalid`);
        }

        return new LocalTime(
            bigintToLong(rust.LocalTimeWrapper.fromString(value)),
        );
    }

    /**
     * Uses the current local time (in milliseconds) and the nanoseconds to create a new instance of LocalTime
     * @param {Number} [nanoseconds=0] A Number from 0 to 999 999 999 representing the time nanosecond portion.
     * @returns {LocalTime}
     */
    static now(nanoseconds) {
        return LocalTime.fromDate(new Date(), nanoseconds);
    }

    /**
     * Uses the provided local time (in milliseconds) and the nanoseconds to create a new instance of LocalTime
     * @param {Date} date Local date portion to extract the time passed since midnight.
     * @param {Number} [nanoseconds=0] A Number from 0 to 999 999 999 representing the nanosecond time portion.
     * @returns {LocalTime}
     */
    static fromDate(date, nanoseconds) {
        if (!(date instanceof Date)) {
            throw new TypeError("Not a valid date");
        }
        // Uses the local date with the time zone, takes the number of milliseconds from midnight.
        const millis =
            (date.getTime() + date.getTimezoneOffset() * -60000) % millisInDay;
        return LocalTime.fromMilliseconds(millis, nanoseconds);
    }

    /**
     * Uses the provided local time (in milliseconds) and the nanoseconds to create a new instance of LocalTime
     * @param {Number} milliseconds A Number from 0 to 86 399 999.
     * @param {Number} [nanoseconds=0] A Number from 0 to 999 999 999 representing the time nanosecond portion.
     * @returns {LocalTime}
     */
    static fromMilliseconds(milliseconds, nanoseconds) {
        if (typeof nanoseconds !== "number") {
            nanoseconds = 0;
        }
        return new LocalTime(
            Long.fromNumber(milliseconds)
                .multiply(nanoSecInMillis)
                .add(Long.fromNumber(nanoseconds)),
        );
    }

    /**
     * Creates a new instance of LocalTime from the bytes representation.
     * @param {Buffer} value
     * @returns {LocalTime}
     */
    static fromBuffer(value) {
        if (!(value instanceof Buffer)) {
            throw new TypeError(
                `Expected Buffer, obtained ${utils.util.inspect(value)}`,
            );
        }
        return new LocalTime(
            new Long(value.readInt32BE(4), value.readInt32BE(0)),
        );
    }

    /**
     * Compares this LocalTime with the given one.
     * @param {LocalTime} other time to compare against.
     * @return {number} 0 if they are the same, 1 if this object is larger,
     * -1 if the given object is larger.
     */
    compare(other) {
        return this.getTotalNanoseconds().compare(other.getTotalNanoseconds());
    }

    /**
     * Returns true if the objects are the same, false otherwise.
     * @param {LocalTime} other
     * @returns {Boolean}
     */
    equals(other) {
        return other instanceof LocalTime && this.compare(other) === 0;
    }

    /**
     * Gets the total amount of nanoseconds since midnight for this instance.
     * @return {Long}
     */
    getTotalNanoseconds() {
        return bigintToLong(this.#internal.value);
    }

    /**
     * Provide the name of the constructor and the string representation
     * @returns {string}
     */
    inspect() {
        return `${this.constructor.name}: ${this.toString()}`;
    }

    /**
     * Returns a big-endian bytes representation of the instance
     * @returns {Buffer}
     */
    toBuffer() {
        const buffer = utils.allocBufferUnsafe(8);
        buffer.writeUInt32BE(
            this.getTotalNanoseconds().getHighBitsUnsigned(),
            0,
        );
        buffer.writeUInt32BE(
            this.getTotalNanoseconds().getLowBitsUnsigned(),
            4,
        );
        return buffer;
    }

    /**
     * Returns the string representation of the instance in the form of hh:MM:ss.ns
     * @returns {string}
     */
    toString() {
        return this.#internal.toString();
    }

    /**
     * Gets the string representation of the instance in the form: hh:MM:ss.ns
     * @returns {string}
     */
    toJSON() {
        return this.toString();
    }

    /**
     * Get LocalTime from rust object. Not intended to be exposed in the API
     * @package
     * @param {rust.LocalTimeWrapper} arg
     * @returns {LocalTime}
     */
    static fromRust(arg) {
        return new LocalTime(bigintToLong(arg.value));
    }

    /**
     * @package
     * @returns {rust.LocalTimeWrapper}
     */
    getInternal() {
        return this.#internal;
    }
}
module.exports = LocalTime;