import { Stereotype } from '../types' ; import { DatePattern , DateTimePattern , DateTimeNoTzPattern } from '../lib/util' ; const FyPattern = /^first-date-of-fy\(([0-9]+)\)$/ ; const FormulaPattern = /^([-+@])([0-9]+)(yr|mo|day|days|hr|min|sec|ms)$/ ; class UtcDate extends Date { public constructor ( ) ; public constructor ( str ) ; public constructor ( year , month , date ? , hours ? , minutes ? , seconds ? , ms ? ) ; public constructor ( year ? , month ? , date ? , hours ? , minutes ? , seconds ? , ms ? ) { super ( ) ; if ( year === void 0 ) { return ; } if ( typeof year === 'string' ) { if ( DateTimePattern . test ( year ) ) { this . setTime ( Date . parse ( year ) ) ; } else if ( DatePattern . test ( year ) ) { const d = new Date ( year ) ; this . setTime ( Date . UTC ( d . getUTCFullYear ( ) , d . getUTCMonth ( ) , d . getUTCDate ( ) ) ) ; } else if ( DateTimeNoTzPattern . test ( year ) ) { const d = new Date ( year ) ; this . setTime ( Date . UTC ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , d . getHours ( ) , d . getMinutes ( ) , d . getSeconds ( ) , d . getMilliseconds ( ) ) ) ; } else { this . setTime ( NaN ) ; } return ; } this . setUTCDate ( 1 ) ; this . setUTCFullYear ( year ) ; this . setUTCMonth ( typeof month === 'number' ? month : 0 ) ; this . setUTCDate ( typeof date === 'number' ? date : 1 ) ; this . setUTCHours ( typeof hours === 'number' ? hours : 0 ) ; this . setUTCMinutes ( typeof minutes === 'number' ? minutes : 0 ) ; this . setUTCSeconds ( typeof seconds === 'number' ? seconds : 0 ) ; this . setUTCMilliseconds ( typeof ms === 'number' ? ms : 0 ) ; } public getFullYear ( ) { return this . getUTCFullYear ( ) ; } public getMonth ( ) { return this . getUTCMonth ( ) ; } public getDate ( ) { return this . getUTCDate ( ) ; } public getHours ( ) { return this . getUTCHours ( ) ; } public getMinutes ( ) { return this . getUTCMinutes ( ) ; } public getSeconds ( ) { return this . getUTCSeconds ( ) ; } public getMilliseconds ( ) { return this . getUTCMilliseconds ( ) ; } } class LcDate extends Date { public constructor ( ) ; public constructor ( str ) ; public constructor ( year , month , date ? , hours ? , minutes ? , seconds ? , ms ? ) ; public constructor ( year ? , month ? , date ? , hours ? , minutes ? , seconds ? , ms ? ) { super ( ) ; if ( year === void 0 ) { return ; } if ( typeof year === 'string' ) { if ( DateTimePattern . test ( year ) ) { this . setTime ( Date . parse ( year ) ) ; } else if ( DatePattern . test ( year ) ) { const d = new Date ( year ) ; const l = new Date ( d . getUTCFullYear ( ) , d . getUTCMonth ( ) , d . getUTCDate ( ) ) ; this . setTime ( l . getTime ( ) ) ; } else if ( DateTimeNoTzPattern . test ( year ) ) { const d = new Date ( year ) ; this . setTime ( d . getTime ( ) ) ; } else { this . setTime ( NaN ) ; } return ; } this . setDate ( 1 ) ; this . setFullYear ( year ) ; this . setMonth ( typeof month === 'number' ? month : 0 ) ; this . setDate ( typeof date === 'number' ? date : 1 ) ; this . setHours ( typeof hours === 'number' ? hours : 0 ) ; this . setMinutes ( typeof minutes === 'number' ? minutes : 0 ) ; this . setSeconds ( typeof seconds === 'number' ? seconds : 0 ) ; this . setMilliseconds ( typeof ms === 'number' ? ms : 0 ) ; } } interface DateConstructor { new ( ) : Date ; new ( str ) : Date ; new ( year , month , date ? , hours ? , minutes ? , seconds ? , ms ? ) : Date ; } function evaluateFormulaBase ( dateCtor , valueOrFormula ) { const errMsg = `evaluateFormula: invalid parameter ${ valueOrFormula }` ; if ( typeof valueOrFormula !== 'string' ) { throw new Error ( errMsg ) ; } if ( valueOrFormula . startsWith ( '=' ) ) { const formula = valueOrFormula . slice ( 1 ) . split ( ' ' ) ; let d = new dateCtor ( ) ; const now = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , d . getHours ( ) , d . getMinutes ( ) ) ; const today = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) ) ; d = now ; for ( const f of formula ) { switch ( f ) { case 'current' : case 'now' : d = now ; break ; case 'today' : d = today ; break ; case 'first-date-of-yr' : case 'first-date-of-fy(1)' : d = new dateCtor ( d . getFullYear ( ) , 0 , 1 ) ; break ; case 'last-date-of-yr' : d = new dateCtor ( d . getFullYear ( ) , 11 , 31 ) ; break ; case 'first-date-of-mo' : d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , 1 ) ; break ; case 'last-date-of-mo' : d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) + 1 , 0 ) ; break ; default : if ( f . startsWith ( 'first-date-of-fy(' ) ) { const m = FyPattern . exec ( f ) ; if ( m ) { const n = Number . parseInt ( m [ 1 ] , 10 ) ; if ( 0 < n && n <= 12 ) { const mo = d . getMonth ( ) + 1 ; let yr = d . getFullYear ( ) ; if ( mo < n ) { yr -- ; } d = new dateCtor ( yr , n - 1 , 1 ) ; } else { throw new Error ( errMsg ) ; } } else { throw new Error ( errMsg ) ; } } else { const m = FormulaPattern . exec ( f ) ; if ( m ) { let n = Number . parseInt ( m [ 2 ] , 10 ) ; switch ( m [ 3 ] ) { case 'yr' : switch ( m [ 1 ] ) { case '@' : break ; case '+' : n = d . getFullYear ( ) + n ; break ; case '-' : n = d . getFullYear ( ) - n ; break ; } d = new dateCtor ( n , d . getMonth ( ) , d . getDate ( ) , d . getHours ( ) , d . getMinutes ( ) , d . getSeconds ( ) , d . getMilliseconds ( ) ) ; break ; case 'mo' : switch ( m [ 1 ] ) { case '@' : n -= 1 ; break ; case '+' : n = d . getMonth ( ) + n ; break ; case '-' : n = d . getMonth ( ) - n ; break ; } d = new dateCtor ( d . getFullYear ( ) , n , d . getDate ( ) , d . getHours ( ) , d . getMinutes ( ) , d . getSeconds ( ) , d . getMilliseconds ( ) ) ; break ; case 'day' : case 'days' : switch ( m [ 1 ] ) { case '@' : break ; case '+' : n = d . getDate ( ) + n ; break ; case '-' : n = d . getDate ( ) - n ; break ; } d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , n , d . getHours ( ) , d . getMinutes ( ) , d . getSeconds ( ) , d . getMilliseconds ( ) ) ; break ; case 'hr' : switch ( m [ 1 ] ) { case '@' : break ; case '+' : n = d . getHours ( ) + n ; break ; case '-' : n = d . getHours ( ) - n ; break ; } d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , n , d . getMinutes ( ) , d . getSeconds ( ) , d . getMilliseconds ( ) ) ; break ; case 'min' : switch ( m [ 1 ] ) { case '@' : break ; case '+' : n = d . getMinutes ( ) + n ; break ; case '-' : n = d . getMinutes ( ) - n ; break ; } d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , d . getHours ( ) , n , d . getSeconds ( ) , d . getMilliseconds ( ) ) ; break ; case 'sec' : switch ( m [ 1 ] ) { case '@' : break ; case '+' : n = d . getSeconds ( ) + n ; break ; case '-' : n = d . getSeconds ( ) - n ; break ; } d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , d . getHours ( ) , d . getMinutes ( ) , n , d . getMilliseconds ( ) ) ; break ; case 'ms' : switch ( m [ 1 ] ) { case '@' : break ; case '+' : n = d . getMilliseconds ( ) + n ; break ; case '-' : n = d . getMilliseconds ( ) - n ; break ; } d = new dateCtor ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) , d . getHours ( ) , d . getMinutes ( ) , d . getSeconds ( ) , n ) ; break ; default : throw new Error ( errMsg ) ; } } else { if ( ! ( DatePattern . test ( f ) || DateTimePattern . test ( f ) || DateTimeNoTzPattern . test ( f ) ) ) { throw new Error ( errMsg ) ; } d = new dateCtor ( f ) ; } } } } return d ; } else { if ( ! DatePattern . test ( valueOrFormula ) ) { throw new Error ( errMsg ) ; } return new dateCtor ( valueOrFormula ) ; } } export const dateStereotype = { tryParse : ( value ) => { return ( typeof value === 'string' && DatePattern . test ( value ) ? { value : ( new UtcDate ( value ) ) . getTime ( ) } : null ) ; } , evaluateFormula : valueOrFormula => { const d = evaluateFormulaBase ( UtcDate , valueOrFormula ) ; return ( new UtcDate ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) ) ) . getTime ( ) ; } , compare : ( a , b ) => a - b , doCast : false , } ; export const lcDateStereotype = { ... dateStereotype , tryParse : ( value ) => { if ( typeof value === 'string' && DatePattern . test ( value ) ) { return ( { value : ( new LcDate ( value ) ) . getTime ( ) } ) ; } else { return null ; } } , evaluateFormula : valueOrFormula => { const d = evaluateFormulaBase ( LcDate , valueOrFormula ) ; return ( new LcDate ( d . getFullYear ( ) , d . getMonth ( ) , d . getDate ( ) ) ) . getTime ( ) ; } , } ; export const datetimeStereotype = { tryParse : ( value ) => { return ( typeof value === 'string' && ( DateTimePattern . test ( value ) || DateTimeNoTzPattern . test ( value ) ) ? { value : ( new UtcDate ( value ) ) . getTime ( ) } : null ) ; } , evaluateFormula : valueOrFormula => evaluateFormulaBase ( UtcDate , valueOrFormula ) . getTime ( ) , compare : ( a , b ) => a - b , doCast : false , } ; export const lcDatetimeStereotype = { ... datetimeStereotype , tryParse : ( value ) => { return ( typeof value === 'string' && ( DateTimePattern . test ( value ) || DateTimeNoTzPattern . test ( value ) ) ? { value : ( new LcDate ( value ) ) . getTime ( ) } : null ) ; } , evaluateFormula : valueOrFormula => evaluateFormulaBase ( LcDate , valueOrFormula ) . getTime ( ) , } ; export const stereotypes = [ [ 'date' , dateStereotype ] , [ 'lcdate' , lcDateStereotype ] , [ 'datetime' , datetimeStereotype ] , [ 'lcdatetime' , lcDatetimeStereotype ] , ] ;