1+ import {
2+ getDateForWeekOfYear
3+ } from "../utils/week-of-year" ;
4+
15export const RomanNumberRegexStr : string = ' *([MDCLXVI]+)' ; // Roman number
26export const CompoundRomanNumberDotRegexStr : string = ' *([MDCLXVI]+(?:\\.[MDCLXVI]+)*)' ; // Compound Roman number with dot as separator
37export const CompoundRomanNumberDashRegexStr : string = ' *([MDCLXVI]+(?:-[MDCLXVI]+)*)' ; // Compound Roman number with dash as separator
@@ -6,15 +10,26 @@ export const NumberRegexStr: string = ' *(\\d+)'; // Plain number
610export const CompoundNumberDotRegexStr : string = ' *(\\d+(?:\\.\\d+)*)' ; // Compound number with dot as separator
711export const CompoundNumberDashRegexStr : string = ' *(\\d+(?:-\\d+)*)' ; // Compound number with dash as separator
812
13+ export const Date_yyyy_mm_dd_RegexStr : string = ' *(\\d{4}-[0-3]*[0-9]-[0-3]*[0-9])'
14+ export const Date_yyyy_dd_mm_RegexStr : string = Date_yyyy_mm_dd_RegexStr
15+
916export const Date_dd_Mmm_yyyy_RegexStr : string = ' *([0-3]*[0-9]-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\\d{4})' ; // Date like 01-Jan-2020
1017export const Date_Mmm_dd_yyyy_RegexStr : string = ' *((?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-[0-3]*[0-9]-\\d{4})' ; // Date like Jan-01-2020
1118
12- export const DOT_SEPARATOR = '.'
19+ export const Date_yyyy_Www_mm_dd_RegexStr : string = ' *(\\d{4}-W[0-5]*[0-9] \\([0-3]*[0-9]-[0-3]*[0-9]\\))'
20+ export const Date_yyyy_WwwISO_RegexStr : string = ' *(\\d{4}-W[0-5]*[0-9][-+]?)'
21+ export const Date_yyyy_Www_RegexStr : string = Date_yyyy_WwwISO_RegexStr
22+
23+ export const DOT_SEPARATOR = '.' // ASCII 46
1324export const DASH_SEPARATOR = '-'
1425
15- const SLASH_SEPARATOR = '/' // ASCII 47
26+ const SLASH_SEPARATOR = '/' // ASCII 47, right before ASCII 48 = '0'
27+ const GT_SEPARATOR = '>' // ASCII 62, alphabetical sorting in Collator puts it after /
1628const PIPE_SEPARATOR = '|' // ASCII 124
1729
30+ const EARLIER_THAN_SLASH_SEPARATOR = DOT_SEPARATOR
31+ const LATER_THAN_SLASH_SEPARATOR = GT_SEPARATOR
32+
1833export const DEFAULT_NORMALIZATION_PLACES = 8 ; // Fixed width of a normalized number (with leading zeros)
1934
2035// Property escapes:
@@ -51,9 +66,9 @@ export function getNormalizedNumber(s: string = '', separator?: string, places?:
5166 // guarantees correct order (/ = ASCII 47, | = ASCII 124)
5267 if ( separator ) {
5368 const components : Array < string > = s . split ( separator ) . filter ( s => s )
54- return `${ components . map ( ( c ) => prependWithZeros ( c , places ?? DEFAULT_NORMALIZATION_PLACES ) ) . join ( PIPE_SEPARATOR ) } // `
69+ return `${ components . map ( ( c ) => prependWithZeros ( c , places ?? DEFAULT_NORMALIZATION_PLACES ) ) . join ( PIPE_SEPARATOR ) } ${ SLASH_SEPARATOR } ${ SLASH_SEPARATOR } `
5570 } else {
56- return `${ prependWithZeros ( s , places ?? DEFAULT_NORMALIZATION_PLACES ) } // `
71+ return `${ prependWithZeros ( s , places ?? DEFAULT_NORMALIZATION_PLACES ) } ${ SLASH_SEPARATOR } ${ SLASH_SEPARATOR } `
5772 }
5873}
5974
@@ -97,9 +112,9 @@ export function getNormalizedRomanNumber(s: string, separator?: string, places?:
97112 // guarantees correct order (/ = ASCII 47, | = ASCII 124)
98113 if ( separator ) {
99114 const components : Array < string > = s . split ( separator ) . filter ( s => s )
100- return `${ components . map ( ( c ) => prependWithZeros ( romanToIntStr ( c ) , places ?? DEFAULT_NORMALIZATION_PLACES ) ) . join ( PIPE_SEPARATOR ) } // `
115+ return `${ components . map ( ( c ) => prependWithZeros ( romanToIntStr ( c ) , places ?? DEFAULT_NORMALIZATION_PLACES ) ) . join ( PIPE_SEPARATOR ) } ${ SLASH_SEPARATOR } ${ SLASH_SEPARATOR } `
101116 } else {
102- return `${ prependWithZeros ( romanToIntStr ( s ) , places ?? DEFAULT_NORMALIZATION_PLACES ) } // `
117+ return `${ prependWithZeros ( romanToIntStr ( s ) , places ?? DEFAULT_NORMALIZATION_PLACES ) } ${ SLASH_SEPARATOR } ${ SLASH_SEPARATOR } `
103118 }
104119}
105120
@@ -117,9 +132,76 @@ export function getNormalizedDate_NormalizerFn_for(separator: string, dayIdx: nu
117132 const monthValue = months ? `${ 1 + MONTHS . indexOf ( components [ monthIdx ] ) } ` : components [ monthIdx ]
118133 const month = prependWithZeros ( monthValue , MONTH_POSITIONS )
119134 const year = prependWithZeros ( components [ yearIdx ] , YEAR_POSITIONS )
120- return `${ year } -${ month } -${ day } // `
135+ return `${ year } -${ month } -${ day } ${ SLASH_SEPARATOR } ${ SLASH_SEPARATOR } `
121136 }
122137}
123138
139+ export const getNormalizedDate_yyyy_mm_dd_NormalizerFn = getNormalizedDate_NormalizerFn_for ( '-' , 2 , 1 , 0 )
140+ export const getNormalizedDate_yyyy_dd_mm_NormalizerFn = getNormalizedDate_NormalizerFn_for ( '-' , 1 , 2 , 0 )
124141export const getNormalizedDate_dd_Mmm_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for ( '-' , 0 , 1 , 2 , MONTHS )
125142export const getNormalizedDate_Mmm_dd_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for ( '-' , 1 , 0 , 2 , MONTHS )
143+
144+ const DateExtractor_orderModifier_earlier_than = '-'
145+ const DateExtractor_orderModifier_later_than = '+'
146+
147+ const DateExtractor_yyyy_Www_mm_dd_Regex = / ( \d { 4 } ) - W ( \d { 1 , 2 } ) \( ( \d { 2 } ) - ( \d { 2 } ) \) /
148+ const DateExtractor_yyyy_Www_Regex = / ( \d { 4 } ) - W ( \d { 1 , 2 } ) ( [ - + ] ? ) /
149+
150+ // Matching groups
151+ const YEAR_IDX = 1
152+ const WEEK_IDX = 2
153+ const MONTH_IDX = 3
154+ const DAY_IDX = 4
155+ const RELATIVE_ORDER_IDX = 3 // For the yyyy-Www only: yyyy-Www- or yyyy-Www+
156+
157+ const DECEMBER = 12
158+ const JANUARY = 1
159+
160+ export function getNormalizedDate_NormalizerFn_yyyy_Www_mm_dd ( consumeWeek : boolean , weeksISO ?: boolean ) {
161+ return ( s : string ) : string | null => {
162+ // Assumption - the regex date matched against input s, no extensive defensive coding needed
163+ const matches = consumeWeek ? DateExtractor_yyyy_Www_Regex . exec ( s ) : DateExtractor_yyyy_Www_mm_dd_Regex . exec ( s )
164+ const yearStr = matches ! [ YEAR_IDX ]
165+ let yearNumber = Number . parseInt ( yearStr , 10 )
166+ let monthNumber : number
167+ let dayNumber : number
168+ let separator = SLASH_SEPARATOR // different values enforce relative > < order of same dates
169+ let useLastDayOfWeek : boolean = false
170+ if ( consumeWeek ) {
171+ const weekNumberStr = matches ! [ WEEK_IDX ]
172+ const weekNumber = Number . parseInt ( weekNumberStr , 10 )
173+ const orderModifier : string | undefined = matches ! [ RELATIVE_ORDER_IDX ]
174+ if ( orderModifier === DateExtractor_orderModifier_earlier_than ) {
175+ separator = EARLIER_THAN_SLASH_SEPARATOR
176+ } else if ( orderModifier === DateExtractor_orderModifier_later_than ) {
177+ separator = LATER_THAN_SLASH_SEPARATOR // Will also need to adjust the date to the last day of the week
178+ useLastDayOfWeek = true
179+ }
180+ const dateForWeek = getDateForWeekOfYear ( yearNumber , weekNumber , weeksISO , useLastDayOfWeek )
181+ monthNumber = dateForWeek . getMonth ( ) + 1 // 1 - 12
182+ dayNumber = dateForWeek . getDate ( ) // 1 - 31
183+ // Be careful with edge dates, which can belong to previous or next year
184+ if ( weekNumber === 1 ) {
185+ if ( monthNumber === DECEMBER ) {
186+ yearNumber --
187+ }
188+ }
189+ if ( weekNumber >= 50 ) {
190+ if ( monthNumber === JANUARY ) {
191+ yearNumber ++
192+ }
193+ }
194+ } else { // ignore week
195+ monthNumber = Number . parseInt ( matches ! [ MONTH_IDX ] , 10 )
196+ dayNumber = Number . parseInt ( matches ! [ DAY_IDX ] , 10 )
197+ }
198+ return `${ prependWithZeros ( `${ yearNumber } ` , YEAR_POSITIONS ) } ` +
199+ `-${ prependWithZeros ( `${ monthNumber } ` , MONTH_POSITIONS ) } ` +
200+ `-${ prependWithZeros ( `${ dayNumber } ` , DAY_POSITIONS ) } ` +
201+ `${ separator } ${ SLASH_SEPARATOR } `
202+ }
203+ }
204+
205+ export const getNormalizedDate_yyyy_Www_mm_dd_NormalizerFn = getNormalizedDate_NormalizerFn_yyyy_Www_mm_dd ( false )
206+ export const getNormalizedDate_yyyy_WwwISO_NormalizerFn = getNormalizedDate_NormalizerFn_yyyy_Www_mm_dd ( true , true )
207+ export const getNormalizedDate_yyyy_Www_NormalizerFn = getNormalizedDate_NormalizerFn_yyyy_Www_mm_dd ( true , false )
0 commit comments