import moment from 'moment' ; export type Unit = 'ms' | 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y' ; export type UnitsMap = { [ k in Unit ] : { weight : number ; type : 'calendar' | 'fixed' | 'mixed' ; base : number ; } ; } ; export const unitsMap = { ms : { weight : 1 , type : 'fixed' , base : 1 } , s : { weight : 2 , type : 'fixed' , base : 1000 } , m : { weight : 3 , type : 'mixed' , base : 1000 * 60 } , h : { weight : 4 , type : 'mixed' , base : 1000 * 60 * 60 } , d : { weight : 5 , type : 'mixed' , base : 1000 * 60 * 60 * 24 } , w : { weight : 6 , type : 'calendar' , base : NaN } , M : { weight : 7 , type : 'calendar' , base : NaN } , y : { weight : 9 , type : 'calendar' , base : NaN } , } ; export const units = Object . keys ( unitsMap ) . sort ( ( a , b ) => unitsMap [ b as Unit ] . weight - unitsMap [ a as Unit ] . weight ) as Unit [ ] ; export const unitsDesc = [ ... units ] as Unit [ ] ; export const unitsAsc = [ ... units ] . reverse ( ) as Unit [ ] ; const isDate = ( d ) => Object . prototype . toString . call ( d ) === '[object Date]' ; const isValidDate = ( d ) => isDate ( d ) && ! isNaN ( d . valueOf ( ) as any ) ; export function parse ( input , options = { } ) { const text = input ; const { roundUp = false , momentInstance = moment , forceNow } = options ; if ( ! text ) return undefined ; if ( momentInstance . isMoment ( text ) ) return text ; if ( isDate ( text ) ) return momentInstance ( text ) ; if ( forceNow !== undefined && ! isValidDate ( forceNow as any ) ) { throw new Error ( 'forceNow must be a valid Date' ) ; } let time ; let mathString = '' ; let index ; let parseString ; if ( text . substring ( 0 , 3 ) === 'now' ) { time = momentInstance ( forceNow ) ; mathString = text . substring ( 'now' . length ) ; } else { index = text . indexOf ( '||' ) ; if ( index === - 1 ) { parseString = text ; mathString = '' ; } else { parseString = text . substring ( 0 , index ) ; mathString = text . substring ( index + 2 ) ; } time = momentInstance ( parseString ) ; } if ( ! mathString . length ) { return time ; } return parseDateMath ( mathString , time , roundUp ) ; } function parseDateMath ( mathString , time , roundUp ) { const dateTime = time ; const len = mathString . length ; let i = 0 ; while ( i < len ) { const c = mathString . charAt ( i ++ ) ; let type ; let num ; let unit ; if ( c === '/' ) { type = 0 ; } else if ( c === '+' ) { type = 1 ; } else if ( c === '-' ) { type = 2 ; } else { return ; } if ( isNaN ( mathString . charAt ( i ) as any ) ) { num = 1 ; } else if ( mathString . length === 2 ) { num = mathString . charAt ( i ) ; } else { const numFrom = i ; while ( ! isNaN ( mathString . charAt ( i ) as any ) ) { i ++ ; if ( i >= len ) return ; } num = parseInt ( mathString . substring ( numFrom , i ) , 10 ) ; } if ( type === 0 ) { if ( num !== 1 ) { return ; } } unit = mathString . charAt ( i ++ ) as Unit ; for ( let j = i ; j < len ; j ++ ) { const unitChar = mathString . charAt ( i ) ; if ( /[a-z]/i . test ( unitChar ) ) { unit += unitChar ; i ++ ; } else { break ; } } if ( units . indexOf ( unit ) === - 1 ) { return ; } else { if ( type === 0 ) { if ( roundUp ) dateTime . endOf ( unit as any ) ; else dateTime . startOf ( unit as any ) ; } else if ( type === 1 ) { dateTime . add ( num as any , unit ) ; } else if ( type === 2 ) { dateTime . subtract ( num as any , unit ) ; } } } return dateTime ; } export default { parse , unitsMap : Object . freeze ( unitsMap ) , units : Object . freeze ( units ) , unitsAsc : Object . freeze ( unitsAsc ) , unitsDesc : Object . freeze ( unitsDesc ) , } ;