~pmikkelsen/dyalog-competition-2022

124a7b41fc6b6ff34ad6375ecf1eb8d3e24e438d — Peter Mikkelsen 8 months ago d7f28d8
Implement a working version of the date program
1 files changed, 54 insertions(+), 120 deletions(-)

M date.apln
M date.apln => date.apln +54 -120
@@ 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