UNPKG

daet

Version:

Minimal immutable date class that supports relative time, calendar time, and plus/minus of different units.

287 lines (286 loc) 11.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Week = exports.Day = exports.Hour = exports.Minute = exports.Second = exports.Millisecond = void 0; const memo_1 = __importDefault(require("@bevry/memo")); const start_of_week_1 = __importDefault(require("start-of-week")); const startOfWeek = (0, start_of_week_1.default)(); const intl = __importStar(require("./intl.js")); exports.Millisecond = 1; exports.Second = exports.Millisecond * 1000; exports.Minute = exports.Second * 60; exports.Hour = exports.Minute * 60; exports.Day = exports.Hour * 24; exports.Week = exports.Day * 7; /** A minimal immutable date class that supports relative time, calendar time, and plus/minus of different units. */ class Daet { /** The tiers used for human relative date display. */ static get tiers() { return [ { // right now limit: exports.Second, refresh: exports.Second, message: intl.rightNow, }, { // x seconds limit: exports.Minute, refresh: exports.Second, message: intl.relativeDelta, }, { // x minutes limit: exports.Hour, refresh: exports.Minute, message: intl.relativeDelta, }, { // x hours x minutes limit: 12 * exports.Hour, refresh: exports.Minute, message: intl.relativeDelta, }, { // later/earlier today when: ({ past }) => past ? new Daet().reset('hour').getTime() : new Daet().plus(1, 'day').reset('hour').getTime(), message: intl.earlierOrLaterToday, }, { // yesterday or tomorrow when: ({ past }) => past ? new Daet().minus(1, 'day').reset('hour').getTime() : new Daet().plus(2, 'day').reset('hour').getTime(), message: intl.yesterdayOrTommorow, }, { // this week when: ({ past }) => past ? new Daet().endOfLastWeek().getTime() : new Daet().startOfNextWeek().getTime(), message: intl.relativeThisWeek, }, { // next or last week when: ({ past }) => past ? new Daet().endOfLastWeek().endOfLastWeek().getTime() : new Daet().startOfNextWeek().startOfNextWeek().getTime(), message: intl.relativeSecondWeek, }, { // earlier or later limit: Infinity, message: intl.earlierOrLater, }, ]; } /** Create a new daet instance based on the input */ static create(input) { return new this(input); } /** Create a new daet instance based on the input */ constructor(input) { /** Get the epoch time of this daet instance. */ this.getTime = (0, memo_1.default)(() => this.raw.getTime()); /** Return a new daet instance that is the start of the week proceeding that of this daet instance. */ this.startOfNextWeek = (0, memo_1.default)(() => { // continue until we become the start of next week let latest = this.plus(1, 'day'); while (latest.raw.getDay() !== startOfWeek) { latest = latest.plus(1, 'day'); } // reset the time, so we become the start time of that week return latest.reset('hour'); }); /** Return a new daet instance that is the end of the week preceeding that of this daet instance. */ this.endOfLastWeek = (0, memo_1.default)(() => { // continue until we become the start of this week let latest = this; while (latest.raw.getDay() !== startOfWeek) { latest = latest.minus(1, 'day'); } // reset the time, then minus a second, so we become the end of the prior week return latest.reset('hour').minus(1, 'second'); }); /** Return the ISO string for this daet instance. */ this.toISOString = (0, memo_1.default)(() => this.raw.toISOString()); /** Return the JSON string for this daet instance. */ this.toJSON = (0, memo_1.default)(() => this.raw.toJSON()); this.raw = input instanceof Daet ? new Date(input.raw) : input ? new Date(input) : new Date(); this.raw.setMilliseconds(0); } /** Return a new daet instance that is in the past by the value amount. */ minus(value, unit) { return this.plus(value * -1, unit); } /** Return a new daet instance that is in the future by the value amount. */ plus(value, unit) { switch (unit) { case 'millisecond': { const next = new Date(this.raw.getTime() + value); return new Daet(next); } case 'second': { return this.plus(value * exports.Second, 'millisecond'); } case 'minute': { return this.plus(value * exports.Minute, 'millisecond'); } case 'hour': { return this.plus(value * exports.Hour, 'millisecond'); } case 'day': { return this.plus(value * exports.Day, 'millisecond'); } case 'week': { return this.plus(value * exports.Week, 'millisecond'); } default: // https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html const neverCheck = unit; throw new Error('unknown unit'); } } /** Clone this daet instance based on its raw value. */ rawClone() { return new Date(this.raw); } /** Clone this daet instance. */ clone() { return new Daet(this); } /** Return a new daet instance that has the unit set to the desired value. */ set(value, unit) { switch (unit) { case 'millisecond': { const next = this.rawClone().setMilliseconds(value); return new Daet(next); } case 'second': { const next = this.rawClone().setSeconds(value); return new Daet(next); } case 'minute': { const next = this.rawClone().setMinutes(value); return new Daet(next); } case 'hour': { const next = this.rawClone().setHours(value); return new Daet(next); } default: // https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html const neverCheck = unit; throw new Error('unknown unit'); } } /** Return a new daet instance that has the desired unit reset to 0. */ reset(unit) { switch (unit) { case 'millisecond': { const next = this.rawClone().setMilliseconds(0); return new Daet(next); } case 'second': { const next = this.rawClone().setSeconds(0, 0); return new Daet(next); } case 'minute': { const next = this.rawClone().setMinutes(0, 0, 0); return new Daet(next); } case 'hour': { const next = this.rawClone().setHours(0, 0, 0, 0); return new Daet(next); } default: // https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html const neverCheck = unit; throw new Error('unknown unit'); } } /** Get the milliseconds from the passed daet instance to this daet instance. */ getMillisecondsFrom(from) { const now = from.getTime(); const time = this.getTime(); return time - now; } /** Get the milliseconds from now to this daet instance */ getMillisecondsFromNow() { return this.getMillisecondsFrom(new Daet()); } /** Use https://devdocs.io/javascript/global_objects/datetimeformat to format our daet instance. */ format(locale, options) { return new Intl.DateTimeFormat(locale, options).format(this.raw); } /** Get the human absolute date display for this daet instance. */ calendar() { return this.format('en', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', }); } /** Return the human relative display from now, as well as the millisecond delta before a refresh is needed. */ fromNowDetails() { const now = new Daet(); const nowTime = now.getTime(); const eventTime = this.getTime(); const past = nowTime > eventTime; const delta = Math.abs(eventTime - nowTime); const tiers = Daet.tiers; let lastTierDelta = 0; for (const tier of tiers) { const limit = tier.limit; const when = tier.when ? tier.when({ past }) : 0; const tierDelta = limit || Math.abs(when - nowTime); if (delta < tierDelta) { const message = tier.message({ past, delta, when: this }); const refresh = tier.refresh || delta - lastTierDelta; return { message, refresh }; } lastTierDelta = tierDelta; } throw new Error('no tier matched the input delta'); } /** Return the human relative display from now. */ fromNow() { return this.fromNowDetails().message; } } exports.default = Daet;