UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

116 lines (115 loc) 4.64 kB
import { ValueError } from "../error/ValueError.js"; import { DAY, HOUR, MINUTE, SECOND } from "./constants.js"; import { getDate } from "./date.js"; import { wrapNumber } from "./number.js"; /** Class representing a time in the day in 24 hour format in the user's current locale. */ export class Time { /** Make a new `Time` instance from a time string. */ static from(value) { if (value === undefined || value === null || value === "") return undefined; if (isTime(value)) return value; if (typeof value === "string") { const matches = value.match(TIME_REGEXP); if (matches) { const [, h, m, s, ms] = matches; return new Time(Number.parseInt(h, 10) * HOUR + Number.parseInt(m, 10) * MINUTE + (typeof s === "string" ? Number.parseInt(s, 10) * SECOND : 0) + (typeof ms === "string" ? Number.parseInt(ms, 10) : 0)); } } const date = getDate(value); if (date) return new Time(date.getHours() * HOUR + date.getMinutes() * MINUTE + date.getSeconds() * SECOND + date.getMilliseconds()); } /* Total number of milliseconds in this time (always a number between `0` and `86400000` because higher/lower numbers wrap into the next/previous day). */ time; constructor(time) { this.time = wrapNumber(Math.round(time), 0, DAY); } /** Get the number of hours in this time. */ get h() { return Math.trunc(this.time / HOUR); } /** Get the number of minutes in this time. */ get m() { return Math.trunc((this.time % HOUR) / MINUTE); } /** Get the number of seconds in this time. */ get s() { return Math.trunc((this.time % MINUTE) / SECOND); } /** Get the number of seconds in this time. */ get ms() { return this.time % SECOND; } /** Get the time as in `hh:mm` format (hours, minutes), e.g. `13.59` */ get short() { return `${_pad(this.h, 2)}:${_pad(this.m, 2)}`; } /** Get the time in `hh:mm:ss` format (hours, minutes seconds), e.g. `13.16.19.123` */ get medium() { return `${_pad(this.h, 2)}:${_pad(this.m, 2)}:${_pad(this.s, 2)}`; } /** Get this time in `hh:mm:ss.fff` format (ISO 8601 compatible, hours, minutes, seconds, milliseconds), e.g. `13:16:19.123` */ get long() { return `${_pad(this.h, 2)}:${_pad(this.m, 2)}:${_pad(this.s, 2)}.${_pad(this.ms, 3)}`; } /** Get a date corresponding to this time. */ get date() { const date = new Date(); date.setHours(this.h, this.m, this.s, this.ms); return date; } /** * Format this time using the browser locale settings with a specified amount of precision. * @param precision Reveal additional parts of the time, e.g. `2` shows hours and minutes, `3` also shows seconds, and `4 | 5 | 6` show mlliseconds at increasing precision. */ format(precision = 2) { return this.date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", second: precision >= 3 ? "2-digit" : undefined, fractionalSecondDigits: precision >= 4 ? (precision - 3) : undefined, }); } // Implement `valueOf()` valueOf() { return this.time; } // Implement `toString()` toString() { return this.long; } } function _pad(num, size) { return num.toString(10).padStart(size, "0000"); } /** Regular expression that matches a time in ISO 8601 format. */ const TIME_REGEXP = /([0-9]+):([0-9]+)(?::([0-9]+)(?:.([0-9]+))?)?/; /** Is an unknown value a `Time` instance. */ export function isTime(value) { return value instanceof Time; } /** * Convert a value to a `Time` instance, or return `undefined` if it couldn't be converted. * - Works with possible dates, e.g. `now` or `Date` or `2022-09-12 18:32` or `19827263567` * - Works with time strings, e.g. `18:32` or `23:59:59.999` * * @param value Any value that we want to parse as a valid time (defaults to `undefined`). */ export function getTime(value) { return Time.from(value); } /** * Convert a possible date to a `Time` instance, or throw `ValueError` if it couldn't be converted (defaults to `"now"`). * @param value Any value that we want to parse as a valid time (defaults to `"now"`). */ export function requireTime(value = "now", caller = requireTime) { const time = Time.from(value); if (!time) throw new ValueError("Invalid time", { received: value, caller }); return time; }