Purescript library to handle the formatting of dates and times.
at main 1900 lines 60 kB view raw
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