forked from finos/morphir-elm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LocalDate.elm
391 lines (278 loc) · 8.14 KB
/
LocalDate.elm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
{-
Copyright 2020 Morgan Stanley
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-}
module Morphir.SDK.LocalDate exposing
( LocalDate
, diffInDays, diffInWeeks, diffInMonths, diffInYears
, addDays, addWeeks, addMonths, addYears
, fromCalendarDate, fromISO, fromOrdinalDate, fromParts, fromRataDie
, toISOString, toRataDie
, DayOfWeek(..), dayOfWeek, isWeekend, isWeekday
, Month(..)
, year, month, day
)
{-| This module adds the definition of a date without time zones. Useful in business modeling.
# Types
@docs LocalDate
# Date Math
@docs diffInDays, diffInWeeks, diffInMonths, diffInYears
@docs addDays, addWeeks, addMonths, addYears
# Constructors
@docs fromCalendarDate, fromISO, fromOrdinalDate, fromParts, fromRataDie
# Convert
@docs toISOString, toRataDie
# Query
@docs DayOfWeek, dayOfWeek, isWeekend, isWeekday
@docs Month
@docs year, month, day
-}
import Date exposing (Date, Unit(..))
import Time
{-| Concept of a date without time zones.
-}
type alias LocalDate =
Date
{-| Find the number of days between the given dates.
-}
diffInDays : LocalDate -> LocalDate -> Int
diffInDays fromDate toDate =
Date.diff Days fromDate toDate
{-| Find the number of weeks between the given dates.
-}
diffInWeeks : LocalDate -> LocalDate -> Int
diffInWeeks fromDate toDate =
Date.diff Weeks fromDate toDate
{-| Find the number of months between the given dates.
-}
diffInMonths : LocalDate -> LocalDate -> Int
diffInMonths fromDate toDate =
Date.diff Months fromDate toDate
{-| Find the number of years between the given dates.
-}
diffInYears : LocalDate -> LocalDate -> Int
diffInYears fromDate toDate =
Date.diff Years fromDate toDate
{-| Add the given days to a given date.
-}
addDays : Int -> LocalDate -> LocalDate
addDays count date =
Date.add Days count date
{-| Add the given weeks to a given date.
-}
addWeeks : Int -> LocalDate -> LocalDate
addWeeks count date =
Date.add Weeks count date
{-| Add the given months to a given date.
-}
addMonths : Int -> LocalDate -> LocalDate
addMonths count date =
Date.add Months count date
{-| Add the given years to a given date.
-}
addYears : Int -> LocalDate -> LocalDate
addYears count date =
Date.add Years count date
{-| Create a date from a [calendar date][gregorian]: a year, month, and day of
the month. Out-of-range day values will be clamped.
import Morphir.SDK.LocalDate exposing (fromCalendarDate, Month(..))
fromCalendarDate 2018 September 26
[gregorian]: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
-}
fromCalendarDate : Int -> Month -> Int -> LocalDate
fromCalendarDate y m d =
Date.fromCalendarDate y (monthToMonth m) d
{-| Create a date from an [ordinal date][ordinaldate]: a year and day of the
year. Out-of-range day values will be clamped.
import Morphir.SDK.LocalDate exposing (fromOrdinalDate)
fromOrdinalDate 2018 269
[ordinaldate]: https://en.wikipedia.org/wiki/Ordinal_date
-}
fromOrdinalDate : Int -> Int -> LocalDate
fromOrdinalDate y d =
Date.fromOrdinalDate y d
{-| Construct a LocalDate based on ISO formatted string. Opportunity for error denoted by Maybe return type.
-}
fromISO : String -> Maybe LocalDate
fromISO iso =
Date.fromIsoString iso |> Result.toMaybe
{-| Convert a LocalDate to a string in ISO format.
-}
toISOString : LocalDate -> String
toISOString localDate =
Date.toIsoString localDate
{-| Construct a LocalDate based on Year, Month, Day. Opportunity for error denoted by Maybe return type.
Errors can occur when any of the given values fall outside of their relevant constraints.
For example, the date given as 2000 2 30 (2000-Feb-30) would fail because the day of the 30th is impossible.
-}
fromParts : Int -> Int -> Int -> Maybe LocalDate
fromParts yearNumber monthNumber dayOfMonthNumber =
-- We do all of this processing because our Elm Date library accepts invalid values while most other languages don't.
-- So we want to maintain consistency.
-- Oddly, Date has fromCalendarParts, but it's not exposed.
let
maybeMonth =
if monthNumber > 0 && monthNumber < 13 then
Just (Date.numberToMonth monthNumber)
else
Nothing
in
maybeMonth
|> Maybe.map
(\m ->
( m, Date.fromCalendarDate yearNumber m dayOfMonthNumber )
)
|> Maybe.map
(\( dateMonth, date ) ->
if Date.year date == yearNumber && Date.month date == dateMonth && Date.day date == dayOfMonthNumber then
Just date
else
Nothing
)
|> Maybe.withDefault Nothing
{-| Returns the year as a number.
-}
year : LocalDate -> Int
year localDate =
Date.year localDate
{-| Returns the month of the year for a given date.
-}
month : LocalDate -> Month
month localDate =
case Date.month localDate of
Time.Jan ->
January
Time.Feb ->
February
Time.Mar ->
March
Time.Apr ->
April
Time.May ->
May
Time.Jun ->
June
Time.Jul ->
July
Time.Aug ->
August
Time.Sep ->
September
Time.Oct ->
October
Time.Nov ->
November
Time.Dec ->
December
monthToMonth : Month -> Time.Month
monthToMonth m =
case m of
January ->
Time.Jan
February ->
Time.Feb
March ->
Time.Mar
April ->
Time.Apr
May ->
Time.May
June ->
Time.Jun
July ->
Time.Jul
August ->
Time.Aug
September ->
Time.Sep
October ->
Time.Oct
November ->
Time.Nov
December ->
Time.Dec
{-| The day of the month (1–31).
-}
day : LocalDate -> Int
day localDate =
Date.day localDate
{-| Returns the day of week for a date.
-}
dayOfWeek : LocalDate -> DayOfWeek
dayOfWeek localDate =
case Date.weekday localDate of
Time.Mon ->
Monday
Time.Tue ->
Tuesday
Time.Wed ->
Wednesday
Time.Thu ->
Thursday
Time.Fri ->
Friday
Time.Sat ->
Saturday
Time.Sun ->
Sunday
{-| Returns true if the date falls on a weekend (Saturday or Sunday).
-}
isWeekend : LocalDate -> Bool
isWeekend localDate =
case dayOfWeek localDate of
Saturday ->
True
Sunday ->
True
_ ->
False
{-| Returns true if the date falls on a weekday (any day other than Saturday or Sunday).
-}
isWeekday : LocalDate -> Bool
isWeekday localDate =
not (isWeekend localDate)
{-| Type that represents a day of the week.
-}
type DayOfWeek
= Monday
| Tuesday
| Wednesday
| Thursday
| Friday
| Saturday
| Sunday
{-| Gregorian calendar months in English.
-}
type Month
= January
| February
| March
| April
| May
| June
| July
| August
| September
| October
| November
| December
{-| Construct a LocalDate from Integer Rata Die, a system for system for assigning calendar days to
numbers, with 1 representing 0001-01-01.
-}
fromRataDie : Int -> LocalDate
fromRataDie rataDieNumber =
Date.fromRataDie rataDieNumber
{-| Convert a LocalDate to its number representation in Rata Die. Rata Die is a system for
assigning calendar days to numbers, with 1 representing 0001-01-01.
-}
toRataDie : LocalDate -> Int
toRataDie localDate =
Date.toRataDie localDate