@@ 1,144 1,78 @@
:Namespace date
- test1←{
- string←'Thu, 17-Feb-2022 15:10:07'
- pattern←'Ddd, DD-Mmm-YYYY hh:mm:ss'
- newstring←⊃pattern(1200⌶)pattern DDN string
- ⎕←string
- ⎕←newstring
- ⎕←'Same? ',(string≡newstring)
- }
- test2←{
- string←'02/17/22 3P:39'
- pattern←'MM/DD/YY tP:mm'
- newstring←⊃pattern(1200⌶)pattern DDN string
- ⎕←string
- ⎕←newstring
- ⎕←'Same? ',(string≡newstring)
- }
- test3←{
- string←'Thursday'
- pattern←'Dddd'
- newstring←⊃pattern(1200⌶)pattern DDN string
- ⎕←string
- ⎕←newstring
- ⎕←'Same? ',(string≡newstring)
- }
DDN←{
- info←13⍴⊂⍬ ⍝ The 13 fields from the variant table, without fractional seconds
- info←⊃parse⍣≡info ⍺ ⍵ ⍝ Continue parsing until there is no more to parse
-
- ⍝ At this point, we may not have all information, so we need to fix that
- info←fixYear fixMonth fixDay fixHour fixMinute fixSecond info
- ⎕←info
- 1 ⎕DT⊂,↑6↑info ⍝ convert the year, month, day, hour, minute, second to DDN
+ (⍺ searchDdn ⍵)⍣≡1 ⎕DT⊂normalize 6↑fixHour⊃parse⍣≡(13⍴⊂⍬)⍺ ⍵
}
parse←{
(info format str)←⍵
- str≡'':⍵
- format startsWith⊂'YYYY':4(4000∘readNum)update 1⊢⍵
- format startsWith⊂'YY':2(0 2000+99∘readNum)update 1⊢⍵
- format startsWith'MMMM' 'Mmmm' 'mmmm' '_mmm':4(0∘readMonth)update 2⊢⍵
- format startsWith'MMM' 'Mmm' 'mmm' '_mm':3(1∘readMonth)update 2⊢⍵
- format startsWith'MM' '_M':2(12∘readNum)update 2⊢⍵
- format startsWith⊂,'M':1(12∘readNum)update 2⊢⍵
- format startsWith'DDDD' 'Dddd' 'dddd' '_ddd':4(0∘readDay)update 7⊢⍵
- format startsWith'DDD' 'Ddd' 'ddd' '_dd':3(1∘readDay)update 7⊢⍵
- format startsWith⊂,'d':1(7∘readNum)update 7⊢⍵
- format startsWith'DD' '_D':2(31∘readNum)update 3⊢⍵
- format startsWith⊂,'D':1(31∘readNum)update 3⊢⍵
- format startsWith'hh' '_h':2(23∘readNum)update 4⊢⍵
- format startsWith⊂,'h':1(23∘readNum)update 4⊢⍵
- format startsWith'mm' '_m':2(59∘readNum)update 5⊢⍵
- format startsWith⊂,'m':1(59∘readNum)update 5⊢⍵
- format startsWith'ss' '_s':2(59∘readNum)update 6⊢⍵
- format startsWith⊂,'s':1(59∘readNum)update 6⊢⍵
- format startsWith'ww' '_w':2(53∘readNum)update 8⊢⍵
- format startsWith⊂,'w':1(53∘readNum)update 8⊢⍵
- format startsWith⊂'WWWW':4(4000∘readNum)update 9⊢⍵
- format startsWith⊂'WW':2(4000∘readNum)update 9⊢⍵
- format startsWith'yy' '_y':2(366∘readNum)update 10⊢⍵
- format startsWith⊂,'y':1(366∘readNum)update 10⊢⍵
- format startsWith'OO' 'Oo' 'oo':2{2,⎕C⊃⍵}update 11⊢⍵
- format startsWith'O' 'o':1{1,⎕C⊃⍵}update 11⊢⍵
- format startsWith'tt' '_t':2(12∘readNum)update 12⊢⍵
- format startsWith⊂,'t':1(12∘readNum)update 12⊢⍵
- format startsWith'PP' 'pp':2{2,⎕C⊃⍵}update 13⊢⍵
- format startsWith'P' 'p':1{1,⎕C⊃⍵}update 13⊢⍵
- 0⊣update 0⊢⍵
- }
- startsWith←{
- ∨⌿⊃⍤⍷∘⍺¨⍵
+ str≡'':⍵ ⍝ Stop parsing if the pattern is empty
+ match←format∘{∨⌿⊃⍤⍷∘⍺¨⍵}
+ match⊂'YYYY':1 4(4000∘readNum)update ⍵
+ match⊂'YY':1 2(0 2000+99∘readNum)update ⍵
+ match'MMMM' 'Mmmm' 'mmmm' '_mmm':2 4(0∘readMonth)update ⍵
+ match'MMM' 'Mmm' 'mmm' '_mm':2 3(1∘readMonth)update ⍵
+ match'MM' '_M':2 2(12∘readNum)update ⍵
+ match⊂,'M':2 1(12∘readNum)update ⍵
+ match'DDDD' 'Dddd' 'dddd' '_ddd':7 4(0∘readDay)update ⍵
+ match'DDD' 'Ddd' 'ddd' '_dd':7 3(1∘readDay)update ⍵
+ match⊂,'d':7 1(7∘readNum)update ⍵
+ match'DD' '_D':3 2(31∘readNum)update ⍵
+ match⊂,'D':3 1(31∘readNum)update ⍵
+ match'hh' '_h':4 2(23∘readNum)update ⍵
+ match⊂,'h':4 1(23∘readNum)update ⍵
+ match'mm' '_m':5 2(59∘readNum)update ⍵
+ match⊂,'m':5 1(59∘readNum)update ⍵
+ match'ss' '_s':6 2(59∘readNum)update ⍵
+ match⊂,'s':6 1(59∘readNum)update ⍵
+ match'ww' '_w':8 2(53∘readNum)update ⍵
+ match⊂,'w':8 1(53∘readNum)update ⍵
+ match⊂'WWWW':9 4(4000∘readNum)update ⍵
+ match⊂'WW':9 2(4000∘readNum)update ⍵
+ match'yy' '_y':10 2(366∘readNum)update ⍵
+ match⊂,'y':10 1(366∘readNum)update ⍵
+ match'OO' 'Oo' 'oo':11 2{2,⎕C⊃⍵}update ⍵
+ match'O' 'o':11 1{1,⎕C⊃⍵}update ⍵
+ match'tt' '_t':12 2(12∘readNum)update ⍵
+ match⊂,'t':12 1(12∘readNum)update ⍵
+ match'PP' 'pp':13 2{2,⎕C⊃⍵}update ⍵
+ match'P' 'p':13 1{1,⎕C⊃⍵}update ⍵
+ 0 0⊣update ⍵
}
update←{
(info format str)←⍵
- ⍵⍵=0:info(1↓format)(1↓str) ⍝ Skip one char in both format and string
- pattern←⍺↑format
+ (index patLength)←⍺
+ index=0:info(1↓format)(1↓str) ⍝ Skip one char in both format and string
+ pattern←patLength↑format
(length value)←⍺⍺ str
- info[⍵⍵]←value
- info(⍺↓format)(length↓str)
+ info[index]←value
+ info(patLength↓format)(length↓str)
}
- ⍝ Below are all the "read" functions. They all return (value length) pairs
+ ⍝ Below are all the "read" functions. They all return (length value) pairs
readNum←{
- (valid n)←⎕VFI ⍵↑⍨maxLen←≢⍕⍺
- valid∧n≤⍺:maxLen n
- (¯1+10*maxLen-1)∇ ⍵
+ str←⍵↑⍨len←≢⍕⍺
+ (valid n)←⎕VFI str
+ valid∧(∧⌿str∊⎕D)∧n≤⍺:len n
+ (¯1+10*len-1)∇ ⍵
}
readMonth←{
- months←'jan' 'feb' 'mar' 'apr' 'may' 'jun' 'jul' 'aug' 'sep' 'oct' 'nov' 'dec'
- lengths←7 8 5 5 3 4 4 6 9 7 8 8
- m←months⍳⊂⎕C 3↑⍵
- ⍺:3 m ⍝ ⍺=1 means short form
- lengths[m]m
+ ⍺('jan' 'feb' 'mar' 'apr' 'may' 'jun' 'jul' 'aug' 'sep' 'oct' 'nov' 'dec'readMonthDay 7 8 5 5 3 4 4 6 9 7 8 8)⍵
}
readDay←{
- days←'mon' 'tue' 'wed' 'thu' 'fri' 'sat' 'sun'
- lengths←6 7 9 8 6 8 6
- d←days⍳⊂⎕C 3↑⍵
- ⍺:3 d ⍝ ⍺=1 means short form
- lengths[d]d
+ ⍺('mon' 'tue' 'wed' 'thu' 'fri' 'sat' 'sun'readMonthDay 6 7 9 8 6 8 6)⍵
}
- ⍝ Below are all the fixup functions. If a value is present at the place
- ⍝ we want to fix, the function doesn't change anything.
- ⍝ Otherwise they use the most precise information available to set the field.
- ⍝ If no information is available, they just pick a valid value.
- fixYear←{
- ⍝ No information can decide the year, so just use 2022
- ⍵[1]≢⊂⍬:⍵
- (2022@1)⍵
+ readMonthDay←{
+ i←⍺⍺⍳⊂⎕C 3↑⍵ ⍝ look for the first 3 letters of ⍵ in ⍺⍺ (ignore case)
+ ⍺:3 i ⍝ ⍺=1 means short form, always length 3
+ ⍵⍵[i]i
}
- fixMonth←{
- ⍝ Week information can decide the month
- ⍝ Day of year can decide the month
- ⍝ Day of month can decide month (limit the cases)
- ⍝ Ordinal indicator for month can decide month
- ⍵[2]≢⊂⍬:⍵
- (1@2)⍵
+ searchDdn←{
+ ⍵+⍵⍵≢⊃⍺⍺(1200⌶)⍵
}
- fixDay←{
- ⍝ The following fields can influence the day:
- ⍝ ⍵[7]: day of week
- ⍝ ⍵[8]: ISO week number
- ⍝ ⍵[9]: Year of iso week number
- ⍝ ⍵[10]: Day of year
- ⍝ ⍵[11]: Ordinal indicator for day of month
- ⍝ PERHAPS: do I need to calculate Y/M/D together, as they all depend on each other?
- ⍵[3]≢⊂⍬:⍵
- (1@3)⍵
+ normalize←{
+ 4000 12 28 23 59 59⌊1899 1 1 0 0 0⌈0@{⍵⍷⍨⊂⍬}⍵
}
fixHour←{
- ⍵[4]≢⊂⍬:⍵
+ (⍵[4]≢⊂⍬)∨(⍵[5]≡⊂⍬)∧(⍵[6]≡⊂⍬):⍵
⍵[12]≢⊂⍬:((⍵[12]+12×⍵[13]='p')@4)⍵ ⍝ If whe have 12 hour clock
⍵[13]≡'p':(12@4)⍵ ⍝ If we have AM/PM, but not clock
- (0@4)⍵ ⍝ Default value of 0
- }
- fixMinute←{
- ⍝ No information describes the minutes, so pick 0
- ⍵[5]≢⊂⍬:⍵
- (0@5)⍵
- }
- fixSecond←{
- ⍝ No information describes the seconds, so pick 0
- ⍵[6]≢⊂⍬:⍵
- (0@6)⍵
}
:EndNamespace