import * as $bool from "../../../gleam_stdlib/gleam/bool.mjs"; import * as $int from "../../../gleam_stdlib/gleam/int.mjs"; import * as $order from "../../../gleam_stdlib/gleam/order.mjs"; import * as $string from "../../../gleam_stdlib/gleam/string.mjs"; import { CustomType as $CustomType, remainderInt, divideInt, isEqual } from "../../gleam.mjs"; class Duration extends $CustomType { constructor(seconds, nanoseconds) { super(); this.seconds = seconds; this.nanoseconds = nanoseconds; } } export class Nanosecond extends $CustomType {} export const Unit$Nanosecond = () => new Nanosecond(); export const Unit$isNanosecond = (value) => value instanceof Nanosecond; export class Microsecond extends $CustomType {} export const Unit$Microsecond = () => new Microsecond(); export const Unit$isMicrosecond = (value) => value instanceof Microsecond; export class Millisecond extends $CustomType {} export const Unit$Millisecond = () => new Millisecond(); export const Unit$isMillisecond = (value) => value instanceof Millisecond; export class Second extends $CustomType {} export const Unit$Second = () => new Second(); export const Unit$isSecond = (value) => value instanceof Second; export class Minute extends $CustomType {} export const Unit$Minute = () => new Minute(); export const Unit$isMinute = (value) => value instanceof Minute; export class Hour extends $CustomType {} export const Unit$Hour = () => new Hour(); export const Unit$isHour = (value) => value instanceof Hour; export class Day extends $CustomType {} export const Unit$Day = () => new Day(); export const Unit$isDay = (value) => value instanceof Day; export class Week extends $CustomType {} export const Unit$Week = () => new Week(); export const Unit$isWeek = (value) => value instanceof Week; export class Month extends $CustomType {} export const Unit$Month = () => new Month(); export const Unit$isMonth = (value) => value instanceof Month; export class Year extends $CustomType {} export const Unit$Year = () => new Year(); export const Unit$isYear = (value) => value instanceof Year; /** * Ensure the duration is represented with `nanoseconds` being positive and * less than 1 second. * * This function does not change the amount of time that the duratoin refers * to, it only adjusts the values used to represent the time. * * @ignore */ function normalise(duration) { let multiplier = 1_000_000_000; let nanoseconds$1 = remainderInt(duration.nanoseconds, multiplier); let overflow = duration.nanoseconds - nanoseconds$1; let seconds$1 = duration.seconds + (divideInt(overflow, multiplier)); let $ = nanoseconds$1 >= 0; if ($) { return new Duration(seconds$1, nanoseconds$1); } else { return new Duration(seconds$1 - 1, multiplier + nanoseconds$1); } } /** * Convert a duration to a number of the largest number of a unit, serving as * a rough description of the duration that a human can understand. * * The size used for each unit are described in the documentation for the * `Unit` type. * * ```gleam * seconds(125) * |> approximate * // -> #(2, Minute) * ``` * * This function rounds _towards zero_. This means that if a duration is just * short of 2 days then it will approximate to 1 day. * * ```gleam * hours(47) * |> approximate * // -> #(1, Day) * ``` */ export function approximate(duration) { let s; let ns; s = duration.seconds; ns = duration.nanoseconds; let minute = 60; let hour = minute * 60; let day = hour * 24; let week = day * 7; let year = day * 365 + hour * 6; let month = globalThis.Math.trunc(year / 12); let microsecond = 1000; let millisecond = microsecond * 1000; let $ = undefined; if (s < 0) { let _block; let _pipe = new Duration(- s, - ns); let _pipe$1 = normalise(_pipe); _block = approximate(_pipe$1); let $1 = _block; let amount; let unit; amount = $1[0]; unit = $1[1]; return [- amount, unit]; } else if (s >= year) { return [divideInt(s, year), new Year()]; } else if (s >= month) { return [divideInt(s, month), new Month()]; } else if (s >= week) { return [divideInt(s, week), new Week()]; } else if (s >= day) { return [divideInt(s, day), new Day()]; } else if (s >= hour) { return [divideInt(s, hour), new Hour()]; } else if (s >= minute) { return [divideInt(s, minute), new Minute()]; } else if (s > 0) { return [s, new Second()]; } else if (ns >= millisecond) { return [divideInt(ns, millisecond), new Millisecond()]; } else if (ns >= microsecond) { return [divideInt(ns, microsecond), new Microsecond()]; } else { return [ns, new Nanosecond()]; } } /** * Compare one duration to another, indicating whether the first spans a * larger amount of time (and so is greater) or smaller amount of time (and so * is lesser) than the second. * * # Examples * * ```gleam * compare(seconds(1), seconds(2)) * // -> order.Lt * ``` * * Whether a duration is negative or positive doesn't matter for comparing * them, only the amount of time spanned matters. * * ```gleam * compare(seconds(-2), seconds(1)) * // -> order.Gt * ``` */ export function compare(left, right) { let parts = (x) => { let $ = x.seconds >= 0; if ($) { return [x.seconds, x.nanoseconds]; } else { return [x.seconds * -1 - 1, 1_000_000_000 - x.nanoseconds]; } }; let $ = parts(left); let ls; let lns; ls = $[0]; lns = $[1]; let $1 = parts(right); let rs; let rns; rs = $1[0]; rns = $1[1]; let _pipe = $int.compare(ls, rs); return $order.break_tie(_pipe, $int.compare(lns, rns)); } /** * Calculate the difference between two durations. * * This is effectively substracting the first duration from the second. * * # Examples * * ```gleam * difference(seconds(1), seconds(5)) * // -> seconds(4) * ``` */ export function difference(left, right) { let _pipe = new Duration( right.seconds - left.seconds, right.nanoseconds - left.nanoseconds, ); return normalise(_pipe); } /** * Add two durations together. * * # Examples * * ```gleam * add(seconds(1), seconds(5)) * // -> seconds(6) * ``` */ export function add(left, right) { let _pipe = new Duration( left.seconds + right.seconds, left.nanoseconds + right.nanoseconds, ); return normalise(_pipe); } function nanosecond_digits(loop$n, loop$position, loop$acc) { while (true) { let n = loop$n; let position = loop$position; let acc = loop$acc; if (position === 9) { return acc; } else if ((acc === "") && ((remainderInt(n, 10)) === 0)) { loop$n = globalThis.Math.trunc(n / 10); loop$position = position + 1; loop$acc = acc; } else { let acc$1 = $int.to_string(n % 10) + acc; loop$n = globalThis.Math.trunc(n / 10); loop$position = position + 1; loop$acc = acc$1; } } } /** * Create a duration of a number of seconds. */ export function seconds(amount) { return new Duration(amount, 0); } /** * Create a duration of a number of minutes. */ export function minutes(amount) { return seconds(amount * 60); } /** * Create a duration of a number of hours. */ export function hours(amount) { return seconds(amount * 60 * 60); } /** * Create a duration of a number of milliseconds. */ export function milliseconds(amount) { let remainder = amount % 1000; let overflow = amount - remainder; let nanoseconds$1 = remainder * 1_000_000; let seconds$1 = globalThis.Math.trunc(overflow / 1000); let _pipe = new Duration(seconds$1, nanoseconds$1); return normalise(_pipe); } /** * Create a duration of a number of nanoseconds. * * # JavaScript int limitations * * Remember that JavaScript can only perfectly represent ints between positive * and negative 9,007,199,254,740,991! If you use a single call to this * function to create durations larger than that number of nanoseconds then * you will likely not get exactly the value you expect. Use `seconds` and * `milliseconds` as much as possible for large durations. */ export function nanoseconds(amount) { let _pipe = new Duration(0, amount); return normalise(_pipe); } /** * Convert the duration to a number of seconds. * * There may be some small loss of precision due to `Duration` being * nanosecond accurate and `Float` not being able to represent this. */ export function to_seconds(duration) { let seconds$1 = $int.to_float(duration.seconds); let nanoseconds$1 = $int.to_float(duration.nanoseconds); return seconds$1 + (nanoseconds$1 / 1_000_000_000.0); } /** * Convert the duration to a number of seconds and nanoseconds. There is no * loss of precision with this conversion on any target. */ export function to_seconds_and_nanoseconds(duration) { return [duration.seconds, duration.nanoseconds]; } export const empty = /* @__PURE__ */ new Duration(0, 0); /** * Convert the duration to an [ISO8601][1] formatted duration string. * * The ISO8601 duration format is ambiguous without context due to months and * years having different lengths, and because of leap seconds. This function * encodes the duration as days, hours, and seconds without any leap seconds. * Be sure to take this into account when using the duration strings. * * [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations */ export function to_iso8601_string(duration) { return $bool.guard( isEqual(duration, empty), "PT0S", () => { let split = (total, limit) => { let amount = remainderInt(total, limit); let remainder = divideInt((total - amount), limit); return [amount, remainder]; }; let $ = split(duration.seconds, 60); let seconds$1; let rest; seconds$1 = $[0]; rest = $[1]; let $1 = split(rest, 60); let minutes$1; let rest$1; minutes$1 = $1[0]; rest$1 = $1[1]; let $2 = split(rest$1, 24); let hours$1; let rest$2; hours$1 = $2[0]; rest$2 = $2[1]; let days = rest$2; let add$1 = (out, value, unit) => { if (value === 0) { return out; } else { return (out + $int.to_string(value)) + unit; } }; let _block; let _pipe = "P"; let _pipe$1 = add$1(_pipe, days, "D"); let _pipe$2 = $string.append(_pipe$1, "T"); let _pipe$3 = add$1(_pipe$2, hours$1, "H"); _block = add$1(_pipe$3, minutes$1, "M"); let output = _block; let $3 = duration.nanoseconds; if ($3 === 0) { if (seconds$1 === 0) { return output; } else { return (output + $int.to_string(seconds$1)) + "S"; } } else { let f = nanosecond_digits(duration.nanoseconds, 0, ""); return (((output + $int.to_string(seconds$1)) + ".") + f) + "S"; } }, ); }