1+ import Utils from "../shared/utils" ;
2+
3+ const utils = new Utils ( ) ;
4+
5+ interface Params {
6+ start ?: string ;
7+ offset ?: number ;
8+ end ?: string ;
9+ freq ?: string ;
10+ period ?: number
11+ }
12+ class DateRange {
13+ private offset ?: number
14+ private start ?: string
15+ private end ?: string
16+ private freq : string
17+ private period ?: number
18+ private freqList : string [ ]
19+
20+ constructor ( { start, end, offset, freq, period} : Params ) {
21+ this . start = start
22+ this . end = end
23+ this . offset = offset
24+ this . freq = freq ? freq : "D"
25+ this . period = period
26+ this . freqList = [ "M" , "D" , "s" , "H" , "m" , "Y" ]
27+
28+ if ( this . freq . length == 1 ) {
29+ if ( ! this . freqList . includes ( this . freq ) ) {
30+ throw new Error ( `invalid freq ${ this . freq } ` ) ;
31+ }
32+ } else {
33+ this . offset = parseInt ( this . freq . slice ( 0 , - 1 ) ) ;
34+ if ( ! Number . isFinite ( this . offset ) ) {
35+ throw new Error ( `invalid freq offset ${ this . freq . slice ( 0 , - 1 ) } ` ) ;
36+ }
37+ this . freq = this . freq . slice ( - 1 ) ;
38+ if ( ! this . freqList . includes ( this . freq ) ) {
39+ throw new Error ( `invalid freq ${ this . freq } ` ) ;
40+ }
41+ }
42+
43+ }
44+
45+ range ( ) : string [ ] {
46+ let start = this . start
47+ let period = this . period
48+ let end = this . end
49+ let offset = this . offset
50+ let startDate : Date
51+ let endDate : Date
52+ let startRange : number
53+ let endRange : number
54+ if ( start && end ) {
55+ startDate = new Date ( start )
56+ startRange = this . freqType ( startDate , this . freq )
57+ endDate = new Date ( end )
58+ endRange = this . freqType ( endDate , this . freq )
59+
60+ let startYear = startDate . getFullYear ( )
61+ let endYear = endDate . getFullYear ( )
62+ if ( ( startYear <= endYear ) && ( startDate . getMonth ( ) !== endDate . getMonth ( ) ) ) {
63+ if ( this . freq == "M" ) {
64+ endRange = this . monthEnd ( startDate , endDate )
65+ }
66+ else if ( this . freq === "D" ) {
67+ endRange = this . dayEnd ( startDate , endDate ) - startRange
68+ }
69+ }
70+ let rangeArray = utils . range ( startRange , endRange )
71+ if ( offset ) {
72+ rangeArray = this . offsetCount ( rangeArray , offset )
73+ }
74+ let dateRange = rangeArray . map ( ( x ) => {
75+ return this . setDateProps ( startDate , this . freq , x )
76+ } )
77+ dateRange [ dateRange . length - 1 ] = endDate
78+ let dateString = this . toLocalString ( dateRange )
79+ return dateString
80+ }
81+ else if ( start && ! ( end ) ) {
82+ startDate = new Date ( start )
83+ startRange = this . freqType ( startDate , this . freq )
84+ period = period as number
85+ endRange = offset ? ( ( period * offset ) - 1 ) : period - 1 ;
86+
87+ if ( startRange > endRange ) {
88+ endRange = endRange + startRange
89+ }
90+ let rangeArray = utils . range ( startRange , endRange )
91+
92+ if ( offset ) {
93+ rangeArray = this . offsetCount ( rangeArray , offset )
94+ }
95+ let dateRange = rangeArray . map ( ( x ) => {
96+ return this . setDateProps ( startDate , this . freq , x )
97+ } )
98+
99+ let dateString = this . toLocalString ( dateRange )
100+ return dateString
101+ }
102+ // if end and not start given
103+ endDate = new Date ( end as string )
104+ endRange = this . freqType ( endDate , this . freq )
105+ period = period as number
106+ startRange = ( endRange - period ) + 1
107+ let rangeArray = utils . range ( startRange , endRange )
108+
109+ if ( offset ) {
110+ rangeArray = this . offsetCount ( rangeArray , offset )
111+ }
112+ let dateRange = rangeArray . map ( ( x ) => {
113+ return this . setDateProps ( endDate , this . freq , x )
114+ } )
115+ let dateString = this . toLocalString ( dateRange )
116+ return dateString
117+ }
118+
119+ private freqType ( date : Date , ftype : string ) : number {
120+ let rslt : number = 0 ;
121+ switch ( ftype ) {
122+
123+ case "M" :
124+ rslt = date . getMonth ( ) ;
125+ break ;
126+ case "Y" :
127+ rslt = date . getFullYear ( ) ;
128+ break ;
129+ case "s" :
130+ rslt = date . getSeconds ( ) ;
131+ break ;
132+ case "D" :
133+ rslt = date . getDate ( ) ;
134+ break ;
135+ case "H" :
136+ rslt = date . getHours ( ) ;
137+ break ;
138+ case "m" :
139+ rslt = date . getMinutes ( ) ;
140+ break ;
141+ }
142+ return rslt ;
143+ }
144+
145+
146+ private offsetCount ( dArray : number [ ] , offset : number ) :number [ ] {
147+ let rArray : number [ ] = [ ]
148+ for ( let i = 0 ; i < dArray . length ; i += offset ) {
149+ rArray . push ( dArray [ i ] ) ;
150+ }
151+ return rArray ;
152+ }
153+
154+ private setDateProps ( date : Date , ftype : string , val : number [ ] | number ) : Date {
155+ let newDate = new Date ( date . valueOf ( ) )
156+ switch ( ftype ) {
157+ case "M" :
158+ if ( Array . isArray ( val ) ) {
159+
160+ newDate . setFullYear ( newDate . getFullYear ( ) + val [ 0 ] ) ;
161+ newDate . setMonth ( val [ 1 ] ) ;
162+ } else {
163+ newDate . setMonth ( val ) ;
164+ }
165+ break ;
166+ case "Y" :
167+ newDate . setFullYear ( val as number ) ;
168+ break ;
169+ case "s" :
170+ newDate . setSeconds ( val as number ) ;
171+ break ;
172+ case "D" :
173+ newDate . setDate ( val as number ) ;
174+ break ;
175+ case "H" :
176+ newDate . setHours ( val as number ) ;
177+ break ;
178+ case "m" :
179+ newDate . setMinutes ( val as number ) ;
180+ break ;
181+ }
182+ return newDate ;
183+ }
184+
185+
186+ private toLocalString ( dArray : Date [ ] ) {
187+ let r_array = dArray . map ( ( x ) => {
188+ return x . toLocaleString ( ) ;
189+ } ) ;
190+ return r_array ;
191+ }
192+
193+ private monthEnd ( startDate : Date , endDate : Date ) {
194+ let endMonth = endDate . getMonth ( )
195+ let diffYear = endDate . getFullYear ( ) - startDate . getFullYear ( )
196+ let endRange = ( 12 * diffYear ) + endMonth
197+ return endRange
198+ }
199+
200+ private monthRange ( range : number [ ] ) : number [ ] [ ] {
201+ let minus : number ;
202+ let yVal = 0
203+ let dateRange : number [ ] [ ] = range . map ( ( x ) => {
204+ if ( x > 11 ) {
205+ if ( x % 12 == 0 ) {
206+ minus = x
207+ yVal = x / 12
208+ return [ yVal , ( x - minus ) ]
209+ }
210+ else {
211+ return [ yVal , ( x - minus ) ]
212+ }
213+ }
214+ return [ yVal , x ]
215+ } )
216+ return dateRange
217+ }
218+
219+ private dayEnd ( startDate : Date , endDate : Date ) : number {
220+ let monthEnd = this . monthEnd ( startDate , endDate )
221+ let range = utils . range ( startDate . getMonth ( ) , monthEnd )
222+ let mRange = this . monthRange ( range )
223+
224+ let sum = 0
225+ for ( let i = 0 ; i < mRange . length ; i ++ ) {
226+ let val = mRange [ i ]
227+ let dDate : number
228+ if ( i === mRange . length - 1 ) {
229+ dDate = new Date ( startDate . getUTCFullYear ( ) + val [ 0 ] , val [ 1 ] , endDate . getDate ( ) ) . getDate ( )
230+ }
231+ else {
232+ dDate = new Date ( startDate . getUTCFullYear ( ) + val [ 0 ] , val [ 1 ] , 0 ) . getDate ( )
233+ }
234+ sum += dDate
235+ }
236+ return sum
237+ }
238+ }
239+
240+ /**
241+ *
242+ * @param start : signify the date to start with
243+ * @param end : signify the date to end with
244+ * @param period : the total number of date to generate
245+ * @param offset : set the date range offset
246+ * @param freq: set the date range frequency and offset
247+ * @return string[]
248+ */
249+ export default function date_range ( param : Params ) : string [ ] {
250+ const dateRange = new DateRange ( param )
251+ return dateRange . range ( )
252+ }
0 commit comments