git.iko.soy/purescript-intl
on master help sign in
DateTimeFormat interface
commit8edc50fac9b762ca63a5ed396ad8876b0fe67eda authorMarcin Szamotulski <mszamot@gmail.com> dateSat 17 Jun 2017, 13:04 (9 years ago) parent2fe5be6
7 files changed+764
README.md+7 bower.json+22 src/Data/Intl/DateTimeFormat.js+21 src/Data/Intl/DateTimeFormat.purs+292 src/Data/Intl/DateTimeFormat/Class.purs+112 src/Data/Intl/DateTimeFormat/Types.purs+32 test/Test/Main.purs+278
README.mdnew file+7
@@ -0,0 +1,7 @@
1+# PureScript bindings to Intl - JavaScript Internalisation API
2+
3+[![Maintainer: coot](https://img.shields.io/badge/maintainer-coot-lightgrey.svg)](http://github.com/coot) [![documentation](https://pursuit.purescript.org/packages/purescript-intl)](https://pursuit.purescript.org/packages/purescript-intl)
4+
5+[Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
6+is ECMAScript Internalisation API. This library provides type safe PureScript
7+bindings.
bower.jsonnew file+22
@@ -0,0 +1,22 @@
1+{
2+ "name": "purescript-intl",
3+ "ignore": [
4+ "**/.*",
5+ "node_modules",
6+ "bower_components",
7+ "output"
8+ ],
9+ "dependencies": {
10+ "purescript-prelude": "^3.0.0",
11+ "purescript-console": "^3.0.0",
12+ "purescript-unsafe-coerce": "^3.0.0",
13+ "purescript-variant": "^1.1.0",
14+ "purescript-generics-rep": "^5.1.0",
15+ "purescript-maps": "^3.3.1",
16+ "purescript-js-date": "^5.0.0"
17+ },
18+ "devDependencies": {
19+ "purescript-psci-support": "^3.0.0",
20+ "purescript-assert": "^3.0.0"
21+ }
22+}
src/Data/Intl/DateTimeFormat.jsnew file+21
@@ -0,0 +1,21 @@
1+"use strict";
2+
3+exports["undefined"] = undefined;
4+
5+exports.dateTimeFormatImpl = function(locales) {
6+ return function(options) {
7+ return new Intl.DateTimeFormat(locales, options);
8+ };
9+};
10+
11+exports.createDateTimeFormatterImpl = function(locales) {
12+ return function(opts) {
13+ return Intl.DateTimeFormat(locales, opts);
14+ };
15+};
16+
17+exports.formatJSDate = function(formatter) {
18+ return function(date) {
19+ return formatter.format(date)
20+ };
21+};
src/Data/Intl/DateTimeFormat.pursnew file+292
@@ -0,0 +1,292 @@
1+module Data.Intl.DateTimeFormat
2+ ( LocalesOption
3+ , LocaleMatcher(..)
4+ , TimeZone(..)
5+ , FormatMatcher(..)
6+ , WeekdayYearMonthDayHourMinuteSecond(..)
7+ , WeekdayYearMonthDay(..)
8+ , YearMonthDay(..)
9+ , YearMonth(..)
10+ , MonthDay(..)
11+ , HourMinuteSecond(..)
12+ , HourMinute(..)
13+ , Undefined
14+ , undefined
15+ , DateTimeFormatOptions(..)
16+ , DateTimeFormatOptions'
17+ , DateTimeComponents(..)
18+ , DateTimeFormatter
19+ , createDateTimeFormatter
20+ , formatJSDate
21+ , module Data.Intl.DateTimeFormat.Types
22+
23+ -- private
24+ , formatDateTimeOptions
25+ , DateTimeFormatOptions_
26+ ) where
27+
28+import Data.Intl.DateTimeFormat.Types
29+
30+import Data.Generic.Rep (class Generic)
31+import Data.Intl.DateTimeFormat.Class (class FormatComponent, defaultComponentRecord, formatComponent, genericFormatComponent)
32+import Data.JSDate (JSDate)
33+import Data.Maybe (Maybe, fromMaybe, maybe)
34+import Data.Newtype (class Newtype)
35+import Data.StrMap (union)
36+import Data.Symbol (SProxy(..))
37+import Data.Variant (Variant, default, on)
38+import Prelude (class Eq, class Show, Unit, id, show, (#), ($))
39+import Unsafe.Coerce (unsafeCoerce)
40+
41+foreign import data LocalesOption' :: Type
42+foreign import data DateTimeFormat :: Type
43+
44+foreign import data Undefined :: Type
45+foreign import undefined :: Undefined
46+
47+foreign import dateTimeFormatImpl :: LocalesOption' -> DateTimeFormatOptions_ -> DateTimeFormat
48+
49+newtype DateTimeFormatOptions_ = DateTimeFormatOptions_
50+ { localeMatcher :: String
51+ , timeZone :: String
52+ , hour12 :: Boolean
53+ , formatMatcher :: String
54+ , weekday :: String
55+ , era :: String
56+ , year :: String
57+ , month :: String
58+ , day :: String
59+ , hour :: String
60+ , minutes :: String
61+ , second :: String
62+ , timeZoneName :: String
63+ }
64+
65+type LocalesOption = Variant (locale :: String, locales :: Array String, undefined :: Undefined)
66+
67+toLocale :: LocalesOption -> LocalesOption'
68+toLocale = unsafeCoerce $
69+ default undefined
70+ # on (SProxy :: SProxy "locale") id
71+ # on (SProxy :: SProxy "locales") id
72+
73+data LocaleMatcher
74+ = LMLookup
75+ | LMBestFit
76+
77+instance showLocaleMatcher :: Show LocaleMatcher where
78+ show LMLookup = "lookup"
79+ show LMBestFit = "best fit"
80+
81+newtype TimeZone = TimeZone String
82+
83+instance showTimeZone :: Show TimeZone where
84+ show (TimeZone tz) = tz
85+
86+derive instance newtypeTimeZone :: Newtype TimeZone _
87+
88+derive instance eqTimeZone :: Eq TimeZone
89+
90+data FormatMatcher
91+ = FMBasic
92+ | FMBestFit
93+
94+instance showFormatMatcher :: Show FormatMatcher where
95+ show FMBasic = "basic"
96+ show FMBestFit = "best fit"
97+
98+newtype WeekdayYearMonthDayHourMinuteSecond = WeekdayYearMonthDayHourMinuteSecond
99+ { weekday :: StringRep
100+ , year :: NumericRep
101+ , month :: MonthRep
102+ , day :: NumericRep
103+ , hour :: NumericRep
104+ , minute :: NumericRep
105+ , second :: NumericRep
106+ }
107+
108+derive instance genericWeekdayYearMonthDayHourMinuteSecond :: Generic WeekdayYearMonthDayHourMinuteSecond _
109+
110+instance formatComponentWeekdayYearMonthDayHourMinuteSecond :: FormatComponent WeekdayYearMonthDayHourMinuteSecond where
111+ formatComponent = genericFormatComponent
112+
113+newtype WeekdayYearMonthDay = WeekdayYearMonthDay
114+ { weekday :: StringRep
115+ , year :: NumericRep
116+ , month :: MonthRep
117+ , day :: NumericRep
118+ }
119+
120+derive instance genericWeekdayYearMonthDay :: Generic WeekdayYearMonthDay _
121+
122+instance formatComponentWeekdayYearMonthDay :: FormatComponent WeekdayYearMonthDay where
123+ formatComponent = genericFormatComponent
124+
125+newtype YearMonthDay = YearMonthDay
126+ { year :: NumericRep
127+ , month :: MonthRep
128+ , day :: NumericRep
129+ }
130+
131+derive instance genericYearMonthDay :: Generic YearMonthDay _
132+
133+instance formatComponentYearMonthDay :: FormatComponent YearMonthDay where
134+ formatComponent = genericFormatComponent
135+
136+newtype YearMonth = YearMonth
137+ { year :: NumericRep
138+ , month :: MonthRep
139+ }
140+
141+derive instance genericYearMonth :: Generic YearMonth _
142+
143+instance formatComponentYearMonth :: FormatComponent YearMonth where
144+ formatComponent = genericFormatComponent
145+
146+newtype MonthDay = MonthDay
147+ { month :: MonthRep
148+ , day :: NumericRep
149+ }
150+
151+derive instance genericMonthDay :: Generic MonthDay _
152+
153+instance formatComponentMonthDay :: FormatComponent MonthDay where
154+ formatComponent = genericFormatComponent
155+
156+newtype HourMinuteSecond = HourMinuteSecond
157+ { hour :: NumericRep
158+ , minute :: NumericRep
159+ , second :: NumericRep
160+ }
161+
162+derive instance genericHourMinuteSecond :: Generic HourMinuteSecond _
163+
164+instance formatComponentHourMinuteSecond :: FormatComponent HourMinuteSecond where
165+ formatComponent = genericFormatComponent
166+
167+newtype HourMinute = HourMinute
168+ { hour :: NumericRep
169+ , minute :: NumericRep
170+ }
171+
172+derive instance genericHourMinute :: Generic HourMinute _
173+
174+instance formatComponentHourMinute :: FormatComponent HourMinute where
175+ formatComponent = genericFormatComponent
176+
177+type DateTimeComponents a =
178+ Variant
179+ ( weekdayYearMonthDayHourMinuteSecond :: WeekdayYearMonthDayHourMinuteSecond
180+ , weekdayYearMonthDay :: WeekdayYearMonthDay
181+ , yearMonthDay :: YearMonthDay
182+ , yearMonth :: YearMonth
183+ , monthDay :: MonthDay
184+ , hourMinuteSecond :: HourMinuteSecond
185+ , hourMinute :: HourMinute
186+ , custom :: a
187+ )
188+
189+data DateTimeFormatOptions a = DateTimeFormatOptions
190+ { localeMatcher :: Maybe LocaleMatcher
191+ , timeZone :: Maybe TimeZone
192+ , hour12 :: Maybe Boolean
193+ , formatMatcher :: Maybe FormatMatcher
194+ }
195+ (DateTimeComponents a)
196+
197+type DateTimeFormatOptions' = DateTimeFormatOptions Unit
198+
199+formatDateTimeOptions :: forall a. (FormatComponent a) => DateTimeFormatOptions a -> DateTimeFormatOptions_
200+formatDateTimeOptions (DateTimeFormatOptions opts comps) = merge opts' (compsCoerce comps')
201+ where
202+
203+ undefinedString :: String
204+ undefinedString = unsafeCoerce undefined
205+
206+ opts' = { localeMatcher: maybe undefinedString show opts.localeMatcher
207+ , timeZone: maybe undefinedString show opts.timeZone
208+ , hour12: fromMaybe (unsafeCoerce undefined) opts.hour12
209+ , formatMatcher: maybe undefinedString show opts.formatMatcher
210+ }
211+ comps' = default (defaultComponentRecord)
212+ # on (SProxy :: SProxy "weekdayYearMonthDayHourMinuteSecond") formatComponent
213+ # on (SProxy :: SProxy "weekdayYearMonthDay") formatComponent
214+ # on (SProxy :: SProxy "yearMonthDay") formatComponent
215+ # on (SProxy :: SProxy "yearMonth") formatComponent
216+ # on (SProxy :: SProxy "monthDay") formatComponent
217+ # on (SProxy :: SProxy "hourMinuteSecond") formatComponent
218+ # on (SProxy :: SProxy "hourMinute") formatComponent
219+ # on (SProxy :: SProxy "custom") formatComponent
220+ $ comps
221+
222+ toUndefined :: Maybe String -> String
223+ toUndefined m = fromMaybe (unsafeCoerce undefined) m
224+
225+ compsCoerce
226+ :: { weekday :: Maybe String
227+ , era :: Maybe String
228+ , year :: Maybe String
229+ , month :: Maybe String
230+ , day :: Maybe String
231+ , hour :: Maybe String
232+ , minute :: Maybe String
233+ , second :: Maybe String
234+ , timeZoneName :: Maybe String
235+ }
236+ -> { weekday :: String
237+ , era :: String
238+ , year :: String
239+ , month :: String
240+ , day :: String
241+ , hour :: String
242+ , minute :: String
243+ , second :: String
244+ , timeZoneName :: String
245+ }
246+ compsCoerce { weekday, era, year, month, day, hour, minute, second, timeZoneName }
247+ = { weekday: toUndefined weekday
248+ , era: toUndefined era
249+ , year: toUndefined year
250+ , month: toUndefined month
251+ , day: toUndefined day
252+ , hour: toUndefined hour
253+ , minute: toUndefined minute
254+ , second: toUndefined second
255+ , timeZoneName: toUndefined timeZoneName
256+ }
257+
258+ merge
259+ :: { localeMatcher :: String
260+ , timeZone :: String
261+ , hour12 :: Boolean
262+ , formatMatcher :: String
263+ }
264+ -> { weekday :: String
265+ , era :: String
266+ , year :: String
267+ , month :: String
268+ , day :: String
269+ , hour :: String
270+ , minute :: String
271+ , second :: String
272+ , timeZoneName :: String
273+ }
274+ -> DateTimeFormatOptions_
275+ merge a b = unsafeCoerce $ union (unsafeCoerce a) (unsafeCoerce b)
276+
277+foreign import data DateTimeFormatter :: Type
278+
279+foreign import createDateTimeFormatterImpl
280+ :: LocalesOption'
281+ -> DateTimeFormatOptions_
282+ -> DateTimeFormatter
283+
284+createDateTimeFormatter
285+ :: forall a
286+ .(FormatComponent a)
287+ => LocalesOption
288+ -> DateTimeFormatOptions a
289+ -> DateTimeFormatter
290+createDateTimeFormatter ls opts = createDateTimeFormatterImpl (toLocale ls) (formatDateTimeOptions opts)
291+
292+foreign import formatJSDate :: DateTimeFormatter -> JSDate -> String
src/Data/Intl/DateTimeFormat/Class.pursnew file+112
@@ -0,0 +1,112 @@
1+module Data.Intl.DateTimeFormat.Class where
2+
3+import Data.Generic.Rep (class Generic, Constructor(Constructor), Field(..), NoConstructors, Product(..), Rec(..), from)
4+import Data.Intl.DateTimeFormat.Types (MonthRep, NumericRep, TimeZoneNameRep, StringRep)
5+import Data.Maybe (Maybe(..))
6+import Data.StrMap (StrMap, lookup, singleton, union)
7+import Prelude (Unit, show)
8+
9+type FormatComponentRecord =
10+ { era :: Maybe String
11+ , weekday :: Maybe String
12+ , year :: Maybe String
13+ , month :: Maybe String
14+ , day :: Maybe String
15+ , hour :: Maybe String
16+ , minute :: Maybe String
17+ , second :: Maybe String
18+ , timeZoneName :: Maybe String
19+ }
20+
21+defaultComponentRecord :: FormatComponentRecord
22+defaultComponentRecord =
23+ { era : Nothing
24+ , weekday : Nothing
25+ , year : Nothing
26+ , month : Nothing
27+ , day : Nothing
28+ , hour : Nothing
29+ , minute : Nothing
30+ , second : Nothing
31+ , timeZoneName : Nothing
32+ }
33+
34+class FormatComponent a where
35+ formatComponent :: a -> FormatComponentRecord
36+
37+instance unitFormatComponent :: FormatComponent Unit where
38+ formatComponent _ = defaultComponentRecord
39+
40+class GenericFormatComponent a where
41+ genericFormatComponent' :: a -> FormatComponentRecord
42+
43+class GenericFormatComponentFields a where
44+ genericFormatComponentFields :: a -> StrMap String
45+
46+instance genericFormatComponentNoConsturctors :: GenericFormatComponent NoConstructors where
47+ genericFormatComponent' a = { era: Nothing, weekday: Nothing, year: Nothing, month: Nothing, day: Nothing, hour: Nothing, minute: Nothing, second: Nothing, timeZoneName: Nothing }
48+
49+instance genericFormatComponentConstructor :: (GenericFormatComponent a) => GenericFormatComponent (Constructor name a) where
50+ genericFormatComponent' (Constructor a) = genericFormatComponent' a
51+
52+instance genericFormatComponentFieldsEra
53+ :: GenericFormatComponentFields (Field "era" StringRep) where
54+ genericFormatComponentFields (Field a) = singleton "era" (show a)
55+
56+instance genericFormatComponentFieldsWeekday
57+ :: GenericFormatComponentFields (Field "weekday" StringRep) where
58+ genericFormatComponentFields (Field a) = singleton "weekday" (show a)
59+
60+instance genericFormatComponentFieldsYear
61+ :: GenericFormatComponentFields (Field "year" NumericRep) where
62+ genericFormatComponentFields (Field a) = singleton "year" (show a)
63+
64+instance genericFormatComponentFieldsMonth
65+ :: GenericFormatComponentFields (Field "month" MonthRep) where
66+ genericFormatComponentFields (Field a) = singleton "month" (show a)
67+
68+instance genericFormatComponentFieldsDay
69+ :: GenericFormatComponentFields (Field "day" NumericRep) where
70+ genericFormatComponentFields (Field a) = singleton "day" (show a)
71+
72+instance genericFormatComponentFieldsHour
73+ :: GenericFormatComponentFields (Field "hour" NumericRep) where
74+ genericFormatComponentFields (Field a) = singleton "hour" (show a)
75+
76+instance genericFormatComponentFieldsMinute
77+ :: GenericFormatComponentFields (Field "minute" NumericRep) where
78+ genericFormatComponentFields (Field a) = singleton "minute" (show a)
79+
80+instance genericFormatComponentFieldsSecond
81+ :: GenericFormatComponentFields (Field "second" NumericRep) where
82+ genericFormatComponentFields (Field a) = singleton "second" (show a)
83+
84+instance genericFormatComponentFieldsTimeZoneName
85+ :: GenericFormatComponentFields (Field "timeZoneName" TimeZoneNameRep) where
86+ genericFormatComponentFields (Field a) = singleton "timeZoneName" (show a)
87+
88+instance genericFormatComponentFieldsProduct
89+ :: (GenericFormatComponentFields a, GenericFormatComponentFields b)
90+ => GenericFormatComponentFields (Product a b) where
91+ genericFormatComponentFields (Product a b) =
92+ union (genericFormatComponentFields a) (genericFormatComponentFields b)
93+
94+instance genericFormatComponentRec
95+ :: (GenericFormatComponentFields a)
96+ => GenericFormatComponent (Rec a) where
97+ genericFormatComponent' (Rec a) =
98+ { era: "era" `lookup` u
99+ , weekday: "weekday" `lookup` u
100+ , year: "year" `lookup` u
101+ , month: "month" `lookup` u
102+ , day: "day" `lookup` u
103+ , hour: "hour" `lookup` u
104+ , minute: "minute" `lookup` u
105+ , second: "second" `lookup` u
106+ , timeZoneName: "timeZoneName" `lookup` u
107+ }
108+ where
109+ u = genericFormatComponentFields a
110+
111+genericFormatComponent :: forall a rep. Generic a rep => GenericFormatComponent rep => a -> FormatComponentRecord
112+genericFormatComponent a = genericFormatComponent' (from a)
src/Data/Intl/DateTimeFormat/Types.pursnew file+32
@@ -0,0 +1,32 @@
1+module Data.Intl.DateTimeFormat.Types where
2+
3+import Prelude (class Show)
4+
5+data StringRep = Narrow | Short | Long
6+
7+instance showStringRep :: Show StringRep where
8+ show Narrow = "narrow"
9+ show Short = "short"
10+ show Long = "long"
11+
12+data NumericRep = Numeric | TwoDigit
13+
14+instance showNumericRep :: Show NumericRep where
15+ show Numeric = "numeric"
16+ show TwoDigit = "2-digit"
17+
18+data MonthRep = MonthNumeric | MonthTwoDigit | MonthNarrow | MonthShort | MonthLong
19+
20+instance showMonthRep :: Show MonthRep where
21+ show MonthNumeric = "numeric"
22+ show MonthTwoDigit = "2-digit"
23+ show MonthNarrow = "narrow"
24+ show MonthShort = "short"
25+ show MonthLong = "long"
26+
27+data TimeZoneNameRep = TimeZoneNameShort | TimeZoneNameLong
28+
29+instance showTimeZoneRep :: Show TimeZoneNameRep where
30+ show TimeZoneNameShort = "short"
31+ show TimeZoneNameLong = "long"
32+
test/Test/Main.pursnew file+278
@@ -0,0 +1,278 @@
1+module Test.Main where
2+
3+import Prelude
4+
5+import Control.Monad.Eff (Eff)
6+import Control.Monad.Eff.Console (CONSOLE, log)
7+import Data.DateTime (Date, DateTime(..), Month(..), canonicalDate)
8+import Data.Enum (toEnum)
9+import Data.Generic.Rep (class Generic)
10+import Data.Intl.DateTimeFormat (DateTimeFormatOptions(..), DateTimeFormatOptions', HourMinute(HourMinute), HourMinuteSecond(HourMinuteSecond), LocalesOption, MonthDay(..), MonthRep(..), NumericRep(Numeric, TwoDigit), StringRep(..), TimeZone(TimeZone), WeekdayYearMonthDay(..), WeekdayYearMonthDayHourMinuteSecond(WeekdayYearMonthDayHourMinuteSecond), YearMonth(..), YearMonthDay(..), createDateTimeFormatter, formatJSDate)
11+import Data.Intl.DateTimeFormat.Class (class FormatComponent, genericFormatComponent)
12+import Data.JSDate (JSDate, fromDateTime)
13+import Data.Maybe (Maybe(..))
14+import Data.Symbol (SProxy(..))
15+import Data.Time (Time(..))
16+import Data.Variant (inj)
17+import Partial.Unsafe (unsafeCrashWith)
18+import Test.Assert (ASSERT, assert)
19+
20+type Tests = Eff (console :: CONSOLE, assert :: ASSERT) Unit
21+
22+newtype EraYear = EraYear
23+ { era :: StringRep
24+ , year :: NumericRep
25+ }
26+
27+derive instance genericEraYear :: Generic EraYear _
28+
29+instance formatComponentEraYear :: FormatComponent EraYear where
30+ formatComponent = genericFormatComponent
31+
32+newtype Era = Era StringRep
33+
34+instance formatComponentEra :: FormatComponent Era where
35+ formatComponent (Era r) =
36+ { era: Just (show r)
37+ , weekday: Nothing
38+ , year: Nothing
39+ , month: Nothing
40+ , day: Nothing
41+ , hour: Nothing
42+ , minute: Nothing
43+ , second: Nothing
44+ , timeZoneName: Nothing
45+ }
46+
47+main :: Tests
48+main = do
49+ let
50+
51+ locales :: LocalesOption
52+ locales = inj (SProxy :: SProxy "locale") "en-GB"
53+
54+ fmtHourMinute =
55+ createDateTimeFormatter locales opts
56+ where
57+ opts :: DateTimeFormatOptions'
58+ opts =
59+ (DateTimeFormatOptions
60+ { localeMatcher: Nothing
61+ , timeZone: (Just (TimeZone "Europe/London"))
62+ , hour12: Just false
63+ , formatMatcher: Nothing
64+ }
65+ (inj (SProxy :: SProxy "hourMinute") (HourMinute {hour: TwoDigit, minute: TwoDigit}))
66+ )
67+
68+ fmtHourMinuteSecond =
69+ createDateTimeFormatter locales opts
70+ where
71+ opts :: DateTimeFormatOptions'
72+ opts =
73+ (DateTimeFormatOptions
74+ { localeMatcher: Nothing
75+ , timeZone: (Just (TimeZone "Europe/London"))
76+ , hour12: Just false
77+ , formatMatcher: Nothing
78+ }
79+ (inj (SProxy :: SProxy "hourMinuteSecond") (HourMinuteSecond {hour: Numeric, minute: Numeric, second: Numeric}))
80+ )
81+
82+ fmtWeekdayYearMonthDayHourMinuteSecond =
83+ createDateTimeFormatter locales opts
84+ where
85+ opts :: DateTimeFormatOptions'
86+ opts =
87+ (DateTimeFormatOptions
88+ { localeMatcher: Nothing
89+ , timeZone: (Just (TimeZone "Europe/London"))
90+ , hour12: Just true
91+ , formatMatcher: Nothing
92+ }
93+ (inj
94+ (SProxy :: SProxy "weekdayYearMonthDayHourMinuteSecond")
95+ (WeekdayYearMonthDayHourMinuteSecond
96+ { weekday: Short
97+ , year: Numeric
98+ , month: MonthLong
99+ , day: Numeric
100+ , hour: Numeric
101+ , minute: Numeric
102+ , second: Numeric
103+ }
104+ )
105+ )
106+ )
107+
108+ fmtWeekdayYearMonthDay =
109+ createDateTimeFormatter locales opts
110+ where
111+ opts :: DateTimeFormatOptions'
112+ opts =
113+ (DateTimeFormatOptions
114+ { localeMatcher: Nothing
115+ , timeZone: (Just (TimeZone "Europe/London"))
116+ , hour12: Just true
117+ , formatMatcher: Nothing
118+ }
119+ (inj
120+ (SProxy :: SProxy "weekdayYearMonthDay")
121+ (WeekdayYearMonthDay
122+ { weekday: Long
123+ , year: TwoDigit
124+ , month: MonthShort
125+ , day: TwoDigit
126+ }
127+ )
128+ )
129+ )
130+
131+ fmtYearMonthDay =
132+ createDateTimeFormatter locales opts
133+ where
134+ opts :: DateTimeFormatOptions'
135+ opts =
136+ (DateTimeFormatOptions
137+ { localeMatcher: Nothing
138+ , timeZone: (Just (TimeZone "Europe/London"))
139+ , hour12: Just true
140+ , formatMatcher: Nothing
141+ }
142+ (inj
143+ (SProxy :: SProxy "yearMonthDay")
144+ (YearMonthDay
145+ { year: TwoDigit
146+ , month: MonthNarrow
147+ , day: TwoDigit
148+ }
149+ )
150+ )
151+ )
152+
153+ fmtYearMonth =
154+ createDateTimeFormatter locales opts
155+ where
156+ opts :: DateTimeFormatOptions'
157+ opts =
158+ (DateTimeFormatOptions
159+ { localeMatcher: Nothing
160+ , timeZone: (Just (TimeZone "Europe/London"))
161+ , hour12: Just true
162+ , formatMatcher: Nothing
163+ }
164+ (inj
165+ (SProxy :: SProxy "yearMonth")
166+ (YearMonth
167+ { year: Numeric
168+ , month: MonthNumeric
169+ }
170+ )
171+ )
172+ )
173+
174+ fmtMonthDay =
175+ createDateTimeFormatter locales opts
176+ where
177+ opts :: DateTimeFormatOptions'
178+ opts =
179+ (DateTimeFormatOptions
180+ { localeMatcher: Nothing
181+ , timeZone: (Just (TimeZone "Europe/London"))
182+ , hour12: Just true
183+ , formatMatcher: Nothing
184+ }
185+ (inj
186+ (SProxy :: SProxy "monthDay")
187+ (MonthDay
188+ { month: MonthTwoDigit
189+ , day: TwoDigit
190+ }
191+ )
192+ )
193+ )
194+
195+ fmtEraYear =
196+ createDateTimeFormatter locales opts
197+ where
198+ opts :: DateTimeFormatOptions EraYear
199+ opts =
200+ (DateTimeFormatOptions
201+ { localeMatcher: Nothing
202+ , timeZone: (Just (TimeZone "Europe/London"))
203+ , hour12: Just true
204+ , formatMatcher: Nothing
205+ }
206+ (inj
207+ (SProxy :: SProxy "custom")
208+ (EraYear
209+ { era: Long
210+ , year: Numeric
211+ }
212+ )
213+ )
214+ )
215+
216+ fmtEra =
217+ createDateTimeFormatter locales opts
218+ where
219+ opts :: DateTimeFormatOptions Era
220+ opts =
221+ (DateTimeFormatOptions
222+ { localeMatcher: Nothing
223+ , timeZone: (Just (TimeZone "Europe/London"))
224+ , hour12: Just true
225+ , formatMatcher: Nothing
226+ }
227+ (inj
228+ (SProxy :: SProxy "custom")
229+ (Era Short)
230+ )
231+ )
232+
233+
234+ date :: Maybe Date
235+ date = do
236+ y <- toEnum 2018
237+ let m = January
238+ d <- toEnum 2
239+ pure $ canonicalDate y m d
240+
241+ time :: Maybe Time
242+ time = do
243+ h <- toEnum 12
244+ m <- toEnum 0
245+ s <- toEnum 0
246+ ms <- toEnum 0
247+ pure $ Time h m s ms
248+
249+ dt :: Maybe DateTime
250+ dt = do
251+ d <- date
252+ t <- time
253+ pure $ DateTime d t
254+
255+ jsDate :: Maybe JSDate
256+ jsDate = fromDateTime <$> dt
257+
258+ case jsDate of
259+ Nothing -> unsafeCrashWith "jsDate not parsed"
260+ Just d -> do
261+ log "fmtWeekdayYearMonthDayHourMinuteSecond"
262+ assert $ formatJSDate fmtWeekdayYearMonthDayHourMinuteSecond d == "Tue, 2 January 2018, 12:00:00 pm"
263+ log "fmtWeekdayYearMonthDay"
264+ assert $ formatJSDate fmtWeekdayYearMonthDay d == "Tuesday, 02 Jan 18"
265+ log "fmtYearMonthDay"
266+ assert $ formatJSDate fmtYearMonthDay d == "02 J 18"
267+ log "fmtYearMonth"
268+ assert $ formatJSDate fmtYearMonth d == "01/2018"
269+ log "fmtMonthDay"
270+ assert $ formatJSDate fmtMonthDay d == "02/01"
271+ log "fmtHourMinuteSecond"
272+ assert $ formatJSDate fmtHourMinuteSecond d == "12:00:00"
273+ log "fmtHourMinute"
274+ assert $ formatJSDate fmtHourMinute d == "12:00"
275+ log "fmtEraYear"
276+ assert $ formatJSDate fmtEraYear d == "2018 Anno Domini"
277+ log "fmtEra"
278+ assert $ formatJSDate fmtEra d == "2 1 2018 AD"