UNPKG

@fable-org/fable-library-js

Version:

Core library used by F# projects compiled with fable.io

262 lines (261 loc) 8.78 kB
/** * DateTimeOffset functions. * * Note: DateOffset instances are always DateObjects in local * timezone (because JS dates are all kinds of messed up). * A local date returns UTC epoch when `.getTime()` is called. * * However, this means that in order to construct an UTC date * from a DateOffset with offset of +5 hours, you first need * to subtract those 5 hours, than add the "local" offset. * As said, all kinds of messed up. * * Basically; invariant: date.getTime() always return UTC time. */ import { fromFloat64, toFloat64 } from "./BigInt.js"; import DateTime, { create as createDate, daysInMonth, parseRaw, ticksToUnixEpochMilliseconds, unixEpochMillisecondsToTicks } from "./Date.js"; import { Exception, compareDates, DateTimeKind } from "./Util.js"; export default function DateTimeOffset(value, offset) { checkOffsetInRange(offset); const d = new Date(value); d.offset = offset != null ? offset : new Date().getTimezoneOffset() * -60000; return d; } export function offset(value) { return value.offset || 0; } function checkOffsetInRange(offset) { if (offset != null && offset !== 0) { if (offset % 60000 !== 0) { throw new Exception("Offset must be specified in whole minutes."); } if (Math.abs(offset / 3600000) > 14) { throw new Exception("Offset must be within plus or minus 14 hours."); } } } export function fromDate(date, offset) { let offset2 = 0; switch (date.kind) { case DateTimeKind.Utc: if (offset != null && offset !== 0) { throw new Exception("The UTC Offset for Utc DateTime instances must be 0."); } offset2 = 0; break; case DateTimeKind.Local: offset2 = date.getTimezoneOffset() * -60000; if (offset != null && offset !== offset2) { throw new Exception("The UTC Offset of the local dateTime parameter does not match the offset argument."); } break; case DateTimeKind.Unspecified: default: if (offset == null) { offset2 = date.getTimezoneOffset() * -60000; } else { offset2 = offset; } break; } return DateTimeOffset(date.getTime(), offset2); } export function fromDateTime(dateOnly, timeOnly, offset) { return DateTimeOffset(dateOnly.getTime() - offset + timeOnly, offset); } export function fromTicks(ticks, offset) { const ms = ticksToUnixEpochMilliseconds(ticks) - offset; return DateTimeOffset(ms, offset); } export function fromUnixTimeMilliseconds(ms) { return DateTimeOffset(toFloat64(ms), 0); } export function fromUnixTimeSeconds(seconds) { return DateTimeOffset(toFloat64(seconds * 1000n), 0); } export function getUtcTicks(date) { return unixEpochMillisecondsToTicks(date.getTime(), 0); } export function minValue() { // This is "0001-01-01T00:00:00.000Z", actual JS min value is -8640000000000000 return DateTimeOffset(-62135596800000, 0); } export function maxValue() { // This is "9999-12-31T23:59:59.999Z", actual JS max value is 8640000000000000 return DateTimeOffset(253402300799999, 0); } export function parse(str) { const [date, offsetMatch] = parseRaw(str); const offset = offsetMatch == null ? date.getTimezoneOffset() * -60000 : (offsetMatch === "Z" ? 0 : offsetMatch * 60000); return DateTimeOffset(date.getTime(), offset); } export function tryParse(v, defValue) { try { defValue.contents = parse(v); return true; } catch (_err) { return false; } } export function create(year, month, day, h, m, s, ms, offset) { if (offset == null) { offset = ms; ms = 0; } checkOffsetInRange(offset); let date = new Date(Date.UTC(year, month - 1, day, h, m, s, ms) - offset); if (year <= 99) { date.setUTCFullYear(year, month - 1, day); } const dateValue = date.getTime(); if (isNaN(dateValue)) { throw new Exception("The parameters describe an unrepresentable Date"); } return DateTimeOffset(dateValue, offset); } export function now() { const date = new Date(); const offset = date.getTimezoneOffset() * -60000; return DateTimeOffset(date.getTime(), offset); } export function utcNow() { const date = new Date(); // const offset = date.getTimezoneOffset() * -60_000; return DateTimeOffset(date.getTime(), 0); } export function toUniversalTime(date) { return DateTime(date.getTime(), DateTimeKind.Utc); } export function toLocalTime(date) { return DateTime(date.getTime(), DateTimeKind.Local); } export function localDateTime(date) { return DateTime(date.getTime(), DateTimeKind.Local); } export function timeOfDay(d) { const d2 = new Date(d.getTime() + offset(d)); return d2.getUTCHours() * 3600000 + d2.getUTCMinutes() * 60000 + d2.getUTCSeconds() * 1000 + d2.getUTCMilliseconds(); } export function date(d) { const d2 = new Date(d.getTime() + offset(d)); return createDate(d2.getUTCFullYear(), d2.getUTCMonth() + 1, d2.getUTCDate(), 0, 0, 0, 0); } export function day(d) { return new Date(d.getTime() + offset(d)).getUTCDate(); } export function hour(d) { return new Date(d.getTime() + offset(d)).getUTCHours(); } export function millisecond(d) { return new Date(d.getTime() + offset(d)).getUTCMilliseconds(); } export function minute(d) { return new Date(d.getTime() + offset(d)).getUTCMinutes(); } export function month(d) { return new Date(d.getTime() + offset(d)).getUTCMonth() + 1; } export function second(d) { return new Date(d.getTime() + offset(d)).getUTCSeconds(); } export function year(d) { return new Date(d.getTime() + offset(d)).getUTCFullYear(); } export function dayOfWeek(d) { return new Date(d.getTime() + offset(d)).getUTCDay(); } export function dayOfYear(d) { const d2 = new Date(d.getTime() + offset(d)); const _year = d2.getUTCFullYear(); const _month = d2.getUTCMonth() + 1; let _day = d2.getUTCDate(); for (let i = 1; i < _month; i++) { _day += daysInMonth(_year, i); } return _day; } export function add(d, ts) { return DateTimeOffset(d.getTime() + ts, offset(d)); } export function addDays(d, v) { return add(d, v * 86400000); } export function addHours(d, v) { return add(d, v * 3600000); } export function addMinutes(d, v) { return add(d, v * 60000); } export function addSeconds(d, v) { return add(d, v * 1000); } export function addMilliseconds(d, v) { return add(d, v); } export function addTicks(d, v) { return add(d, toFloat64(v / 10000n)); } export function addYears(d, v) { const newMonth = d.getUTCMonth() + 1; const newYear = d.getUTCFullYear() + v; const _daysInMonth = daysInMonth(newYear, newMonth); const newDay = Math.min(_daysInMonth, d.getUTCDate()); return create(newYear, newMonth, newDay, d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds(), offset(d)); } export function addMonths(d, v) { const d2 = new Date(d.getTime() + offset(d)); let newMonth = d2.getUTCMonth() + 1 + v; let newMonth_ = 0; let yearOffset = 0; if (newMonth > 12) { newMonth_ = newMonth % 12; yearOffset = Math.floor(newMonth / 12); newMonth = newMonth_; } else if (newMonth < 1) { newMonth_ = 12 + newMonth % 12; yearOffset = Math.floor(newMonth / 12) + (newMonth_ === 12 ? -1 : 0); newMonth = newMonth_; } const newYear = d2.getUTCFullYear() + yearOffset; const _daysInMonth = daysInMonth(newYear, newMonth); const newDay = Math.min(_daysInMonth, d2.getUTCDate()); return create(newYear, newMonth, newDay, d2.getUTCHours(), d2.getUTCMinutes(), d2.getUTCSeconds(), d2.getUTCMilliseconds(), offset(d)); } export function subtract(d, that) { return typeof that === "number" ? DateTimeOffset(d.getTime() - that, offset(d)) : d.getTime() - that.getTime(); } export function equals(d1, d2) { return d1.getTime() === d2.getTime(); } export function equalsExact(d1, d2) { return d1.getTime() === d2.getTime() && d1.offset === d2.offset; } export function compare(d1, d2) { return compareDates(d1, d2); } export const compareTo = compare; export function op_Addition(x, y) { return add(x, y); } export function op_Subtraction(x, y) { return subtract(x, y); } export function toOffset(d, offset) { return DateTimeOffset(d.getTime(), offset); } export function toUnixTimeMilliseconds(d) { return fromFloat64(d.getTime()); } export function toUnixTimeSeconds(d) { return fromFloat64(d.getTime() / 1000.0); }