import { FSharpRef } from "./Types.ts"; import { hours, minutes, seconds, milliseconds } from "./TimeSpan.ts"; import { Exception, DateTimeKind, IDateTime, padWithZeros } from "./Util.ts"; const millisecondsPerDay = 86400000; export function create(h: number = 0, m: number = 0, s: number = 0, ms: number = 0) { if (h < 0 || m < 0 || s < 0 || ms < 0) throw new Exception("The parameters describe an unrepresentable TimeOnly."); return h * 3600000 + m * 60_000 + s * 1000 + ms; } export function fromTicks(ticks: number | bigint) { return Number(BigInt(ticks) / 10000n); } export function fromTimeSpan(timeSpan: number) { if (timeSpan < 0 || timeSpan >= millisecondsPerDay) throw new Exception("The TimeSpan describes an unrepresentable TimeOnly."); return timeSpan; } export function fromDateTime(d: IDateTime) { return d.kind === DateTimeKind.Utc ? create(d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds()) : create(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()); } export function maxValue() { // This is "23:59:59.999" return millisecondsPerDay - 1; } export function add(t: number, ts: number, wrappedDays?: FSharpRef) { if (wrappedDays === undefined) { const t2 = (t + ts) % millisecondsPerDay; return t2 < 0 ? millisecondsPerDay + t2 : t2; } wrappedDays.contents = ts / millisecondsPerDay; let newMs = t + ts % millisecondsPerDay; if (newMs < 0) { wrappedDays.contents--; newMs += millisecondsPerDay; } else { if (newMs >= millisecondsPerDay) { wrappedDays.contents++; newMs -= millisecondsPerDay; } } return newMs; } export function addHours(t: number, h: number) { return add(t, h * 3600000); } export function addMinutes(t: number, m: number) { return add(t, m * 60_000); } export function isBetween(t: number, start: number, end: number) { return start <= end ? (start <= t && end > t) : (start <= t || end > t); } export function toString(t: number, format = "t", _provider?: any) { if (["r", "R", "o", "O", "t", "T"].indexOf(format) === -1) { throw new Exception("Custom formats are not supported"); } const base = `${padWithZeros(hours(t), 2)}:${padWithZeros(minutes(t), 2)}` if (format === "t") return base; const s = padWithZeros(seconds(t), 2); // We're limited to millisecond precision, so the last 4 digits will always be 0 return `${base}${format === "o" || format === "O" ? `:${s}.${padWithZeros(milliseconds(t), 3)}0000` : `:${s}`}`; } export function parse(str: string) { // Allowed format types: // hh:mm // hh:mm:ss // hh:mm:ss.fffffff const r = /^\s*([0-1]?\d|2[0-3])\s*:\s*([0-5]?\d)(\s*:\s*([0-5]?\d)(\.(\d+))?)?\s*$/.exec(str); if (r != null && r[1] != null && r[2] != null) { let ms = 0; let s = 0; const h = +r[1]; const m = +r[2]; if (r[4] != null) { s = +r[4]; } if (r[6] != null) { // Depending on the number of decimals passed, we need to adapt the numbers switch (r[6].length) { case 1: ms = +r[6] * 100; break; case 2: ms = +r[6] * 10; break; case 3: ms = +r[6]; break; case 4: ms = +r[6] / 10; break; case 5: ms = +r[6] / 100; break; case 6: ms = +r[6] / 1000; break; default: ms = +r[6].substring(0, 7) / 10000; break; } } return create(h, m, s, Math.trunc(ms)); } throw new Exception(`String '${str}' was not recognized as a valid TimeOnly.`); } export function tryParse(v: string, defValue: FSharpRef): boolean { try { defValue.contents = parse(v); return true; } catch { return false; } } export function op_Subtraction(left: number, right: number) { return add(left, -right); }