@@ -1,47 +1,39 @@
1−module Data.Intl.DateTimeFormat
1+module Data.Intl.DateTimeFormat
22 ( LocalesOption
33 , LocaleMatcher(..)
44 , TimeZone(..)
55 , FormatMatcher(..)
6− , WeekdayYearMonthDayHourMinuteSecond(..)
7− , WeekdayYearMonthDay(..)
8− , YearMonthDay(..)
9− , YearMonth(..)
10− , MonthDay(..)
11− , HourMinuteSecond(..)
12− , HourMinute(..)
13− , Undefined
14− , undefined
156 , DateTimeFormatOptions(..)
167 , DateTimeFormatOptions'
178 , DateTimeComponents(..)
18− , DateTimeFormatter
19− , createDateTimeFormatter
9+ , DateTimeFormat
10+ , createDateTimeFormat
2011 , formatJSDate
2112 , module Data.Intl.DateTimeFormat.Types
2213 , supportedLocalesOf
14+ , formatToParts
15+ , resolvedOptions
2316 ) where
2417
2518import Data.Intl.DateTimeFormat.Types
2619
20+import Control.Monad.Error.Class (throwError)
2721import Data.Either (Either(..))
28−import Data.Function.Uncurried (Fn3, runFn3)
29−import Data.Generic.Rep (class Generic)
30−import Data.Intl.DateTimeFormat.Generic (class FormatComponent, defaultComponentRecord, formatComponent, genericFormatComponent)
22+import Data.Foreign (F, Foreign, ForeignError(..), isUndefined, readString)
23+import Data.Foreign.Index ((!))
24+import Data.Function.Uncurried (Fn3, Fn4, runFn3, runFn4)
25+import Data.Intl.DateTimeFormat.Class (class FormatComponent, FormatComponentRecord(FormatComponentRecord), formatComponent)
3126import Data.JSDate (JSDate)
32−import Data.Maybe (Maybe, fromMaybe, maybe)
33−import Data.Newtype (class Newtype)
27+import Data.Maybe (Maybe(..), fromMaybe, maybe)
28+import Data.Newtype (class Newtype, un)
3429import Data.StrMap (union)
3530import Data.Symbol (SProxy(..))
36−import Data.Variant (Variant, default, on)
37−import Prelude (class Eq, class Show, Unit, id, show, (#), ($))
31+import Data.Traversable (sequence)
32+import Data.Variant (Variant, case_, default, on)
33+import Prelude (class Eq, class Show, Unit, bind, id, pure, show, (#), ($), (<$>), (<<<), (>>=))
3834import Unsafe.Coerce (unsafeCoerce)
3935
4036foreign import data LocalesOption' :: Type
41−foreign import data DateTimeFormat :: Type
42−
43−foreign import data Undefined :: Type
44−foreign import undefined :: Undefined
4537
4638foreign import dateTimeFormatImpl :: LocalesOption' -> DateTimeFormatOptions_ -> DateTimeFormat
4739
@@ -94,85 +86,6 @@ instance showFormatMatcher :: Show FormatMatcher where
9486 show FMBasic = "basic"
9587 show FMBestFit = "best fit"
9688
97−newtype WeekdayYearMonthDayHourMinuteSecond = WeekdayYearMonthDayHourMinuteSecond
98− { weekday :: StringRep
99− , year :: NumericRep
100− , month :: MonthRep
101− , day :: NumericRep
102− , hour :: NumericRep
103− , minute :: NumericRep
104− , second :: NumericRep
105− }
106−
107−derive instance genericWeekdayYearMonthDayHourMinuteSecond :: Generic WeekdayYearMonthDayHourMinuteSecond _
108−
109−instance formatComponentWeekdayYearMonthDayHourMinuteSecond :: FormatComponent WeekdayYearMonthDayHourMinuteSecond where
110− formatComponent = genericFormatComponent
111−
112−newtype WeekdayYearMonthDay = WeekdayYearMonthDay
113− { weekday :: StringRep
114− , year :: NumericRep
115− , month :: MonthRep
116− , day :: NumericRep
117− }
118−
119−derive instance genericWeekdayYearMonthDay :: Generic WeekdayYearMonthDay _
120−
121−instance formatComponentWeekdayYearMonthDay :: FormatComponent WeekdayYearMonthDay where
122− formatComponent = genericFormatComponent
123−
124−newtype YearMonthDay = YearMonthDay
125− { year :: NumericRep
126− , month :: MonthRep
127− , day :: NumericRep
128− }
129−
130−derive instance genericYearMonthDay :: Generic YearMonthDay _
131−
132−instance formatComponentYearMonthDay :: FormatComponent YearMonthDay where
133− formatComponent = genericFormatComponent
134−
135−newtype YearMonth = YearMonth
136− { year :: NumericRep
137− , month :: MonthRep
138− }
139−
140−derive instance genericYearMonth :: Generic YearMonth _
141−
142−instance formatComponentYearMonth :: FormatComponent YearMonth where
143− formatComponent = genericFormatComponent
144−
145−newtype MonthDay = MonthDay
146− { month :: MonthRep
147− , day :: NumericRep
148− }
149−
150−derive instance genericMonthDay :: Generic MonthDay _
151−
152−instance formatComponentMonthDay :: FormatComponent MonthDay where
153− formatComponent = genericFormatComponent
154−
155−newtype HourMinuteSecond = HourMinuteSecond
156− { hour :: NumericRep
157− , minute :: NumericRep
158− , second :: NumericRep
159− }
160−
161−derive instance genericHourMinuteSecond :: Generic HourMinuteSecond _
162−
163−instance formatComponentHourMinuteSecond :: FormatComponent HourMinuteSecond where
164− formatComponent = genericFormatComponent
165−
166−newtype HourMinute = HourMinute
167− { hour :: NumericRep
168− , minute :: NumericRep
169− }
170−
171−derive instance genericHourMinute :: Generic HourMinute _
172−
173−instance formatComponentHourMinute :: FormatComponent HourMinute where
174− formatComponent = genericFormatComponent
175−
17689type DateTimeComponents a =
17790 Variant
17891 ( weekdayYearMonthDayHourMinuteSecond :: WeekdayYearMonthDayHourMinuteSecond
@@ -207,7 +120,7 @@ formatDateTimeOptions (DateTimeFormatOptions opts comps) = merge opts' (compsCoe
207120 , hour12: fromMaybe (unsafeCoerce undefined) opts.hour12
208121 , formatMatcher: maybe undefinedString show opts.formatMatcher
209122 }
210− comps' = default (defaultComponentRecord)
123+ comps' = un FormatComponentRecord $ case_
211124 # on (SProxy :: SProxy "weekdayYearMonthDayHourMinuteSecond") formatComponent
212125 # on (SProxy :: SProxy "weekdayYearMonthDay") formatComponent
213126 # on (SProxy :: SProxy "yearMonthDay") formatComponent
@@ -273,24 +186,158 @@ formatDateTimeOptions (DateTimeFormatOptions opts comps) = merge opts' (compsCoe
273186 -> DateTimeFormatOptions_
274187 merge a b = unsafeCoerce $ union (unsafeCoerce a) (unsafeCoerce b)
275188
276−foreign import data DateTimeFormatter :: Type
277−
278−foreign import createDateTimeFormatterImpl
279− :: LocalesOption'
280− -> DateTimeFormatOptions_
281− -> DateTimeFormatter
189+foreign import data DateTimeFormat :: Type
282190
283−createDateTimeFormatter
191+foreign import createDateTimeFormatImpl
192+ :: Fn4
193+ (forall a. String -> F a)
194+ (forall a. a -> F a)
195+ LocalesOption'
196+ DateTimeFormatOptions_
197+ (F DateTimeFormat)
198+
199+-- | Examples
200+-- | ``` purescript
201+-- | import Data.Variant (inj)
202+-- |
203+-- | fmtHourMinute = F DateTimeFormat
204+-- | fmtHourMinute = createDateTimeFormat locale opts
205+-- | where
206+-- | locale = inj (SProxy :: SProxy "locale") "en-GB"
207+-- | opts = (DateTimeFormatOptions
208+-- | { localeMatcher: Nothing
209+-- | , timeZone: (Just (TimeZone "Europe/London"))
210+-- | , hour12: Just false
211+-- | , formatMatcher: Nothing
212+-- | }
213+-- | (inj (SProxy :: SProxy "hourMinute")
214+-- | (HourMinute {hour: TwoDigit, minute: TwoDigit}))
215+-- |
216+-- |
217+-- | fmtDate :: F DateTimeFormat
218+-- | fmtDate = createDateTimeFormat locale opts
219+-- | wher
220+-- | locale = inj (SProxy :: SProxy "locale") "en-GB"
221+-- | opts :: DateTimeFormatOptions'
222+-- | opts =
223+-- | (DateTimeFormatOptions
224+-- | { localeMatcher: Nothing
225+-- | , timeZone: (Just (TimeZone "Europe/London"))
226+-- | , hour12: Just true
227+-- | , formatMatcher: Nothing
228+-- | }
229+-- | (inj
230+-- | (SProxy :: SProxy "weekdayYearMonthDayHourMinuteSecond")
231+-- | (WeekdayYearMonthDayHourMinuteSecond
232+-- | { weekday: Short
233+-- | , year: Numeric
234+-- | , month: MonthLong
235+-- | , day: Numeric
236+-- | , hour: Numeric
237+-- | , minute: Numeric
238+-- | , second: Numeric
239+-- | }
240+-- | )
241+-- | )
242+-- | )
243+-- | ```
244+-- | You have predifined types: `WeekdayYearMonthDayHourMinuteSecond`,
245+-- | `WeekdayYearMonthDay`, `YearMonthDay`, `YearMonth`, `MonthDay`,
246+-- | `HourMinuteSecond`, `HourMinute`. But you can also provide your own by
247+-- | implementing `FormatComponent` class (or by deriving it), e.g.
248+-- | ``` purescript
249+-- | newtype EraYear = EraYear
250+-- | { era :: StringRep
251+-- | , year :: NumericRep
252+-- | }
253+-- |
254+-- | derive instance genericEraYear :: Generic EraYear _
255+-- |
256+-- | instance formatComponentEraYear :: FormatComponent EraYear where
257+-- | formatComponent = genericFormatComponent
258+-- |
259+-- | fmtEraYear :: F DateTimeFormat
260+-- | fmtEraYear = createDateTimeFormat locales opts
261+-- | where
262+-- | locale = inj (SProxy :: SProxy "locale") "en-GB"
263+-- | opts :: DateTimeFormatOptions EraYear
264+-- | opts =
265+-- | (DateTimeFormatOptions
266+-- | { localeMatcher: Nothing
267+-- | , timeZone: (Just (TimeZone "Europe/London"))
268+-- | , hour12: Just true
269+-- | , formatMatcher: Nothing
270+-- | }
271+-- | (inj
272+-- | (SProxy :: SProxy "custom")
273+-- | (EraYear
274+-- | { era: Long
275+-- | , year: Numeric
276+-- | }
277+-- | )
278+-- | )
279+-- | )
280+-- | ```
281+createDateTimeFormat
284282 :: forall a
285283 .(FormatComponent a)
286284 => LocalesOption
287285 -> DateTimeFormatOptions a
288− -> DateTimeFormatter
289−createDateTimeFormatter ls opts = createDateTimeFormatterImpl (toLocale ls) (formatDateTimeOptions opts)
290−
291−foreign import formatJSDate :: DateTimeFormatter -> JSDate -> String
292−
293−foreign import supportedLocalesOfImpl :: Fn3 (String -> Either String (Array String)) (Array String -> Either String (Array String)) (Array String) (Either String (Array String))
286+ -> F DateTimeFormat
287+createDateTimeFormat ls opts =
288+ runFn4 createDateTimeFormatImpl
289+ (throwError <<< pure <<< ForeignError)
290+ pure
291+ (toLocale ls)
292+ (formatDateTimeOptions opts)
293+
294+-- | Examples
295+-- | ``` purescript
296+-- | formatJSDate (either (const "") id $ runExcept fmtHourMinute) date -- "12:00"
297+-- | formatJSDate (either (const "") id $ fmtDate) date -- "Tue, January 2, 2018, 12:00:00 PM"
298+-- | formatJSDate (either (const "") id $ fmtEraYear) date -- "2018 Anno Domini"
299+-- | ```
300+foreign import formatJSDate :: DateTimeFormat -> JSDate -> String
301+
302+foreign import supportedLocalesOfImpl
303+ :: Fn3
304+ (String -> Either String (Array String))
305+ (Array String -> Either String (Array String))
306+ (Array String)
307+ (Either String (Array String))
308+
309+foreign import formatToPartsImpl :: DateTimeFormat -> JSDate -> Array Foreign
310+
311+formatToParts :: DateTimeFormat -> JSDate -> F (Array FormatParts)
312+formatToParts formatter date = sequence $ readFormatParts <$> formatToPartsImpl formatter date
294313
295314supportedLocalesOf :: Array String -> Either String (Array String)
296315supportedLocalesOf locales = runFn3 supportedLocalesOfImpl Left Right locales
316+
317+foreign import resolvedOptionsImpl :: DateTimeFormat -> Foreign
318+
319+resolvedOptions :: DateTimeFormat -> F ResolvedOptions
320+resolvedOptions fmt = do
321+ locale <- obj ! "locale" >>= readString
322+ calendar <- obj ! "calendar" >>= readString
323+ numberingSystem <- obj ! "numberingSystem" >>= readString
324+ timeZone <- obj ! "timeZone" >>= readString
325+ era <- obj ! "era" >>= readMaybeString
326+ weekday <- obj ! "weekday" >>= readMaybeString
327+ year <- obj ! "year" >>= readMaybeString
328+ day <- obj ! "day" >>= readMaybeString
329+ hour <- obj ! "hour" >>= readMaybeString
330+ minute <- obj ! "minute" >>= readMaybeString
331+ second <- obj ! "second" >>= readMaybeString
332+ timeZoneName <- obj ! "timeZoneName" >>= readMaybeString
333+
334+ pure $ ResolvedOptions { locale, calendar, numberingSystem, timeZone, era, weekday, year, day, hour, minute, second, timeZoneName }
335+
336+ where
337+ obj = resolvedOptionsImpl fmt
338+
339+ readMaybeString :: Foreign -> F (Maybe String)
340+ readMaybeString f =
341+ if isUndefined f
342+ then pure Nothing
343+ else Just <$> readString f