import { isString , memoize } from 'lodash' ; export enum IntervalCadence { Minute = 'm' , Second = 's' , Hour = 'h' , Day = 'd' } export type Interval = string ; export function isInterval ( interval ) { const numericAsStr = interval . slice ( 0 , - 1 ) ; const numeric = parseInt ( numericAsStr , 10 ) ; const cadence = interval . slice ( - 1 ) ; return ! ( ! isCadence ( cadence ) || isNaN ( numeric ) || numeric <= 0 || ! isNumeric ( numericAsStr ) ) ; } const VALID_CADENCE = new Set ( Object . values ( IntervalCadence ) ) ; const CADENCE_IN_MS = { [ IntervalCadence . Second ] : 1000 , [ IntervalCadence . Minute ] : 60 * 1000 , [ IntervalCadence . Hour ] : 60 * 60 * 1000 , [ IntervalCadence . Day ] : 24 * 60 * 60 * 1000 , } ; function isCadence ( cadence ) { return VALID_CADENCE . has ( cadence as IntervalCadence ) ; } export function asInterval ( ms ) { const secondsRemainder = ms % 1000 ; const minutesRemainder = ms % 60000 ; return secondsRemainder ? `${ ms }ms` : minutesRemainder ? `${ ms / 1000 }s` : `${ ms / 60000 }m` ; } param { Interval } interval export function intervalFromNow ( interval ? ) { if ( interval === undefined ) { return ; } return secondsFromNow ( parseIntervalAsSecond ( interval ) ) ; } param { Date } date param { Interval } interval export function intervalFromDate ( date , interval ? ) { if ( interval === undefined ) { return ; } return secondsFromDate ( date , parseIntervalAsSecond ( interval ) ) ; } export function maxIntervalFromDate ( date , ... intervals ) { const maxSeconds = Math . max ( ... intervals . filter ( isString ) . map ( ( interval ) => parseIntervalAsSecond ( interval as Interval ) ) ) ; if ( ! isNaN ( maxSeconds ) ) { return secondsFromDate ( date , maxSeconds ) ; } } param secs export function secondsFromNow ( secs ) { return secondsFromDate ( new Date ( ) , secs ) ; } param date param secs export function secondsFromDate ( date , secs ) { const result = new Date ( date . valueOf ( ) ) ; result . setSeconds ( result . getSeconds ( ) + secs ) ; return result ; } param { Interval } interval returns { number } export const parseIntervalAsSecond = memoize ( ( interval ) : number => { return Math . round ( parseIntervalAsMillisecond ( interval ) / 1000 ) ; } ) ; export const parseIntervalAsMillisecond = memoize ( ( interval ) : number => { const numericAsStr = interval . slice ( 0 , - 1 ) ; const numeric = parseInt ( numericAsStr , 10 ) ; const cadence = interval . slice ( - 1 ) ; if ( ! isCadence ( cadence ) || isNaN ( numeric ) || numeric <= 0 || ! isNumeric ( numericAsStr ) ) { throw new Error ( `Invalid interval "${ interval }". Intervals must be of the form {number}m. Example: 5m.` ) ; } return numeric * CADENCE_IN_MS [ cadence ] ; } ) ; const isNumeric = ( numAsStr ) => /^\d+$/ . test ( numAsStr ) ;