Purescript library to handle the formatting of dates and times.
1-- SPDX-License-Identifier: MIT
2-- Copyright (C) 2022 Roland Csaszar
3--
4-- Project: purescript-datetimeformat
5-- File: DateTimeFormat.purs
6-- Date: 12.Feb.2022
7--
8-- ==============================================================================
9-- | Module Data.DateTimeFormat, to set the locale and format of a date or time.
10-- | See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
11module Data.DateTimeFormat
12 ( DateStyle(..)
13 , DateTimeFormat
14 , DateTimeFormatOptions(..)
15 , DayFormat(..)
16 , DayPeriod(..)
17 , EraFormat(..)
18 , FormatMatcher(..)
19 , FractionalSecondDigits(..)
20 , Hour12(..)
21 , HourCycle(..)
22 , HourFormat(..)
23 , Locale(..)
24 , LocaleMatcher(..)
25 , MinuteFormat(..)
26 , MonthFormat(..)
27 , SecondFormat(..)
28 , TimeStyle(..)
29 , TimeZone(..)
30 , TimeZoneNameStyle(..)
31 , WeekDayFormat(..)
32 , YearFormat(..)
33 , dateTimeFormat
34 , defaultDateTimeFormat
35 , emptyDateTimeFormatOptions
36 , formatDate
37 , formatDateInts
38 , formatDateIntsUnsafe
39 , formatDateTime
40 , formatDateTimeInts
41 , formatDateTimeIntsUnsafe
42 , formatDateTimeNow
43 , isoDateTime
44 , isoDateTimeInts
45 , isoDateTimeIntsUnsafe
46 , isoDateTimeNow
47 , localeToString
48 , setCalendarFormat
49 , setDateStyle
50 , setDayFormat
51 , setDayPeriod
52 , setEraFormat
53 , setFormatMatcher
54 , setFractionalSecondDigits
55 , setHour12
56 , setHourCycle
57 , setHourFormat
58 , setLocaleMatcher
59 , setMinuteFormat
60 , setMonthFormat
61 , setNumberingSystem
62 , setSecondFormat
63 , setTimeStyle
64 , setTimeZone
65 , setTimeZoneNameStyle
66 , setWeekDayFormat
67 , setYearFormat
68 , stringToLocale
69 , stringToLocaleUnsafe
70 , stringToTimeZone
71 , timeZoneToString
72 ) where
73
74import Prelude
75import Data.Argonaut (class DecodeJson, class EncodeJson)
76import Data.Argonaut.Decode.Generic (genericDecodeJson)
77import Data.Argonaut.Encode.Generic (genericEncodeJson)
78import Data.CalendarFormat (CalendarFormat, calendarFormatToString)
79import Data.Date (Date, canonicalDate, day, month, year)
80import Data.DateTime (DateTime(..), Time(..), hour, minute, second)
81import Data.Enum (fromEnum, toEnum)
82import Data.Function.Uncurried (Fn2, Fn4, Fn6, Fn7, runFn2, runFn4, runFn6, runFn7)
83import Data.Generic.Rep (class Generic)
84import Data.Maybe (Maybe(..))
85import Data.Newtype (class Newtype, unwrap, wrap)
86import Data.NumberingSystem (NumberingSystem, numberingSystemToString)
87import Data.Show.Generic (genericShow)
88import Data.String (trim)
89import Data.String.Regex (Regex, match)
90import Data.String.Regex.Flags (unicode)
91import Data.String.Regex.Unsafe (unsafeRegex)
92import Effect (Effect)
93import Test.QuickCheck (class Arbitrary, arbitrary)
94import Test.QuickCheck.Arbitrary (genericArbitrary)
95import Untagged.Castable (cast)
96import Untagged.Union (UndefinedOr)
97
98{-------------------------------------------------------------------------------
99| Type to hold the locale and formatting options of a date or a time.
100-}
101foreign import data DateTimeFormat :: Type
102
103{-------------------------------------------------------------------------------
104| The type of a locale.
105|
106| This is a wrapped BCP 47 with Unicode extensions ('Extension U') locale tag.
107|
108| Examples of valid locale strings:
109|
110| "en", "zh", "de"
111| "eng", "zho", "deu"
112| "en-US", "zh-cn", "de-AT"
113| "en-US-u-ca-buddhist", "gsw-u-sd-chzh", "he-IL-u-ca-hebrew-tz-jeruslm"
114-}
115newtype Locale
116 = Locale String
117
118derive newtype instance eqLocale :: Eq Locale
119
120derive newtype instance ordLocale :: Ord Locale
121
122derive newtype instance showLocale :: Show Locale
123
124derive newtype instance decodeJsonLocale :: DecodeJson Locale
125
126derive newtype instance encodeJsonLocale :: EncodeJson Locale
127
128derive instance genericLocale :: Generic Locale _
129
130derive instance newtypeLocale :: Newtype Locale _
131
132derive newtype instance arbitraryLocale :: Arbitrary Locale
133
134{-------------------------------------------------------------------------------
135| Convert a `Locale` to a `String`.
136|
137| * `locale` - The `Locale` to convert to a `String`.
138-}
139localeToString :: Locale -> String
140localeToString = unwrap
141
142{-------------------------------------------------------------------------------
143| Convert a BCP 47 language tag `String` to a `Locale`.
144|
145| The given `String` is not validated, every non-empty String containing any
146| non-whitespace character returns a `Locale`!
147|
148| * `str` - The `String` to convert to a `Locale`.
149-}
150stringToLocale :: String -> Maybe Locale
151stringToLocale "" = Nothing
152
153stringToLocale str = case match notOnlyWhitespaceRegex str of
154 Nothing -> Nothing
155 _ -> Just $ wrap $ trim str
156
157{-------------------------------------------------------------------------------
158| Convert a BCP 47 language tag `String` to a `Locale`.
159|
160| The given `String` is not validated, every String returns a `Locale`!
161|
162| * `str` - The `String` to convert to a `Locale`.
163-}
164stringToLocaleUnsafe :: String -> Locale
165stringToLocaleUnsafe = wrap
166
167{-------------------------------------------------------------------------------
168| A empty `DateTimeFormatOptions` object, with all fields set to `Nothing`.
169|
170| This is also the `Monoid`s `mempty`.
171-}
172emptyDateTimeFormatOptions ∷ DateTimeFormatOptions
173emptyDateTimeFormatOptions =
174 DateTimeFormatOptions
175 { dateStyle: Nothing
176 , timeStyle: Nothing
177 , calendar: Nothing
178 , dayPeriod: Nothing
179 , numberingSystem: Nothing
180 , localeMatcher: Nothing
181 , timeZone: Nothing
182 , hour12: Nothing
183 , hourCycle: Nothing
184 , formatMatcher: Nothing
185 , weekDay: Nothing
186 , era: Nothing
187 , year: Nothing
188 , month: Nothing
189 , day: Nothing
190 , hour: Nothing
191 , minute: Nothing
192 , second: Nothing
193 , fractionalSecondDigits: Nothing
194 , timeZoneNameStyle: Nothing
195 }
196
197{-------------------------------------------------------------------------------
198| Set the `dateStyle` field of the given `DateTimeFormatOptions` to the given
199| value.
200-}
201setDateStyle :: DateStyle -> DateTimeFormatOptions -> DateTimeFormatOptions
202setDateStyle style (DateTimeFormatOptions options) = DateTimeFormatOptions options { dateStyle = Just style }
203
204{-------------------------------------------------------------------------------
205| Set the `timeStyle` field of the given `DateTimeFormatOptions` to the given
206| value.
207-}
208setTimeStyle :: TimeStyle -> DateTimeFormatOptions -> DateTimeFormatOptions
209setTimeStyle style (DateTimeFormatOptions options) = DateTimeFormatOptions options { timeStyle = Just style }
210
211{-------------------------------------------------------------------------------
212| Set the `calendar` field of the given `DateTimeFormatOptions` to the given
213| value.
214-}
215setCalendarFormat :: CalendarFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
216setCalendarFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { calendar = Just style }
217
218{-------------------------------------------------------------------------------
219| Set the `dayPeriod` field of the given `DateTimeFormatOptions` to the given
220| value.
221-}
222setDayPeriod :: DayPeriod -> DateTimeFormatOptions -> DateTimeFormatOptions
223setDayPeriod style (DateTimeFormatOptions options) = DateTimeFormatOptions options { dayPeriod = Just style }
224
225{-------------------------------------------------------------------------------
226| Set the `numberingSystem` field of the given `DateTimeFormatOptions` to the given
227| value.
228-}
229setNumberingSystem :: NumberingSystem -> DateTimeFormatOptions -> DateTimeFormatOptions
230setNumberingSystem style (DateTimeFormatOptions options) = DateTimeFormatOptions options { numberingSystem = Just style }
231
232{-------------------------------------------------------------------------------
233| Set the `localeMatcher` field of the given `DateTimeFormatOptions` to the given
234| value.
235-}
236setLocaleMatcher :: LocaleMatcher -> DateTimeFormatOptions -> DateTimeFormatOptions
237setLocaleMatcher style (DateTimeFormatOptions options) = DateTimeFormatOptions options { localeMatcher = Just style }
238
239{-------------------------------------------------------------------------------
240| Set the `timeZone` field of the given `DateTimeFormatOptions` to the given
241| value.
242-}
243setTimeZone :: TimeZone -> DateTimeFormatOptions -> DateTimeFormatOptions
244setTimeZone style (DateTimeFormatOptions options) = DateTimeFormatOptions options { timeZone = Just style }
245
246{-------------------------------------------------------------------------------
247| Set the `hour12` field of the given `DateTimeFormatOptions` to the given
248| value.
249-}
250setHour12 :: Hour12 -> DateTimeFormatOptions -> DateTimeFormatOptions
251setHour12 style (DateTimeFormatOptions options) = DateTimeFormatOptions options { hour12 = Just style }
252
253{-------------------------------------------------------------------------------
254| Set the `hourCycle` field of the given `DateTimeFormatOptions` to the given
255| value.
256-}
257setHourCycle :: HourCycle -> DateTimeFormatOptions -> DateTimeFormatOptions
258setHourCycle style (DateTimeFormatOptions options) = DateTimeFormatOptions options { hourCycle = Just style }
259
260{-------------------------------------------------------------------------------
261| Set the `formatMatcher` field of the given `DateTimeFormatOptions` to the given
262| value.
263-}
264setFormatMatcher :: FormatMatcher -> DateTimeFormatOptions -> DateTimeFormatOptions
265setFormatMatcher style (DateTimeFormatOptions options) = DateTimeFormatOptions options { formatMatcher = Just style }
266
267{-------------------------------------------------------------------------------
268| Set the `weekDay` field of the given `DateTimeFormatOptions` to the given
269| value.
270-}
271setWeekDayFormat :: WeekDayFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
272setWeekDayFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { weekDay = Just style }
273
274{-------------------------------------------------------------------------------
275| Set the `era` field of the given `DateTimeFormatOptions` to the given
276| value.
277-}
278setEraFormat :: EraFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
279setEraFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { era = Just style }
280
281{-------------------------------------------------------------------------------
282| Set the `year` field of the given `DateTimeFormatOptions` to the given
283| value.
284-}
285setYearFormat :: YearFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
286setYearFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { year = Just style }
287
288{-------------------------------------------------------------------------------
289| Set the `month` field of the given `DateTimeFormatOptions` to the given
290| value.
291-}
292setMonthFormat :: MonthFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
293setMonthFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { month = Just style }
294
295{-------------------------------------------------------------------------------
296| Set the `day` field of the given `DateTimeFormatOptions` to the given
297| value.
298-}
299setDayFormat :: DayFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
300setDayFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { day = Just style }
301
302{-------------------------------------------------------------------------------
303| Set the `hour` field of the given `DateTimeFormatOptions` to the given
304| value.
305-}
306setHourFormat :: HourFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
307setHourFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { hour = Just style }
308
309{-------------------------------------------------------------------------------
310| Set the `minute` field of the given `DateTimeFormatOptions` to the given
311| value.
312-}
313setMinuteFormat :: MinuteFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
314setMinuteFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { minute = Just style }
315
316{-------------------------------------------------------------------------------
317| Set the `second` field of the given `DateTimeFormatOptions` to the given
318| value.
319-}
320setSecondFormat :: SecondFormat -> DateTimeFormatOptions -> DateTimeFormatOptions
321setSecondFormat style (DateTimeFormatOptions options) = DateTimeFormatOptions options { second = Just style }
322
323{-------------------------------------------------------------------------------
324| Set the `fractionalSecondDigits` field of the given `DateTimeFormatOptions` to the given
325| value.
326-}
327setFractionalSecondDigits :: FractionalSecondDigits -> DateTimeFormatOptions -> DateTimeFormatOptions
328setFractionalSecondDigits style (DateTimeFormatOptions options) = DateTimeFormatOptions options { fractionalSecondDigits = Just style }
329
330{-------------------------------------------------------------------------------
331| Set the `timeZoneNameStyle` field of the given `DateTimeFormatOptions` to the given
332| value.
333-}
334setTimeZoneNameStyle :: TimeZoneNameStyle -> DateTimeFormatOptions -> DateTimeFormatOptions
335setTimeZoneNameStyle style (DateTimeFormatOptions options) = DateTimeFormatOptions options { timeZoneNameStyle = Just style }
336
337{-------------------------------------------------------------------------------
338| The options of a `DateTimeFormat`.
339|
340| You can set the fields of `DateTimeFormat` using `Monoid`s `append` `<>`, or by
341| chaining calls to the `set...` functions using `#`.
342|
343| Example:
344|
345| ```purescript
346| formatOpts = setDateStyle (DateStyle Long) mempty <> setTimeStyle (TimeStyle MediumT) mempty
347|
348| formatOpts = mempty # setDateStyle (DateStyle Long) # setTimeStyle (TimeStyle MediumT)
349| ```
350|
351| See MDN for more information of the fields:
352| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
353-}
354newtype DateTimeFormatOptions
355 = DateTimeFormatOptions
356 { dateStyle :: Maybe DateStyle
357 , timeStyle :: Maybe TimeStyle
358 , calendar :: Maybe CalendarFormat
359 , dayPeriod :: Maybe DayPeriod
360 , numberingSystem :: Maybe NumberingSystem
361 , localeMatcher :: Maybe LocaleMatcher
362 , timeZone :: Maybe TimeZone
363 , hour12 :: Maybe Hour12
364 , hourCycle :: Maybe HourCycle
365 , formatMatcher :: Maybe FormatMatcher
366 , weekDay :: Maybe WeekDayFormat
367 , era :: Maybe EraFormat
368 , year :: Maybe YearFormat
369 , month :: Maybe MonthFormat
370 , day :: Maybe DayFormat
371 , hour :: Maybe HourFormat
372 , minute :: Maybe MinuteFormat
373 , second :: Maybe SecondFormat
374 , fractionalSecondDigits :: Maybe FractionalSecondDigits
375 , timeZoneNameStyle :: Maybe TimeZoneNameStyle
376 }
377
378derive instance eqDateTimeFormatOptions :: Eq DateTimeFormatOptions
379
380derive instance ordDateTimeFormatOptions :: Ord DateTimeFormatOptions
381
382derive instance genericDateTimeFormatOptions :: Generic DateTimeFormatOptions _
383
384instance decodeJsonDateTimeFormatOptions :: DecodeJson DateTimeFormatOptions where
385 decodeJson = genericDecodeJson
386
387instance encodeJsonDateTimeFormatOptions :: EncodeJson DateTimeFormatOptions where
388 encodeJson = genericEncodeJson
389
390instance showDateTimeFormatOptions :: Show DateTimeFormatOptions where
391 show = genericShow
392
393instance arbitraryDateTimeFormatOptions :: Arbitrary DateTimeFormatOptions where
394 arbitrary = genericArbitrary
395
396instance semiGroupDateTimeFormatOptions :: Semigroup DateTimeFormatOptions where
397 append (DateTimeFormatOptions a) (DateTimeFormatOptions b) =
398 DateTimeFormatOptions
399 { dateStyle: setAOrB a.dateStyle b.dateStyle
400 , timeStyle: setAOrB a.timeStyle b.timeStyle
401 , calendar: setAOrB a.calendar b.calendar
402 , dayPeriod: setAOrB a.dayPeriod b.dayPeriod
403 , numberingSystem: setAOrB a.numberingSystem b.numberingSystem
404 , localeMatcher: setAOrB a.localeMatcher b.localeMatcher
405 , timeZone: setAOrB a.timeZone b.timeZone
406 , hour12: setAOrB a.hour12 b.hour12
407 , hourCycle: setAOrB a.hourCycle b.hourCycle
408 , formatMatcher: setAOrB a.formatMatcher b.formatMatcher
409 , weekDay: setAOrB a.weekDay b.weekDay
410 , era: setAOrB a.era b.era
411 , year: setAOrB a.year b.year
412 , month: setAOrB a.month b.month
413 , day: setAOrB a.day b.day
414 , hour: setAOrB a.hour b.hour
415 , minute: setAOrB a.minute b.minute
416 , second: setAOrB a.second b.second
417 , fractionalSecondDigits: setAOrB a.fractionalSecondDigits b.fractionalSecondDigits
418 , timeZoneNameStyle: setAOrB a.timeZoneNameStyle b.timeZoneNameStyle
419 }
420 where
421 setAOrB :: forall a. Maybe a -> Maybe a -> Maybe a
422 setAOrB c Nothing = c
423
424 setAOrB _ (Just d) = Just d
425
426instance monoidDateTimeFormatOptions :: Monoid DateTimeFormatOptions where
427 mempty = emptyDateTimeFormatOptions
428
429{-------------------------------------------------------------------------------
430| Return the default `DateTimeFormat` object of the current (browser) locale.
431-}
432foreign import defaultDateTimeFormat :: Unit -> Effect DateTimeFormat
433
434{-------------------------------------------------------------------------------
435| Return a `DateTimeFormat` from the given `Locale` and `DateTimeFormatOptions`.
436|
437| This format is to be used as argument to to `format...` functions, like
438| `formatDateTime`.
439-}
440dateTimeFormat :: Array Locale -> DateTimeFormatOptions -> DateTimeFormat
441dateTimeFormat locales options = runFn2 getDateTimeFormatJS locales optionsJS
442 where
443 optionsJS = convertDateTimeOptions options
444
445foreign import getDateTimeFormatJS :: Fn2 (Array Locale) DateTimeFormatOptionsJS DateTimeFormat
446
447{-------------------------------------------------------------------------------
448| Return a string of the current date and time formatted using the given
449| formatter object.
450-}
451foreign import formatDateTimeNow :: DateTimeFormat -> Effect String
452
453{-------------------------------------------------------------------------------
454| Return the current date and time as ISO ISO 8601 string as UTC.
455-}
456foreign import isoDateTimeNow :: Unit -> Effect String
457
458foreign import isoDateTimeJS :: Fn6 Int Int Int Int Int Int String
459
460{-------------------------------------------------------------------------------
461| Return the given date and time as ISO ISO 8601 string as UTC.
462|
463| * `dateTime` - The local date and time to return as ISO ISO 8601 string.
464-}
465isoDateTime :: DateTime -> String
466isoDateTime (DateTime date time) = runFn6 isoDateTimeJS year' month' day' hour' minute' seconds'
467 where
468 year' = fromEnum $ year date
469
470 month' = (fromEnum $ month date) - 1
471
472 day' = fromEnum $ day date
473
474 hour' = fromEnum $ hour time
475
476 minute' = fromEnum $ minute time
477
478 seconds' = fromEnum $ second time
479
480{-------------------------------------------------------------------------------
481| Return the given local date and time as ISO ISO 8601 string as UTC.
482|
483| All bounds are checked, but for dates like the `31st of November` the
484| `1st of December` is returned. `30th February` yields `2nd (or 1st) March`.
485| A date like the `52th of November` yields `Nothing`.
486|
487| * year - The Year
488| * month - The month (starting at 1, January is 1)
489| * day - The day of the month (starting at 1)
490| * hour - The hour
491| * minutes - The minutes
492| * seconds - The seconds
493-}
494isoDateTimeInts ::
495 Int -> Int -> Int -> Int -> Int -> Int -> Maybe String
496isoDateTimeInts yearI monthI dayI hourI minuteI secondsI = do
497 year' <- toEnum yearI
498 month' <- toEnum monthI
499 day' <- toEnum dayI
500 hour' <- toEnum hourI
501 minute' <- toEnum minuteI
502 seconds' <- toEnum secondsI
503 millis' <- toEnum 0
504 let
505 date = canonicalDate year' month' day'
506
507 time = Time hour' minute' seconds' millis'
508 pure $ isoDateTime (DateTime date time)
509
510{-------------------------------------------------------------------------------
511| Return the given date and time as ISO ISO 8601 string as UTC.
512|
513| No bounds are checked, a date like the `52th of November` yields
514| `22nd of December`.
515|
516| * year - The Year
517| * month - The month (starting at 1, January is 1)
518| * day - The day of the month (starting at 1)
519| * hour - The hour
520| * minutes - The minutes
521| * seconds - The seconds
522-}
523isoDateTimeIntsUnsafe ::
524 Int -> Int -> Int -> Int -> Int -> Int -> String
525isoDateTimeIntsUnsafe yearI monthI dayI hourI minuteI secondsI = runFn6 isoDateTimeJS yearI (monthI - 1) dayI hourI minuteI secondsI
526
527foreign import formatDateJS :: Fn4 DateTimeFormat Int Int Int String
528
529{-------------------------------------------------------------------------------
530| Return the given local date formatted by the given formatter.
531|
532| All bounds are checked, but for dates like the `31st of November` the
533| `1st of December` is returned. `30th February` yields `2nd (or 1st) March`.
534| A date like the `52th of November` yields `Nothing`.
535|
536| * formatter - The format to use
537| * date - The local date to format
538-}
539formatDate :: DateTimeFormat -> Date -> String
540formatDate formatter date = runFn4 formatDateJS formatter year' month' day'
541 where
542 year' = fromEnum $ year date
543
544 month' = (fromEnum $ month date) - 1
545
546 day' = fromEnum $ day date
547
548{-------------------------------------------------------------------------------
549| Return the given local date formatted by the given formatter.
550|
551| All bounds are checked, but for dates like the `31st of November` the
552| `1st of December` is returned. `30th February` yields `2nd (or 1st) March`.
553| A date like the `52th of November` yields `Nothing`.
554|
555| * formatter - The format to use
556| * year - The Year
557| * month - The month (starting at 1, January is 1)
558| * day - The day of the month (starting at 1)
559-}
560formatDateInts :: DateTimeFormat -> Int -> Int -> Int -> Maybe String
561formatDateInts formatter yearI monthI dayI = do
562 year' <- toEnum yearI
563 month' <- toEnum monthI
564 day' <- toEnum dayI
565 let
566 date = canonicalDate year' month' day'
567 pure $ formatDate formatter date
568
569{-------------------------------------------------------------------------------
570| Return the given date formatted by the given formatter.
571|
572| No bounds are checked, a date like the `52th of November` yields
573| `22nd of December`.
574|
575| * formatter - The format to use
576| * year - The Year
577| * month - The month (starting at 1, January is 1)
578| * day - The day of the month (starting at 1)
579-}
580formatDateIntsUnsafe :: DateTimeFormat -> Int -> Int -> Int -> String
581formatDateIntsUnsafe formatter yearI monthI dayI = runFn4 formatDateJS formatter yearI (monthI - 1) dayI
582
583foreign import formatDateTimeJS :: Fn7 DateTimeFormat Int Int Int Int Int Int String
584
585{-------------------------------------------------------------------------------
586| Return the given local date and time formatted by the given formatter.
587|
588| All bounds are checked, but for dates like the `31st of November` the
589| `1st of December` is returned. `30th February` yields `2nd (or 1st) March`.
590| A date like the `52th of November` yields `Nothing`.
591|
592| * formatter - The format to use
593| * dateTime - The local date and time to format
594-}
595formatDateTime :: DateTimeFormat -> DateTime -> String
596formatDateTime formatter (DateTime date time) = runFn7 formatDateTimeJS formatter year' month' day' hour' minute' seconds'
597 where
598 year' = fromEnum $ year date
599
600 month' = (fromEnum $ month date) - 1
601
602 day' = fromEnum $ day date
603
604 hour' = fromEnum $ hour time
605
606 minute' = fromEnum $ minute time
607
608 seconds' = fromEnum $ second time
609
610{-------------------------------------------------------------------------------
611| Return the given local date and time formatted by the given formatter.
612|
613| All bounds are checked, but for dates like the `31st of November` the
614| `1st of December` is returned. `30th February` yields `2nd (or 1st) March`.
615| A date like the `52th of November` yields `Nothing`.
616|
617| * formatter - The format to use
618| * year - The Year
619| * month - The month (starting at 1, January is 1)
620| * day - The day of the month (starting at 1)
621| * hour - The hour
622| * minutes - The minutes
623| * seconds - The seconds
624-}
625formatDateTimeInts ::
626 DateTimeFormat -> Int -> Int -> Int -> Int -> Int -> Int -> Maybe String
627formatDateTimeInts formatter yearI monthI dayI hourI minuteI secondsI = do
628 year' <- toEnum yearI
629 month' <- toEnum monthI
630 day' <- toEnum dayI
631 hour' <- toEnum hourI
632 minute' <- toEnum minuteI
633 seconds' <- toEnum secondsI
634 millis' <- toEnum 0
635 let
636 date = canonicalDate year' month' day'
637
638 time = Time hour' minute' seconds' millis'
639 pure $ formatDateTime formatter (DateTime date time)
640
641{-------------------------------------------------------------------------------
642| Return the given date and time formatted by the given formatter.
643|
644| No bounds are checked, a date like the `52th of November` yields
645| `22nd of December`.
646|
647| * formatter - The format to use
648| * year - The Year
649| * month - The month (starting at 1, January is 1)
650| * day - The day of the month (starting at 1)
651| * hour - The hour
652| * minutes - The minutes
653| * seconds - The seconds
654-}
655formatDateTimeIntsUnsafe ::
656 DateTimeFormat -> Int -> Int -> Int -> Int -> Int -> Int -> String
657formatDateTimeIntsUnsafe formatter yearI monthI dayI hourI minuteI secondsI = runFn7 formatDateTimeJS formatter yearI (monthI - 1) dayI hourI minuteI secondsI
658
659{-------------------------------------------------------------------------------
660| The style with which to format the date.
661|
662| One of
663| * Full
664| * Long
665| * Medium
666| * Short
667-}
668data DateStyle
669 = Full
670 | Long
671 | Medium
672 | Short
673
674{-------------------------------------------------------------------------------
675| The possible values of the `dateStyle` field in the Javascript options record.
676-}
677dateStyleJS ::
678 { full :: String
679 , long :: String
680 , medium :: String
681 , short :: String
682 }
683dateStyleJS =
684 { full: "full"
685 , long: "long"
686 , medium: "medium"
687 , short: "short"
688 }
689
690toDateStyleJS :: DateStyle -> String
691toDateStyleJS Full = dateStyleJS.full
692
693toDateStyleJS Long = dateStyleJS.long
694
695toDateStyleJS Medium = dateStyleJS.medium
696
697toDateStyleJS Short = dateStyleJS.short
698
699derive instance eqDateStyle :: Eq DateStyle
700
701derive instance ordDateStyle :: Ord DateStyle
702
703derive instance genericDateStyle :: Generic DateStyle _
704
705instance decodeJsonDateStyle :: DecodeJson DateStyle where
706 decodeJson = genericDecodeJson
707
708instance encodeJsonDateStyle :: EncodeJson DateStyle where
709 encodeJson = genericEncodeJson
710
711instance showDateStyle :: Show DateStyle where
712 show = genericShow
713
714{-------------------------------------------------------------------------------
715 ATTENTION: 4 is the number of values of `DateStyle`.
716-}
717instance arbitraryDateStyle :: Arbitrary DateStyle where
718 arbitrary = map intToDateStyle arbitrary
719 where
720 intToDateStyle :: Int -> DateStyle
721 intToDateStyle n
722 | n >= 0 = case n `mod` 4 of
723 0 -> Full
724 1 -> Long
725 2 -> Medium
726 _ -> Short
727 | otherwise = intToDateStyle (-n)
728
729{-------------------------------------------------------------------------------
730| The style of the time format.
731|
732| One of
733| * FullT
734| * LongT
735| * MediumT
736| * ShortT
737-}
738data TimeStyle
739 = FullT
740 | LongT
741 | MediumT
742 | ShortT
743
744{-------------------------------------------------------------------------------
745| The possible values of the `timeStyle` field in the Javascript options record.
746-}
747timeStyleJS ::
748 { full :: String
749 , long :: String
750 , medium :: String
751 , short :: String
752 }
753timeStyleJS =
754 { full: "full"
755 , long: "long"
756 , medium: "medium"
757 , short: "short"
758 }
759
760toTimeStyleJS :: TimeStyle -> String
761toTimeStyleJS FullT = timeStyleJS.full
762
763toTimeStyleJS LongT = timeStyleJS.long
764
765toTimeStyleJS MediumT = timeStyleJS.medium
766
767toTimeStyleJS ShortT = timeStyleJS.short
768
769derive instance eqTimeStyle :: Eq TimeStyle
770
771derive instance ordTimeStyle :: Ord TimeStyle
772
773derive instance genericTimeStyle :: Generic TimeStyle _
774
775instance decodeJsonTimeStyle :: DecodeJson TimeStyle where
776 decodeJson = genericDecodeJson
777
778instance encodeJsonTimeStyle :: EncodeJson TimeStyle where
779 encodeJson = genericEncodeJson
780
781instance showTimeStyle :: Show TimeStyle where
782 show = genericShow
783
784{-------------------------------------------------------------------------------
785 ATTENTION: 4 is the number of values of `TimeStyle`.
786-}
787instance arbitraryTimeStyle :: Arbitrary TimeStyle where
788 arbitrary = map intToTimeStyle arbitrary
789 where
790 intToTimeStyle :: Int -> TimeStyle
791 intToTimeStyle n
792 | n >= 0 = case n `mod` 4 of
793 0 -> FullT
794 1 -> LongT
795 2 -> MediumT
796 _ -> ShortT
797 | otherwise = intToTimeStyle (-n)
798
799{-------------------------------------------------------------------------------
800| The format of a day period - like `AM` and `PM`.
801|
802| 12 hour format must be enabled for this to have an effect.
803|
804| One of
805| * Narrow
806| * Short
807| * Long
808-}
809data DayPeriod
810 = NarrowDP
811 | ShortDP
812 | LongDP
813
814{-------------------------------------------------------------------------------
815| The format of a day period, values for Javascript FFI.
816-}
817dayPeriodJS ::
818 { long :: String
819 , narrow :: String
820 , short :: String
821 }
822dayPeriodJS = { narrow: "narrow", short: "short", long: "long" }
823
824toDayPeriodJS :: DayPeriod -> String
825toDayPeriodJS NarrowDP = dayPeriodJS.narrow
826
827toDayPeriodJS ShortDP = dayPeriodJS.short
828
829toDayPeriodJS LongDP = dayPeriodJS.long
830
831derive instance eqDayPeriod :: Eq DayPeriod
832
833derive instance ordDayPeriod :: Ord DayPeriod
834
835derive instance genericDayPeriod :: Generic DayPeriod _
836
837instance decodeJsonDayPeriod :: DecodeJson DayPeriod where
838 decodeJson = genericDecodeJson
839
840instance encodeJsonDayPeriod :: EncodeJson DayPeriod where
841 encodeJson = genericEncodeJson
842
843instance showDayPeriod :: Show DayPeriod where
844 show = genericShow
845
846{-------------------------------------------------------------------------------
847 ATTENTION: 3 is the number of values of `DayPeriod`.
848-}
849instance arbitraryDayPeriod :: Arbitrary DayPeriod where
850 arbitrary = map intToDayPeriod arbitrary
851 where
852 intToDayPeriod :: Int -> DayPeriod
853 intToDayPeriod n
854 | n >= 0 = case n `mod` 3 of
855 0 -> NarrowDP
856 1 -> ShortDP
857 _ -> LongDP
858 | otherwise = intToDayPeriod (-n)
859
860{-------------------------------------------------------------------------------
861| The options to match the locale.
862|
863| One of
864| * Lookup
865| * BestFit
866-}
867data LocaleMatcher
868 = Lookup
869 | BestFit
870
871{-------------------------------------------------------------------------------
872| The values of a `LocaleMatcher` for the JS FFI.
873-}
874localeMatcherJS ::
875 { bestFit :: String
876 , lookup :: String
877 }
878localeMatcherJS = { lookup: "lookup", bestFit: "best fit" }
879
880toLocalMatcherJS :: LocaleMatcher -> String
881toLocalMatcherJS Lookup = localeMatcherJS.lookup
882
883toLocalMatcherJS BestFit = localeMatcherJS.bestFit
884
885derive instance eqLocaleMatcher :: Eq LocaleMatcher
886
887derive instance ordLocaleMatcher :: Ord LocaleMatcher
888
889derive instance genericLocaleMatcher :: Generic LocaleMatcher _
890
891instance decodeJsonLocaleMatcher :: DecodeJson LocaleMatcher where
892 decodeJson = genericDecodeJson
893
894instance encodeJsonLocaleMatcher :: EncodeJson LocaleMatcher where
895 encodeJson = genericEncodeJson
896
897instance showLocaleMatcher :: Show LocaleMatcher where
898 show = genericShow
899
900{-------------------------------------------------------------------------------
901 ATTENTION: 2 is the number of values of `LocaleMatcher`.
902-}
903instance arbitraryLocaleMatcher :: Arbitrary LocaleMatcher where
904 arbitrary = map intToLocaleMatcher arbitrary
905 where
906 intToLocaleMatcher :: Int -> LocaleMatcher
907 intToLocaleMatcher n
908 | n >= 0 = case n `mod` 2 of
909 0 -> Lookup
910 _ -> BestFit
911 | otherwise = intToLocaleMatcher (-n)
912
913{-------------------------------------------------------------------------------
914| The timezone of a locale.
915|
916| See https://www.iana.org/time-zones for (long ;) list.
917-}
918newtype TimeZone
919 = TimeZone String
920
921derive newtype instance eqTimeZone :: Eq TimeZone
922
923derive newtype instance ordTimeZone :: Ord TimeZone
924
925derive newtype instance showTimeZone :: Show TimeZone
926
927derive newtype instance decodeJsonTimeZone :: DecodeJson TimeZone
928
929derive newtype instance encodeJsonTimeZone :: EncodeJson TimeZone
930
931derive instance genericTimeZone :: Generic TimeZone _
932
933derive instance newtypeTimeZone :: Newtype TimeZone _
934
935derive newtype instance arbitraryTimeZone :: Arbitrary TimeZone
936
937{-------------------------------------------------------------------------------
938| Convert a `TimeZone` to a `String`.
939|
940| * `obj` - The `TimeZone` to convert to a `String`.
941-}
942timeZoneToString :: TimeZone -> String
943timeZoneToString = unwrap
944
945{-------------------------------------------------------------------------------
946| Convert a String to a `TimeZone`.
947|
948| * `obj` - The `String` to convert to a `TimeZone`.
949-}
950stringToTimeZone :: String -> TimeZone
951stringToTimeZone = wrap
952
953{-------------------------------------------------------------------------------
954| Set the time formt to a 12 hour one.
955|
956| Only works if the number of hours isn't set to 24 elsewhere.
957|
958| One of
959| * Hour12
960| * Hour24
961-}
962data Hour12
963 = Hour12
964 | Hour24
965
966derive instance eqHour12 :: Eq Hour12
967
968derive instance ordHour12 :: Ord Hour12
969
970derive instance genericHour12 :: Generic Hour12 _
971
972instance decodeJsonHour12 :: DecodeJson Hour12 where
973 decodeJson = genericDecodeJson
974
975instance encodeJsonHour12 :: EncodeJson Hour12 where
976 encodeJson = genericEncodeJson
977
978instance showHour12 :: Show Hour12 where
979 show = genericShow
980
981{-------------------------------------------------------------------------------
982 ATTENTION: 2 is the number of values of `Hour12`.
983-}
984instance arbitraryHour12 :: Arbitrary Hour12 where
985 arbitrary = map intToHour12 arbitrary
986 where
987 intToHour12 :: Int -> Hour12
988 intToHour12 n
989 | n >= 0 = case n `mod` 2 of
990 0 -> Hour12
991 _ -> Hour24
992 | otherwise = intToHour12 (-n)
993
994hour12ToBoolean :: Hour12 -> Boolean
995hour12ToBoolean Hour12 = true
996
997hour12ToBoolean Hour24 = false
998
999{-------------------------------------------------------------------------------
1000| The number of hours in a clock cycle of a day.
1001|
1002| One of
1003| * H11
1004| * H12
1005| * H23
1006| * H24
1007-}
1008data HourCycle
1009 = H11
1010 | H12
1011 | H23
1012 | H24
1013
1014{-------------------------------------------------------------------------------
1015| The values of `HourCycle` fo teh JS FFI.
1016-}
1017hourCycleJS ::
1018 { h11 :: String
1019 , h12 :: String
1020 , h23 :: String
1021 , h24 :: String
1022 }
1023hourCycleJS = { h11: "h11", h12: "h12", h23: "h23", h24: "h24" }
1024
1025toHourCycleJS :: HourCycle -> String
1026toHourCycleJS H11 = hourCycleJS.h11
1027
1028toHourCycleJS H12 = hourCycleJS.h12
1029
1030toHourCycleJS H23 = hourCycleJS.h23
1031
1032toHourCycleJS H24 = hourCycleJS.h24
1033
1034derive instance eqHourCycle :: Eq HourCycle
1035
1036derive instance ordHourCycle :: Ord HourCycle
1037
1038derive instance genericHourCycle :: Generic HourCycle _
1039
1040instance decodeJsonHourCycle :: DecodeJson HourCycle where
1041 decodeJson = genericDecodeJson
1042
1043instance encodeJsonHourCycle :: EncodeJson HourCycle where
1044 encodeJson = genericEncodeJson
1045
1046instance showHourCycle :: Show HourCycle where
1047 show = genericShow
1048
1049{-------------------------------------------------------------------------------
1050 ATTENTION: 4 is the number of values of `HourCycle`.
1051-}
1052instance arbitraryHourCycle :: Arbitrary HourCycle where
1053 arbitrary = map intToHourCycle arbitrary
1054 where
1055 intToHourCycle :: Int -> HourCycle
1056 intToHourCycle n
1057 | n >= 0 = case n `mod` 4 of
1058 0 -> H11
1059 1 -> H12
1060 2 -> H23
1061 _ -> H24
1062 | otherwise = intToHourCycle (-n)
1063
1064{-------------------------------------------------------------------------------
1065| The possible values of a format matcher.
1066|
1067| One of
1068| * BasicFM
1069| * BestFitFM
1070-}
1071data FormatMatcher
1072 = BasicFM
1073 | BestFitFM
1074
1075{-------------------------------------------------------------------------------
1076| The values for the formatMatcher in the DateTimeFormat options.
1077-}
1078formatMatcherJS ::
1079 { basic :: String
1080 , bestFit :: String
1081 }
1082formatMatcherJS = { basic: "basic", bestFit: "best fit" }
1083
1084toFormatMatcherJS :: FormatMatcher -> String
1085toFormatMatcherJS BasicFM = formatMatcherJS.basic
1086
1087toFormatMatcherJS BestFitFM = formatMatcherJS.bestFit
1088
1089derive instance eqFormatMatcher :: Eq FormatMatcher
1090
1091derive instance ordFormatMatcher :: Ord FormatMatcher
1092
1093derive instance genericFormatMatcher :: Generic FormatMatcher _
1094
1095instance decodeJsonFormatMatcher :: DecodeJson FormatMatcher where
1096 decodeJson = genericDecodeJson
1097
1098instance encodeJsonFormatMatcher :: EncodeJson FormatMatcher where
1099 encodeJson = genericEncodeJson
1100
1101instance showFormatMatcher :: Show FormatMatcher where
1102 show = genericShow
1103
1104{-------------------------------------------------------------------------------
1105 ATTENTION: 2 is the number of values of `FormatMatcher`.
1106-}
1107instance arbitraryFormatMatcher :: Arbitrary FormatMatcher where
1108 arbitrary = map intToFormatMatcher arbitrary
1109 where
1110 intToFormatMatcher :: Int -> FormatMatcher
1111 intToFormatMatcher n
1112 | n >= 0 = case n `mod` 2 of
1113 0 -> BasicFM
1114 _ -> BestFitFM
1115 | otherwise = intToFormatMatcher (-n)
1116
1117{-------------------------------------------------------------------------------
1118| The possible values of a `WeekDayFormat`
1119|
1120| One of
1121| * LongWD
1122| * ShortWD
1123| * NarrowWD
1124-}
1125data WeekDayFormat
1126 = LongWD
1127 | ShortWD
1128 | NarrowWD
1129
1130{-------------------------------------------------------------------------------
1131| JS values of `WeekDayFormat`.
1132-}
1133weekDayFormatJS ::
1134 { long :: String
1135 , narrow :: String
1136 , short :: String
1137 }
1138weekDayFormatJS = { long: "long", short: "short", narrow: "narrow" }
1139
1140toWeekDayFormatJS :: WeekDayFormat -> String
1141toWeekDayFormatJS LongWD = weekDayFormatJS.long
1142
1143toWeekDayFormatJS ShortWD = weekDayFormatJS.short
1144
1145toWeekDayFormatJS NarrowWD = weekDayFormatJS.narrow
1146
1147derive instance eqWeekDayFormat :: Eq WeekDayFormat
1148
1149derive instance ordWeekDayFormat :: Ord WeekDayFormat
1150
1151derive instance genericWeekDayFormat :: Generic WeekDayFormat _
1152
1153instance decodeJsonWeekDayFormat :: DecodeJson WeekDayFormat where
1154 decodeJson = genericDecodeJson
1155
1156instance encodeJsonWeekDayFormat :: EncodeJson WeekDayFormat where
1157 encodeJson = genericEncodeJson
1158
1159instance showWeekDayFormat :: Show WeekDayFormat where
1160 show = genericShow
1161
1162{-------------------------------------------------------------------------------
1163 ATTENTION: 3 is the number of values of `WeekDayFormat`.
1164-}
1165instance arbitraryWeekDayFormat :: Arbitrary WeekDayFormat where
1166 arbitrary = map intToWeekDayFormat arbitrary
1167 where
1168 intToWeekDayFormat :: Int -> WeekDayFormat
1169 intToWeekDayFormat n
1170 | n >= 0 = case n `mod` 3 of
1171 0 -> LongWD
1172 1 -> ShortWD
1173 _ -> NarrowWD
1174 | otherwise = intToWeekDayFormat (-n)
1175
1176{-------------------------------------------------------------------------------
1177| The format for the era of a date
1178|
1179| One of
1180| * LongE
1181| * ShortE
1182| * NarrowE
1183-}
1184data EraFormat
1185 = LongE
1186 | ShortE
1187 | NarrowE
1188
1189{-------------------------------------------------------------------------------
1190| Values of the era format for JS FFI.
1191-}
1192eraFormatJS ::
1193 { long :: String
1194 , narrow :: String
1195 , short :: String
1196 }
1197eraFormatJS = { long: "long", short: "short", narrow: "narrow" }
1198
1199toEraFormatJS :: EraFormat -> String
1200toEraFormatJS LongE = eraFormatJS.long
1201
1202toEraFormatJS ShortE = eraFormatJS.short
1203
1204toEraFormatJS NarrowE = eraFormatJS.narrow
1205
1206derive instance eqEraFormat :: Eq EraFormat
1207
1208derive instance ordEraFormat :: Ord EraFormat
1209
1210derive instance genericEraFormat :: Generic EraFormat _
1211
1212instance decodeJsonEraFormat :: DecodeJson EraFormat where
1213 decodeJson = genericDecodeJson
1214
1215instance encodeJsonEraFormat :: EncodeJson EraFormat where
1216 encodeJson = genericEncodeJson
1217
1218instance showEraFormat :: Show EraFormat where
1219 show = genericShow
1220
1221{-------------------------------------------------------------------------------
1222 ATTENTION: 3 is the number of values of `EraFormat`.
1223-}
1224instance arbitraryEraFormat :: Arbitrary EraFormat where
1225 arbitrary = map intToEraFormat arbitrary
1226 where
1227 intToEraFormat :: Int -> EraFormat
1228 intToEraFormat n
1229 | n >= 0 = case n `mod` 3 of
1230 0 -> LongE
1231 1 -> ShortE
1232 _ -> NarrowE
1233 | otherwise = intToEraFormat (-n)
1234
1235{-------------------------------------------------------------------------------
1236| The format of a year in a date.
1237|
1238| One of
1239| * NumericY
1240| * TwoDigitY
1241-}
1242data YearFormat
1243 = NumericY
1244 | TwoDigitY
1245
1246{-------------------------------------------------------------------------------
1247| The format of a year, JS FFI version.
1248-}
1249yearFormatJS ::
1250 { numeric :: String
1251 , twoDigit :: String
1252 }
1253yearFormatJS = { numeric: "numeric", twoDigit: "2-digit" }
1254
1255toYearFormatJS :: YearFormat -> String
1256toYearFormatJS NumericY = yearFormatJS.numeric
1257
1258toYearFormatJS TwoDigitY = yearFormatJS.twoDigit
1259
1260derive instance eqYearFormat :: Eq YearFormat
1261
1262derive instance ordYearFormat :: Ord YearFormat
1263
1264derive instance genericYearFormat :: Generic YearFormat _
1265
1266instance decodeJsonYearFormat :: DecodeJson YearFormat where
1267 decodeJson = genericDecodeJson
1268
1269instance encodeJsonYearFormat :: EncodeJson YearFormat where
1270 encodeJson = genericEncodeJson
1271
1272instance showYearFormat :: Show YearFormat where
1273 show = genericShow
1274
1275{-------------------------------------------------------------------------------
1276 ATTENTION: 2 is the number of values of `YearFormat`.
1277-}
1278instance arbitraryYearFormat :: Arbitrary YearFormat where
1279 arbitrary = map intToYearFormat arbitrary
1280 where
1281 intToYearFormat :: Int -> YearFormat
1282 intToYearFormat n
1283 | n >= 0 = case n `mod` 2 of
1284 0 -> NumericY
1285 _ -> TwoDigitY
1286 | otherwise = intToYearFormat (-n)
1287
1288{-------------------------------------------------------------------------------
1289| The format of a month.
1290|
1291| One of
1292| * NumericM
1293| * TwoDigitM
1294| * LongM
1295| * ShortM
1296| * NarrowM
1297-}
1298data MonthFormat
1299 = NumericM
1300 | TwoDigitM
1301 | LongM
1302 | ShortM
1303 | NarrowM
1304
1305{-------------------------------------------------------------------------------
1306| Month format in the JS version for FFI.
1307-}
1308monthFormatJS ∷
1309 { long ∷ String
1310 , narrow ∷ String
1311 , numeric ∷ String
1312 , short ∷ String
1313 , twoDigit ∷ String
1314 }
1315monthFormatJS =
1316 { numeric: "numeric"
1317 , twoDigit: "2-digit"
1318 , long: "long"
1319 , short: "short"
1320 , narrow: "narrow"
1321 }
1322
1323toMonthFormatJS :: MonthFormat -> String
1324toMonthFormatJS NumericM = monthFormatJS.numeric
1325
1326toMonthFormatJS TwoDigitM = monthFormatJS.twoDigit
1327
1328toMonthFormatJS LongM = monthFormatJS.long
1329
1330toMonthFormatJS ShortM = monthFormatJS.short
1331
1332toMonthFormatJS NarrowM = monthFormatJS.narrow
1333
1334derive instance eqMonthFormat :: Eq MonthFormat
1335
1336derive instance ordMonthFormat :: Ord MonthFormat
1337
1338derive instance genericMonthFormat :: Generic MonthFormat _
1339
1340instance decodeJsonMonthFormat :: DecodeJson MonthFormat where
1341 decodeJson = genericDecodeJson
1342
1343instance encodeJsonMonthFormat :: EncodeJson MonthFormat where
1344 encodeJson = genericEncodeJson
1345
1346instance showMonthFormat :: Show MonthFormat where
1347 show = genericShow
1348
1349{-------------------------------------------------------------------------------
1350 ATTENTION: 5 is the number of values of `MonthFormat`.
1351-}
1352instance arbitraryMonthFormat :: Arbitrary MonthFormat where
1353 arbitrary = map intToMonthFormat arbitrary
1354 where
1355 intToMonthFormat :: Int -> MonthFormat
1356 intToMonthFormat n
1357 | n >= 0 = case n `mod` 5 of
1358 0 -> NumericM
1359 1 -> TwoDigitM
1360 2 -> LongM
1361 3 -> ShortM
1362 _ -> NarrowM
1363 | otherwise = intToMonthFormat (-n)
1364
1365{-------------------------------------------------------------------------------
1366| The format of a day
1367|
1368| One of
1369| * NumericD
1370| * TwoDigitD
1371-}
1372data DayFormat
1373 = NumericD
1374 | TwoDigitD
1375
1376{-------------------------------------------------------------------------------
1377| The values of a day format for the JS FFI.
1378-}
1379dayFormatJS ::
1380 { numeric :: String
1381 , twoDigit :: String
1382 }
1383dayFormatJS = { numeric: "numeric", twoDigit: "2-digit" }
1384
1385toDayFormatJS :: DayFormat -> String
1386toDayFormatJS NumericD = dayFormatJS.numeric
1387
1388toDayFormatJS TwoDigitD = dayFormatJS.twoDigit
1389
1390derive instance eqDayFormat :: Eq DayFormat
1391
1392derive instance ordDayFormat :: Ord DayFormat
1393
1394derive instance genericDayFormat :: Generic DayFormat _
1395
1396instance decodeJsonDayFormat :: DecodeJson DayFormat where
1397 decodeJson = genericDecodeJson
1398
1399instance encodeJsonDayFormat :: EncodeJson DayFormat where
1400 encodeJson = genericEncodeJson
1401
1402instance showDayFormat :: Show DayFormat where
1403 show = genericShow
1404
1405{-------------------------------------------------------------------------------
1406 ATTENTION: 2 is the number of values of `DayFormat`.
1407-}
1408instance arbitraryDayFormat :: Arbitrary DayFormat where
1409 arbitrary = map intToDayFormat arbitrary
1410 where
1411 intToDayFormat :: Int -> DayFormat
1412 intToDayFormat n
1413 | n >= 0 = case n `mod` 2 of
1414 0 -> NumericD
1415 _ -> TwoDigitD
1416 | otherwise = intToDayFormat (-n)
1417
1418{-------------------------------------------------------------------------------
1419| The format fo an hour.
1420|
1421| One of
1422| * NumericH
1423| * TwoDigitH
1424-}
1425data HourFormat
1426 = NumericH
1427 | TwoDigitH
1428
1429{-------------------------------------------------------------------------------
1430| The format of an hour, for JS FFI.
1431-}
1432hourFormatJS ::
1433 { numeric :: String
1434 , twoDigit :: String
1435 }
1436hourFormatJS = { numeric: "numeric", twoDigit: "2-digit" }
1437
1438toHourFormatJS :: HourFormat -> String
1439toHourFormatJS NumericH = hourFormatJS.numeric
1440
1441toHourFormatJS TwoDigitH = hourFormatJS.twoDigit
1442
1443derive instance eqHourFormat :: Eq HourFormat
1444
1445derive instance ordHourFormat :: Ord HourFormat
1446
1447derive instance genericHourFormat :: Generic HourFormat _
1448
1449instance decodeJsonHourFormat :: DecodeJson HourFormat where
1450 decodeJson = genericDecodeJson
1451
1452instance encodeJsonHourFormat :: EncodeJson HourFormat where
1453 encodeJson = genericEncodeJson
1454
1455instance showHourFormat :: Show HourFormat where
1456 show = genericShow
1457
1458{-------------------------------------------------------------------------------
1459 ATTENTION: 2 is the number of values of `HourFormat`.
1460-}
1461instance arbitraryHourFormat :: Arbitrary HourFormat where
1462 arbitrary = map intToHourFormat arbitrary
1463 where
1464 intToHourFormat :: Int -> HourFormat
1465 intToHourFormat n
1466 | n >= 0 = case n `mod` 2 of
1467 0 -> NumericH
1468 _ -> TwoDigitH
1469 | otherwise = intToHourFormat (-n)
1470
1471{-------------------------------------------------------------------------------
1472| The format of a minute.
1473|
1474| One of
1475| * NumericMi
1476| * TwoDigitMi
1477-}
1478data MinuteFormat
1479 = NumericMi
1480 | TwoDigitMi
1481
1482{-------------------------------------------------------------------------------
1483| The minute format, JS interop.
1484-}
1485minuteFormatJS ::
1486 { numeric :: String
1487 , twoDigit :: String
1488 }
1489minuteFormatJS = { numeric: "numeric", twoDigit: "2-digit" }
1490
1491toMinuteFormatJS :: MinuteFormat -> String
1492toMinuteFormatJS NumericMi = minuteFormatJS.numeric
1493
1494toMinuteFormatJS TwoDigitMi = minuteFormatJS.twoDigit
1495
1496derive instance eqMinuteFormat :: Eq MinuteFormat
1497
1498derive instance ordMinuteFormat :: Ord MinuteFormat
1499
1500derive instance genericMinuteFormat :: Generic MinuteFormat _
1501
1502instance decodeJsonMinuteFormat :: DecodeJson MinuteFormat where
1503 decodeJson = genericDecodeJson
1504
1505instance encodeJsonMinuteFormat :: EncodeJson MinuteFormat where
1506 encodeJson = genericEncodeJson
1507
1508instance showMinuteFormat :: Show MinuteFormat where
1509 show = genericShow
1510
1511{-------------------------------------------------------------------------------
1512 ATTENTION: 2 is the number of values of `MinuteFormat`.
1513-}
1514instance arbitraryMinuteFormat :: Arbitrary MinuteFormat where
1515 arbitrary = map intToMinuteFormat arbitrary
1516 where
1517 intToMinuteFormat :: Int -> MinuteFormat
1518 intToMinuteFormat n
1519 | n >= 0 = case n `mod` 2 of
1520 0 -> NumericMi
1521 _ -> TwoDigitMi
1522 | otherwise = intToMinuteFormat (-n)
1523
1524{-------------------------------------------------------------------------------
1525| The format of a second.
1526|
1527| One of
1528| * NumericS
1529| * TwoDigitS
1530-}
1531data SecondFormat
1532 = NumericS
1533 | TwoDigitS
1534
1535{-------------------------------------------------------------------------------
1536| Format of a second, JS FFI.
1537-}
1538secondFormatJS ::
1539 { numeric :: String
1540 , twoDigit :: String
1541 }
1542secondFormatJS = { numeric: "numeric", twoDigit: "2-digit" }
1543
1544toSecondFormatJS :: SecondFormat -> String
1545toSecondFormatJS NumericS = secondFormatJS.numeric
1546
1547toSecondFormatJS TwoDigitS = secondFormatJS.twoDigit
1548
1549derive instance eqSecondFormat :: Eq SecondFormat
1550
1551derive instance ordSecondFormat :: Ord SecondFormat
1552
1553derive instance genericSecondFormat :: Generic SecondFormat _
1554
1555instance decodeJsonSecondFormat :: DecodeJson SecondFormat where
1556 decodeJson = genericDecodeJson
1557
1558instance encodeJsonSecondFormat :: EncodeJson SecondFormat where
1559 encodeJson = genericEncodeJson
1560
1561instance showSecondFormat :: Show SecondFormat where
1562 show = genericShow
1563
1564{-------------------------------------------------------------------------------
1565 ATTENTION: 2 is the number of values of `SecondFormat`.
1566-}
1567instance arbitrarySecondFormat :: Arbitrary SecondFormat where
1568 arbitrary = map intToSecondFormat arbitrary
1569 where
1570 intToSecondFormat :: Int -> SecondFormat
1571 intToSecondFormat n
1572 | n >= 0 = case n `mod` 2 of
1573 0 -> NumericS
1574 _ -> TwoDigitS
1575 | otherwise = intToSecondFormat (-n)
1576
1577{-------------------------------------------------------------------------------
1578| The number of digits in the fractional part of the second.
1579|
1580| One of
1581| * Digits0
1582| * Digits1
1583| * Digits2
1584| * Digits3
1585-}
1586data FractionalSecondDigits
1587 = Digits0
1588 | Digits1
1589 | Digits2
1590 | Digits3
1591
1592{-------------------------------------------------------------------------------
1593| The number of digits in the fractional part of a second, JS interop.
1594-}
1595fractionalSecondDigitsJS ::
1596 { digits0 :: Int
1597 , digits1 :: Int
1598 , digits2 :: Int
1599 , digits3 :: Int
1600 }
1601fractionalSecondDigitsJS =
1602 { digits0: 0
1603 , digits1: 1
1604 , digits2: 2
1605 , digits3: 3
1606 }
1607
1608toFractionalDigitsJS :: FractionalSecondDigits -> Int
1609toFractionalDigitsJS Digits0 = fractionalSecondDigitsJS.digits0
1610
1611toFractionalDigitsJS Digits1 = fractionalSecondDigitsJS.digits1
1612
1613toFractionalDigitsJS Digits2 = fractionalSecondDigitsJS.digits2
1614
1615toFractionalDigitsJS Digits3 = fractionalSecondDigitsJS.digits3
1616
1617derive instance eqFractionalSecondDigits :: Eq FractionalSecondDigits
1618
1619derive instance ordFractionalSecondDigits :: Ord FractionalSecondDigits
1620
1621derive instance genericFractionalSecondDigits :: Generic FractionalSecondDigits _
1622
1623instance decodeJsonFractionalSecondDigits :: DecodeJson FractionalSecondDigits where
1624 decodeJson = genericDecodeJson
1625
1626instance encodeJsonFractionalSecondDigits :: EncodeJson FractionalSecondDigits where
1627 encodeJson = genericEncodeJson
1628
1629instance showFractionalSecondDigits :: Show FractionalSecondDigits where
1630 show = genericShow
1631
1632{-------------------------------------------------------------------------------
1633 ATTENTION: 4 is the number of values of `FractionalSecondDigits`.
1634-}
1635instance arbitraryFractionalSecondDigits :: Arbitrary FractionalSecondDigits where
1636 arbitrary = map intToFractionalSecondDigits arbitrary
1637 where
1638 intToFractionalSecondDigits :: Int -> FractionalSecondDigits
1639 intToFractionalSecondDigits n
1640 | n >= 0 = case n `mod` 4 of
1641 0 -> Digits0
1642 0 -> Digits1
1643 0 -> Digits2
1644 _ -> Digits3
1645 | otherwise = intToFractionalSecondDigits (-n)
1646
1647{-------------------------------------------------------------------------------
1648| The style of the time zone name.
1649|
1650| One of
1651| * Short
1652| * Long
1653| * ShortOffset
1654| * LongOffset
1655| * ShortGeneric
1656| * LongGeneric
1657-}
1658data TimeZoneNameStyle
1659 = ShortTZ
1660 | LongTZ
1661 | ShortOffsetTZ
1662 | LongOffsetTZ
1663 | ShortGenericTZ
1664 | LongGenericTZ
1665
1666{-------------------------------------------------------------------------------
1667| The possible values of the `timeZoneName` field in the Javascript options
1668| record.
1669-}
1670timeZoneNameStyleJS ::
1671 { long :: String
1672 , longGeneric :: String
1673 , longOffset :: String
1674 , short :: String
1675 , shortGeneric :: String
1676 , shortOffset :: String
1677 }
1678timeZoneNameStyleJS =
1679 { short: "short"
1680 , long: "long"
1681 , shortOffset: "shortOffset"
1682 , longOffset: "longOffset"
1683 , shortGeneric: "shortGeneric"
1684 , longGeneric: "longGeneric"
1685 }
1686
1687toTimeZoneNameStyleJS :: TimeZoneNameStyle -> String
1688toTimeZoneNameStyleJS ShortTZ = timeZoneNameStyleJS.short
1689
1690toTimeZoneNameStyleJS LongTZ = timeZoneNameStyleJS.long
1691
1692toTimeZoneNameStyleJS ShortOffsetTZ = timeZoneNameStyleJS.shortOffset
1693
1694toTimeZoneNameStyleJS LongOffsetTZ = timeZoneNameStyleJS.longOffset
1695
1696toTimeZoneNameStyleJS ShortGenericTZ = timeZoneNameStyleJS.shortGeneric
1697
1698toTimeZoneNameStyleJS LongGenericTZ = timeZoneNameStyleJS.longGeneric
1699
1700derive instance eqTimeZoneNameStyle :: Eq TimeZoneNameStyle
1701
1702derive instance ordTimeZoneNameStyle :: Ord TimeZoneNameStyle
1703
1704derive instance genericTimeZoneNameStyle :: Generic TimeZoneNameStyle _
1705
1706instance decodeJsonTimeZoneNameStyle :: DecodeJson TimeZoneNameStyle where
1707 decodeJson = genericDecodeJson
1708
1709instance encodeJsonTimeZoneNameStyle :: EncodeJson TimeZoneNameStyle where
1710 encodeJson = genericEncodeJson
1711
1712instance showTimeZoneNameStyle :: Show TimeZoneNameStyle where
1713 show = genericShow
1714
1715{-------------------------------------------------------------------------------
1716 ATTENTION: 6 is the number of values of `TimeZoneNameStyle`.
1717-}
1718instance arbitraryTimeZoneNameStyle :: Arbitrary TimeZoneNameStyle where
1719 arbitrary = map intToTimeZoneNameStyle arbitrary
1720 where
1721 intToTimeZoneNameStyle :: Int -> TimeZoneNameStyle
1722 intToTimeZoneNameStyle n
1723 | n >= 0 = case n `mod` 6 of
1724 0 -> ShortTZ
1725 1 -> LongTZ
1726 2 -> ShortOffsetTZ
1727 3 -> LongOffsetTZ
1728 4 -> ShortGenericTZ
1729 _ -> LongGenericTZ
1730 | otherwise = intToTimeZoneNameStyle (-n)
1731
1732{-------------------------------------------------------------------------------
1733| The JS version of `DateTimeFormatOptions`, the record which is used for JS
1734| FFI.
1735-}
1736type DateTimeFormatOptionsJS
1737 = { dateStyle :: UndefinedOr String
1738 , timeStyle :: UndefinedOr String
1739 , calendar :: UndefinedOr String
1740 , dayPeriod :: UndefinedOr String
1741 , numberingSystem :: UndefinedOr String
1742 , localeMatcher :: UndefinedOr String
1743 , timeZone :: UndefinedOr String
1744 , hour12 :: UndefinedOr Boolean
1745 , hourCycle :: UndefinedOr String
1746 , formatMatcher :: UndefinedOr String
1747 , weekDay :: UndefinedOr String
1748 , era :: UndefinedOr String
1749 , year :: UndefinedOr String
1750 , month :: UndefinedOr String
1751 , day :: UndefinedOr String
1752 , hour :: UndefinedOr String
1753 , minute :: UndefinedOr String
1754 , second :: UndefinedOr String
1755 , fractionalSecondDigits :: UndefinedOr Int
1756 , timeZoneNameStyle :: UndefinedOr String
1757 }
1758
1759{-------------------------------------------------------------------------------
1760| Helper function: convert a `DateTimeFormatOptions` object to a
1761| `DateTimeFormatOptionsJS` object.
1762-}
1763convertDateTimeOptions :: DateTimeFormatOptions -> DateTimeFormatOptionsJS
1764convertDateTimeOptions (DateTimeFormatOptions options) =
1765 setDTFOJSFromMaybe setDateStyleJS options.dateStyle (cast {})
1766 # setDTFOJSFromMaybe setTimeStyleJS options.timeStyle
1767 # setDTFOJSFromMaybe setCalendarFormatJS options.calendar
1768 # setDTFOJSFromMaybe setDayPeriodStyleJS options.dayPeriod
1769 # setDTFOJSFromMaybe setNumberingSystemJS options.numberingSystem
1770 # setDTFOJSFromMaybe setLocaleMatcherJS options.localeMatcher
1771 # setDTFOJSFromMaybe setTimeZoneJS options.timeZone
1772 # setDTFOJSFromMaybe setHour12JS options.hour12
1773 # setDTFOJSFromMaybe setHourCycleJS options.hourCycle
1774 # setDTFOJSFromMaybe setFormatMatcherJS options.formatMatcher
1775 # setDTFOJSFromMaybe setWeekDayFormatJS options.weekDay
1776 # setDTFOJSFromMaybe setEraFormatJS options.era
1777 # setDTFOJSFromMaybe setYearFormatJS options.year
1778 # setDTFOJSFromMaybe setMonthFormatJS options.month
1779 # setDTFOJSFromMaybe setDayFormatJS options.day
1780 # setDTFOJSFromMaybe setHourFormatJS options.hour
1781 # setDTFOJSFromMaybe setMinuteFormatJS options.minute
1782 # setDTFOJSFromMaybe setSecondFormatJS options.second
1783 # setDTFOJSFromMaybe setFractionalSecondDigitsJS options.fractionalSecondDigits
1784 # setDTFOJSFromMaybe setTimeZoneNameStyleJS options.timeZoneNameStyle
1785
1786setDTFOJSFromMaybe ::
1787 forall a.
1788 (DateTimeFormatOptionsJS -> a -> DateTimeFormatOptionsJS) ->
1789 Maybe a ->
1790 DateTimeFormatOptionsJS ->
1791 DateTimeFormatOptionsJS
1792setDTFOJSFromMaybe f (Just x) fmt = f fmt x
1793
1794setDTFOJSFromMaybe _ Nothing fmt = fmt
1795
1796setDateStyleJS ::
1797 DateTimeFormatOptionsJS ->
1798 DateStyle -> DateTimeFormatOptionsJS
1799setDateStyleJS fmt style = cast fmt { dateStyle = toDateStyleJS style }
1800
1801setTimeStyleJS ::
1802 DateTimeFormatOptionsJS ->
1803 TimeStyle -> DateTimeFormatOptionsJS
1804setTimeStyleJS fmt style = cast fmt { timeStyle = toTimeStyleJS style }
1805
1806setCalendarFormatJS ::
1807 DateTimeFormatOptionsJS ->
1808 CalendarFormat -> DateTimeFormatOptionsJS
1809setCalendarFormatJS fmt calFmt = cast fmt { calendar = calendarFormatToString calFmt }
1810
1811setDayPeriodStyleJS ::
1812 DateTimeFormatOptionsJS ->
1813 DayPeriod -> DateTimeFormatOptionsJS
1814setDayPeriodStyleJS fmt style = cast fmt { dayPeriod = toDayPeriodJS style }
1815
1816setNumberingSystemJS ::
1817 DateTimeFormatOptionsJS ->
1818 NumberingSystem -> DateTimeFormatOptionsJS
1819setNumberingSystemJS fmt numSys = cast fmt { numberingSystem = numberingSystemToString numSys }
1820
1821setLocaleMatcherJS ::
1822 DateTimeFormatOptionsJS ->
1823 LocaleMatcher -> DateTimeFormatOptionsJS
1824setLocaleMatcherJS fmt matcher = cast fmt { localeMatcher = toLocalMatcherJS matcher }
1825
1826setTimeZoneJS ::
1827 DateTimeFormatOptionsJS ->
1828 TimeZone -> DateTimeFormatOptionsJS
1829setTimeZoneJS fmt timezone = cast fmt { timeZone = timeZoneToString timezone }
1830
1831setHour12JS ::
1832 DateTimeFormatOptionsJS ->
1833 Hour12 -> DateTimeFormatOptionsJS
1834setHour12JS fmt hour12 = cast fmt { hour12 = hour12ToBoolean hour12 }
1835
1836setHourCycleJS ::
1837 DateTimeFormatOptionsJS ->
1838 HourCycle -> DateTimeFormatOptionsJS
1839setHourCycleJS fmt style = cast fmt { hourCycle = toHourCycleJS style }
1840
1841setFormatMatcherJS ::
1842 DateTimeFormatOptionsJS ->
1843 FormatMatcher -> DateTimeFormatOptionsJS
1844setFormatMatcherJS fmt style = cast fmt { formatMatcher = toFormatMatcherJS style }
1845
1846setWeekDayFormatJS ::
1847 DateTimeFormatOptionsJS ->
1848 WeekDayFormat -> DateTimeFormatOptionsJS
1849setWeekDayFormatJS fmt style = cast fmt { weekDay = toWeekDayFormatJS style }
1850
1851setEraFormatJS ::
1852 DateTimeFormatOptionsJS ->
1853 EraFormat -> DateTimeFormatOptionsJS
1854setEraFormatJS fmt style = cast fmt { era = toEraFormatJS style }
1855
1856setYearFormatJS ::
1857 DateTimeFormatOptionsJS ->
1858 YearFormat -> DateTimeFormatOptionsJS
1859setYearFormatJS fmt style = cast fmt { year = toYearFormatJS style }
1860
1861setMonthFormatJS ::
1862 DateTimeFormatOptionsJS ->
1863 MonthFormat -> DateTimeFormatOptionsJS
1864setMonthFormatJS fmt style = cast fmt { month = toMonthFormatJS style }
1865
1866setDayFormatJS ::
1867 DateTimeFormatOptionsJS ->
1868 DayFormat -> DateTimeFormatOptionsJS
1869setDayFormatJS fmt style = cast fmt { day = toDayFormatJS style }
1870
1871setHourFormatJS ::
1872 DateTimeFormatOptionsJS ->
1873 HourFormat -> DateTimeFormatOptionsJS
1874setHourFormatJS fmt style = cast fmt { hour = toHourFormatJS style }
1875
1876setMinuteFormatJS ::
1877 DateTimeFormatOptionsJS ->
1878 MinuteFormat -> DateTimeFormatOptionsJS
1879setMinuteFormatJS fmt style = cast fmt { minute = toMinuteFormatJS style }
1880
1881setSecondFormatJS ::
1882 DateTimeFormatOptionsJS ->
1883 SecondFormat -> DateTimeFormatOptionsJS
1884setSecondFormatJS fmt style = cast fmt { second = toSecondFormatJS style }
1885
1886setFractionalSecondDigitsJS ::
1887 DateTimeFormatOptionsJS ->
1888 FractionalSecondDigits -> DateTimeFormatOptionsJS
1889setFractionalSecondDigitsJS fmt style = cast fmt { fractionalSecondDigits = toFractionalDigitsJS style }
1890
1891setTimeZoneNameStyleJS ::
1892 DateTimeFormatOptionsJS ->
1893 TimeZoneNameStyle -> DateTimeFormatOptionsJS
1894setTimeZoneNameStyleJS fmt style = cast fmt { timeZoneNameStyle = toTimeZoneNameStyleJS style }
1895
1896{-------------------------------------------------------------------------------
1897| Regex matching a `String` not containing only whitespace or being empty.
1898-}
1899notOnlyWhitespaceRegex :: Regex
1900notOnlyWhitespaceRegex = unsafeRegex "\\S+" unicode