import { Day } from './Day' ; import { DaySpan } from './DaySpan' ; import { Functions as fn } from './Functions' ; import { Locales } from './Locale' ; /**
 * The type for identifiers. Most of the time an identifier can be stored as a
 * number because the 4 digit year is first. However when the year is below
 * 1000 a string will be used with zero padding. Storing identifiers as numbers
 * enable very quick comparisons and using strings or numbers allows the
 * identifier to be used as a key to a map.
 */ export type IdentifierInput = number | string ; /**
 * The possible properties which can be pulled from an identifier.
 */ export interface IdentifierObject { /**
     * The year pulled from an identifier (0-9999).
     */ year ? : number ; /**
     * The quarter of the year pulled from an identifier (1-4)
     */ quarter ? : number ; /**
     * The month of the year pulled from an identifier (0-11)
     */ month ? : number ; /**
     * The week of the year pulled from an identifier (1-52)
     */ week ? : number ; /**
     * The day of the month pulled from an identifier (1-31)
     */ day ? : number ; /**
     * The hour of the day pulled from an identifier (0-23)
     */ hour ? : number ; /**
     * The minute of the hour pulled from an identifier (0-59)
     */ minute ? : number ; } /**
 * A class for detecting, parsing, and building identifiers to and from days.
 *
 * An identifier is a simple value which represents a span of time. It may
 * represent an entire year, a quarter (3 months) of a year, a week of a year,
 * a month in a year, a specific day of a month of a year, or a specific hour,
 * minute, day, and month of a year.
 *
 * For example:
 * - `2018`: The year 2018
 * - `201801`: January 2018
 * - `2014023`: The 23rd week of 2014
 * - `20170311`: March 11th, 2017
 * - `201406151651`: June 15th 2016 at 4:51 pm
 * - `'0525'`: Year 525 of the first age, Elrond and Elros are born
 */ export abstract class Identifier { param id returns public is ( id ) { return ( id + '' ) . length === this . getLength ( ) ; } param day returns abstract get ( day ) ; param id returns abstract object ( id ) ; param id returns abstract start ( id ) ; param id param endInclusive returns abstract span ( id , endInclusive ) ; param day param id returns abstract matches ( day , id ) ; param id param short returns abstract describe ( id , short ) ; /**
     * The scales for all the different values stored in an identifier.
     */ protected abstract getScales ( ) ; /**
     * The length of the identifier of this type in digits.
     */ protected abstract getLength ( ) ; param values returns protected compute ( ... values ) { const scales = this . getScales ( ) ; let total = 0 ; for ( let i = 0 ; i < values . length ; i ++ ) { total += values [ i ] * scales [ i ] ; } return this . is ( total ) ? total : fn . padNumber ( total , this . getLength ( ) ) ; } param id returns protected decompute ( id ) { const scales = this . getScales ( ) ; let total = fn . isNumber ( id ) ? id : parseInt ( id ) ; const values = [ ] ; for ( let i = 0 ; i < scales . length - 1 ; i ++ ) { const curr = scales [ i + 0 ] ; const next = scales [ i + 1 ] ; const mod = next / curr ; const value = total % mod ; values . push ( value ) ; total = Math . floor ( total / mod ) ; } values . push ( total ) ; return values ; } /**
     * The identifier type for an hour of time on a specific day.
     */ public static Time : Identifier = null ; /**
     * The identifier type for a specific day.
     */ public static Day : Identifier = null ; /**
     * The identifier type for a specific week of a year.
     */ public static Week : Identifier = null ; /**
     * The identifier type for a specific month of a year.
     */ public static Month : Identifier = null ; /**
     * The identifier type for a specific quarter of a year.
     */ public static Quarter : Identifier = null ; /**
     * The identifier type for a specific year.
     */ public static Year : Identifier = null ; param id returns public static find ( id ) { if ( this . Time . is ( id ) ) return this . Time ; if ( this . Day . is ( id ) ) return this . Day ; if ( this . Week . is ( id ) ) return this . Week ; if ( this . Month . is ( id ) ) return this . Month ; if ( this . Year . is ( id ) ) return this . Year ; return null ; } param outer param inner returns public static contains ( outer , inner ) { const outerString = outer + '' ; return ( inner + '' ) . substring ( 0 , outerString . length ) === outerString ; } } class IdentifierTime extends Identifier { private static SCALES : number [ ] = [ 1 , 100 , 10000 , 1000000 , 100000000 ] ; private static LENGTH : number = 12 ; protected getScales ( ) { return IdentifierTime . SCALES ; } protected getLength ( ) { return IdentifierTime . LENGTH ; } public get ( day ) { return this . compute ( day . minute , day . hour , day . dayOfMonth , day . month + 1 , day . year ) ; } public object ( id ) { const values = this . decompute ( id ) ; return { minute : values [ 0 ] , hour : values [ 1 ] , day : values [ 2 ] , month : values [ 3 ] - 1 , year : values [ 4 ] } ; } public start ( id ) { const obj = this . object ( id ) ; const start = Day . build ( obj . year , obj . month , obj . day , obj . hour , obj . minute ) ; return start ; } public span ( id , endInclusive = false ) { const start = this . start ( id ) ; const end = start . endOf ( 'hour' , endInclusive ) ; return new DaySpan ( start , end ) ; } public describe ( id , short = false ) { const start = this . start ( id ) ; const format = Locales . current . identifierTime ( short ) ; return start . format ( format ) ; } public matches ( day , id ) { return day . timeIdentifier === id ; } } class IdentifierDay extends Identifier { private static SCALES : number [ ] = [ 1 , 100 , 10000 ] ; private static LENGTH : number = 8 ; protected getScales ( ) { return IdentifierDay . SCALES ; } protected getLength ( ) { return IdentifierDay . LENGTH ; } public get ( day ) { return this . compute ( day . dayOfMonth , day . month + 1 , day . year ) ; } public object ( id ) { const values = this . decompute ( id ) ; return { day : values [ 0 ] , month : values [ 1 ] - 1 , year : values [ 2 ] } ; } public start ( id ) { const obj = this . object ( id ) ; const start = Day . build ( obj . year , obj . month , obj . day ) ; return start ; } public span ( id , endInclusive = false ) { const start = this . start ( id ) ; const end = start . endOf ( 'day' , endInclusive ) ; return new DaySpan ( start , end ) ; } public describe ( id , short = false ) { const start = this . start ( id ) ; const format = Locales . current . identifierDay ( short ) ; return start . format ( format ) ; } public matches ( day , id ) { return day . dayIdentifier === id ; } } class IdentifierWeek extends Identifier { private static SCALES : number [ ] = [ 1 , 1000 ] ; private static LENGTH : number = 7 ; protected getScales ( ) { return IdentifierWeek . SCALES ; } protected getLength ( ) { return IdentifierWeek . LENGTH ; } public get ( day ) { return this . compute ( day . weekOfYear , day . year ) ; } public object ( id ) { const values = this . decompute ( id ) ; return { week : values [ 0 ] , year : values [ 1 ] } ; } public start ( id ) { const obj = this . object ( id ) ; const start = Day . build ( obj . year , 0 ) . withWeekOfYear ( obj . week ) ; return start ; } public span ( id , endInclusive = false ) { const start = this . start ( id ) ; const end = start . endOf ( 'week' , endInclusive ) ; return new DaySpan ( start , end ) ; } public describe ( id , short = false ) { const start = this . start ( id ) ; const format = Locales . current . identifierWeek ( short ) ; return start . format ( format ) ; } public matches ( day , id ) { return day . weekIdentifier === id ; } } class IdentifierMonth extends Identifier { private static SCALES : number [ ] = [ 1 , 100 ] ; private static LENGTH : number = 6 ; protected getScales ( ) { return IdentifierMonth . SCALES ; } protected getLength ( ) { return IdentifierMonth . LENGTH ; } public get ( day ) { return this . compute ( day . month + 1 , day . year ) ; } public object ( id ) { const values = this . decompute ( id ) ; return { month : values [ 0 ] - 1 , year : values [ 1 ] } ; } public start ( id ) { const obj = this . object ( id ) ; const start = Day . build ( obj . year , obj . month ) ; return start ; } public span ( id , endInclusive = false ) { const start = this . start ( id ) ; const end = start . endOf ( 'month' , endInclusive ) ; return new DaySpan ( start , end ) ; } public describe ( id , short = false ) { const start = this . start ( id ) ; const format = Locales . current . identifierMonth ( short ) ; return start . format ( format ) ; } public matches ( day , id ) { return day . monthIdentifier === id ; } } class IdentifierQuarter extends Identifier { private static SCALES : number [ ] = [ 1 , 10 ] ; private static LENGTH : number = 5 ; protected getScales ( ) { return IdentifierQuarter . SCALES ; } protected getLength ( ) { return IdentifierQuarter . LENGTH ; } public get ( day ) { return this . compute ( day . quarter , day . year ) ; } public object ( id ) { const values = this . decompute ( id ) ; return { quarter : values [ 0 ] , year : values [ 1 ] } ; } public start ( id ) { const obj = this . object ( id ) ; const start = Day . build ( obj . year , ( obj . quarter - 1 ) * 3 ) ; return start ; } public span ( id , endInclusive = false ) { const start = this . start ( id ) ; const end = start . add ( 'month' , 3 ) . endOf ( 'month' , endInclusive ) ; return new DaySpan ( start , end ) ; } public describe ( id , short = false ) { const start = this . start ( id ) ; const format = Locales . current . identifierQuarter ( short ) ; return start . format ( format ) ; } public matches ( day , id ) { return day . quarterIdentifier === id ; } } class IdentifierYear extends Identifier { private static SCALES : number [ ] = [ 1 ] ; private static LENGTH : number = 4 ; protected getScales ( ) { return IdentifierYear . SCALES ; } protected getLength ( ) { return IdentifierYear . LENGTH ; } public get ( day ) { return this . compute ( day . year ) ; } public object ( id ) { const values = this . decompute ( id ) ; return { year : values [ 0 ] } ; } public start ( id ) { const obj = this . object ( id ) ; const start = Day . build ( obj . year , 0 ) ; return start ; } public span ( id , endInclusive = false ) { const start = this . start ( id ) ; const end = start . endOf ( 'year' , endInclusive ) ; return new DaySpan ( start , end ) ; } public describe ( id , short = false ) { const start = this . start ( id ) ; const format = Locales . current . identifierYear ( short ) ; return start . format ( format ) ; } public matches ( day , id ) { return day . year === id ; } } Identifier . Time = new IdentifierTime ( ) ; Identifier . Day = new IdentifierDay ( ) ; Identifier . Week = new IdentifierWeek ( ) ; Identifier . Month = new IdentifierMonth ( ) ; Identifier . Quarter = new IdentifierQuarter ( ) ; Identifier . Year = new IdentifierYear ( ) ;