0 | module IotaTime.Pattern.CalendarDate
2 | import Data.String.Parser
4 | import IotaTime.Pattern
5 | import IotaTime.Locale
6 | import IotaTime.Calendar
7 | import IotaTime.Calendar.Gregorian
8 | import IotaTime.Internal.Text
9 | import IotaTime.Pattern.Calendar
15 | record DateFieldsRep where
16 | constructor MkDateFields
17 | parsedYear : Integer
18 | parsedMonth : Integer
20 | {default Nothing parsedWeekday : Maybe (Fin 7)}
25 | DateFields = DateFieldsRep
31 | dateFields : (year : Integer) -> (month : Integer) -> (day : Integer) ->
33 | dateFields year month day = MkDateFields year month day
35 | initialDateFields : DateFields
36 | initialDateFields = dateFields 2000 3 1
38 | monthFromInteger : Integer -> Month
39 | monthFromInteger 1 = January
40 | monthFromInteger 2 = February
41 | monthFromInteger 3 = March
42 | monthFromInteger 4 = April
43 | monthFromInteger 5 = May
44 | monthFromInteger 6 = June
45 | monthFromInteger 7 = July
46 | monthFromInteger 8 = August
47 | monthFromInteger 9 = September
48 | monthFromInteger 10 = October
49 | monthFromInteger 11 = November
50 | monthFromInteger _ = December
52 | finishDate : {calendar : Type} ->
53 | {auto patterned : CalendarPattern calendar} ->
54 | DateFields -> Either PatternError (CalendarDate calendar)
55 | finishDate {calendar} @{patterned} fields = do
56 | date <- refinePatternDate {calendar} @{patterned}
57 | fields.parsedYear fields.parsedMonth fields.parsedDay
58 | case fields.parsedWeekday of
59 | Nothing => Right date
61 | if patternWeekdayIndex {calendar} @{patterned} date == expected
63 | else Left (InvalidValue "weekday does not match date")
65 | dateField : {calendar : Type} ->
66 | {auto patterned : CalendarPattern calendar} ->
67 | (CalendarDate calendar -> Integer) ->
68 | (Integer -> DateFields -> DateFields) ->
69 | (width : Nat) -> (maximumWidth : Nat) ->
70 | (minimum : Integer) -> (maximum : Integer) ->
71 | Pattern DateFields (CalendarDate calendar)
72 | dateField getter setter width maximumWidth minimum maximum = MkPattern
75 | (numberUpdatePart setter width maximumWidth minimum maximum)
76 | (zeroPadInteger width . getter)
78 | setYearField : Integer -> DateFields -> DateFields
79 | setYearField value fields = { parsedYear := value } fields
81 | setMonthField : Integer -> DateFields -> DateFields
82 | setMonthField value fields = { parsedMonth := value } fields
84 | setDayField : Integer -> DateFields -> DateFields
85 | setDayField value fields = { parsedDay := value } fields
87 | setWeekdayField : Fin 7 -> DateFields -> DateFields
88 | setWeekdayField value fields = { parsedWeekday := Just value } fields
90 | setMonth : Month -> DateFields -> DateFields
91 | setMonth value fields = { parsedMonth := monthNumber value } fields
93 | calendarYear : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
94 | CalendarDate calendar -> Integer
95 | calendarYear date = yearValue (yearFor {calendar} date)
97 | calendarMonthIndex : {calendar : Type} ->
98 | {auto patterned : CalendarPattern calendar} ->
99 | CalendarDate calendar ->
100 | Fin (patternMonthCount {calendar} @{patterned})
101 | calendarMonthIndex {calendar} @{patterned} =
102 | patternMonthIndex {calendar} @{patterned}
104 | calendarMonth : {calendar : Type} ->
105 | {auto patterned : CalendarPattern calendar} ->
106 | CalendarDate calendar -> Integer
107 | calendarMonth {calendar} @{patterned} date =
108 | cast (finToNat (calendarMonthIndex {calendar} @{patterned} date)) + 1
110 | calendarDay : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
111 | CalendarDate calendar -> Integer
112 | calendarDay date = dayOfMonthValue (dayFor {calendar} date)
114 | gregorianMonths : Vect 12 Month
116 | [ January, February, March, April, May, June
117 | , July, August, September, October, November, December
120 | gregorianWeekdays : Vect 7 DayOfWeek
121 | gregorianWeekdays =
122 | [ Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday ]
124 | abbreviate : String -> String
125 | abbreviate = substr 0 3
127 | indexedNames : Integer -> Vect size String -> List (String, Integer)
128 | indexedNames _ [] = []
129 | indexedNames value (name :: names) =
130 | (name, value) :: indexedNames (value + 1) names
132 | indexedFinNames : Vect size String -> List (String, Fin size)
133 | indexedFinNames [] = []
134 | indexedFinNames (name :: names) =
135 | (name, FZ) :: map (map FS) (indexedFinNames names)
137 | calendarMonthNamePattern : {calendar : Type} ->
138 | {auto patterned : CalendarPattern calendar} ->
139 | Vect (patternMonthCount {calendar} @{patterned}) String ->
140 | Pattern DateFields (CalendarDate calendar)
141 | calendarMonthNamePattern {calendar} @{patterned} names = MkPattern
144 | (namedUpdatePart (indexedNames 1 names) setMonthField)
145 | (\date => index (calendarMonthIndex {calendar} @{patterned} date) names)
147 | weekdayNames : Vect 7 String
149 | [ "Sunday", "Monday", "Tuesday", "Wednesday"
150 | , "Thursday", "Friday", "Saturday"
153 | weekdayAbbreviations : Vect 7 String
154 | weekdayAbbreviations =
155 | [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]
157 | calendarDayNamePattern : {calendar : Type} ->
158 | {auto patterned : CalendarPattern calendar} ->
159 | Vect 7 String -> Pattern DateFields (CalendarDate calendar)
160 | calendarDayNamePattern {calendar} @{patterned} names = MkPattern
163 | (namedConsumePart (toList names))
164 | (\date => index (patternWeekdayIndex {calendar} @{patterned} date) names)
166 | verifiedCalendarDayNamePattern : {calendar : Type} ->
167 | {auto patterned : CalendarPattern calendar} ->
168 | Vect 7 String -> Pattern DateFields (CalendarDate calendar)
169 | verifiedCalendarDayNamePattern {calendar} @{patterned} names = MkPattern
172 | (namedUpdatePart (indexedFinNames names) setWeekdayField)
173 | (patternFormatPart (calendarDayNamePattern {calendar} @{patterned} names))
175 | englishMonthNames : Vect 12 String
176 | englishMonthNames = map show gregorianMonths
178 | englishMonthAbbreviations : Vect 12 String
179 | englishMonthAbbreviations = map abbreviate englishMonthNames
181 | englishWeekdayNames : Vect 7 String
182 | englishWeekdayNames = map show gregorianWeekdays
184 | englishWeekdayAbbreviations : Vect 7 String
185 | englishWeekdayAbbreviations = map abbreviate englishWeekdayNames
189 | pyear : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
190 | Nat -> Pattern DateFields (CalendarDate calendar)
191 | pyear width = dateField calendarYear setYearField width 4 0 9999
195 | pyyyy : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
196 | Pattern DateFields (CalendarDate calendar)
199 | inferTwoDigitYear : Integer -> Integer -> Integer
200 | inferTwoDigitYear template value =
201 | let base = (template `div` 100) * 100 + value
202 | adjustment = (template - base + 50) `div` 100
203 | in base + adjustment * 100
207 | pyy : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
208 | Pattern DateFields (CalendarDate calendar)
214 | { parsedYear := inferTwoDigitYear fields.parsedYear value } fields)
216 | (zeroPadInteger 2 . (`mod` 100) . calendarYear)
220 | pmonthNum : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
221 | Nat -> Pattern DateFields (CalendarDate calendar)
222 | pmonthNum {calendar} @{patterned} width =
223 | dateField (calendarMonth {calendar} @{patterned}) setMonthField
224 | width 2 1 (cast (patternMonthCount {calendar} @{patterned}))
228 | pMM : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
229 | Pattern DateFields (CalendarDate calendar)
238 | pMonthName : {default Gregorian calendar : Type} ->
239 | {auto patterned : CalendarPattern calendar} ->
241 | (names : Vect size String) ->
242 | {auto 0 complete : size =
243 | patternMonthCount {calendar} @{patterned}} ->
244 | Pattern DateFields (CalendarDate calendar)
245 | pMonthName {complete = Refl} names = calendarMonthNamePattern names
249 | pMMMM : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
250 | Pattern DateFields (CalendarDate calendar)
251 | pMMMM {calendar} @{patterned} = calendarMonthNamePattern
252 | (patternMonthNames {calendar} @{patterned})
256 | pMMM : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
257 | Pattern DateFields (CalendarDate calendar)
258 | pMMM {calendar} @{patterned} = calendarMonthNamePattern
259 | (patternMonthAbbreviations {calendar} @{patterned})
263 | pday : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
264 | Nat -> Pattern DateFields (CalendarDate calendar)
265 | pday width = dateField calendarDay setDayField width 2 1 31
269 | pdd : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
270 | Pattern DateFields (CalendarDate calendar)
275 | pdaySpace : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
276 | Pattern DateFields (CalendarDate calendar)
277 | pdaySpace = MkPattern
280 | (spaceNumberUpdatePart setDayField 2 1 31)
281 | (\date => let shown = show (calendarDay date) in
282 | if length (unpack shown) < 2 then " " ++ shown else shown)
290 | pDayName : {default Gregorian calendar : Type} ->
291 | {auto patterned : CalendarPattern calendar} ->
292 | Vect 7 String -> Pattern DateFields (CalendarDate calendar)
293 | pDayName = calendarDayNamePattern
298 | pVerifiedDayName : {default Gregorian calendar : Type} ->
299 | {auto patterned : CalendarPattern calendar} ->
300 | Vect 7 String -> Pattern DateFields (CalendarDate calendar)
301 | pVerifiedDayName = verifiedCalendarDayNamePattern
305 | pdddd : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
306 | Pattern DateFields (CalendarDate calendar)
307 | pdddd = calendarDayNamePattern weekdayNames
311 | pddd : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
312 | Pattern DateFields (CalendarDate calendar)
313 | pddd = calendarDayNamePattern weekdayAbbreviations
317 | pddddVerified : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
318 | Pattern DateFields (CalendarDate calendar)
319 | pddddVerified {calendar} =
320 | pVerifiedDayName {calendar} englishWeekdayNames
324 | pdddVerified : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
325 | Pattern DateFields (CalendarDate calendar)
326 | pdddVerified {calendar} =
327 | pVerifiedDayName {calendar} englishWeekdayAbbreviations
329 | localeMonthNames : {calendar : Type} ->
330 | {auto patterned : CalendarPattern calendar} ->
331 | Locale -> Vect (patternMonthCount {calendar} @{patterned}) String
332 | localeMonthNames {calendar} @{patterned} locale =
333 | case patternMonthNameSource {calendar} @{patterned} of
334 | GregorianLocaleMonthNames countIsTwelve =>
335 | replace {p = \monthCount => Vect monthCount String}
336 | (sym countIsTwelve) (monthNames locale)
337 | CanonicalCalendarMonthNames => patternMonthNames {calendar} @{patterned}
339 | localeMonthAbbreviations : {calendar : Type} ->
340 | {auto patterned : CalendarPattern calendar} ->
341 | Locale -> Vect (patternMonthCount {calendar} @{patterned}) String
342 | localeMonthAbbreviations {calendar} @{patterned} locale =
343 | case patternMonthNameSource {calendar} @{patterned} of
344 | GregorianLocaleMonthNames countIsTwelve =>
345 | replace {p = \monthCount => Vect monthCount String}
346 | (sym countIsTwelve) (monthNamesShort locale)
347 | CanonicalCalendarMonthNames =>
348 | patternMonthAbbreviations {calendar} @{patterned}
353 | pMMMM' : {default Gregorian calendar : Type} ->
354 | {auto patterned : CalendarPattern calendar} ->
355 | Locale -> Pattern DateFields (CalendarDate calendar)
356 | pMMMM' {calendar} @{patterned} locale = calendarMonthNamePattern
357 | (localeMonthNames {calendar} @{patterned} locale)
362 | pMMM' : {default Gregorian calendar : Type} ->
363 | {auto patterned : CalendarPattern calendar} ->
364 | Locale -> Pattern DateFields (CalendarDate calendar)
365 | pMMM' {calendar} @{patterned} locale = calendarMonthNamePattern
366 | (localeMonthAbbreviations {calendar} @{patterned} locale)
370 | pdddd' : {default Gregorian calendar : Type} ->
371 | {auto patterned : CalendarPattern calendar} ->
372 | Locale -> Pattern DateFields (CalendarDate calendar)
373 | pdddd' {calendar} locale = pDayName {calendar} (dayNames locale)
377 | pddd' : {default Gregorian calendar : Type} ->
378 | {auto patterned : CalendarPattern calendar} ->
379 | Locale -> Pattern DateFields (CalendarDate calendar)
380 | pddd' {calendar} locale = pDayName {calendar} (dayNamesShort locale)
384 | pddddVerified' : {default Gregorian calendar : Type} ->
385 | {auto patterned : CalendarPattern calendar} ->
386 | Locale -> Pattern DateFields (CalendarDate calendar)
387 | pddddVerified' {calendar} locale =
388 | pVerifiedDayName {calendar} (dayNames locale)
392 | pdddVerified' : {default Gregorian calendar : Type} ->
393 | {auto patterned : CalendarPattern calendar} ->
394 | Locale -> Pattern DateFields (CalendarDate calendar)
395 | pdddVerified' {calendar} locale =
396 | pVerifiedDayName {calendar} (dayNamesShort locale)
400 | pd : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
401 | Pattern DateFields (CalendarDate calendar)
402 | pd = ((pdd <% char '/') <+> (pMM <% char '/')) <+> pyyyy
406 | pD : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
407 | Pattern DateFields (CalendarDate calendar)
408 | pD = (((pdddd <% string ", ") <+> (pdd <% char ' ')) <+>
409 | (pMMMM <% char ' ')) <+> pyyyy
413 | pR : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
414 | Pattern DateFields (CalendarDate calendar)
415 | pR = ((pyyyy <% char '-') <+> (pMM <% char '-')) <+> pdd
419 | pmonthDay : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
420 | Pattern DateFields (CalendarDate calendar)
421 | pmonthDay = (pMMMM <% char ' ') <+> pdd
425 | pyearMonth : {calendar : Type} -> {auto patterned : CalendarPattern calendar} ->
426 | Pattern DateFields (CalendarDate calendar)
427 | pyearMonth = (pyyyy <% char ' ') <+> pMMMM