@@ -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