Civil.Date
A date on the Gregorian calendar, with no time of day and no time zone.
2024-02-29 is a date. It is not a moment in time: it began at different
instants in Auckland and in Denver, and asking when it "was" has no answer
until someone names a time zone. It is a position on the calendar, and that is
useful on its own: a birthday, an invoice date, the day a file is filed under.
Every Date that exists is a real date. fromParts,
fromRataDie and fromString are the only
ways to make one, and all of them check the calendar. 2023-02-29 never
becomes a value, so code that receives a Date never has to check it.
Range
Year 1 through year 9999, which is the range RFC 3339 allows and therefore the range TOML allows. Year zero and negative years are not dates in this module. Dates before 1582 are computed as if the Gregorian calendar had always been in use, which is what RFC 3339 specifies.
A date on the Gregorian calendar.
Two Dates are == exactly when they are the same day.
Creating
A date from a year, a month numbered 1 to 12, and a day numbered from 1.
Date.fromParts { year = 2024, month = 2, day = 29 }
|> Maybe.map Date.toString
--> Just "2024-02-29"
Nothing if the year is outside 1 to 9999, the month outside 1 to 12, or the
day outside the length of that month in that year:
Date.fromParts { year = 2100, month = 2, day = 29 }
--> Nothing
A date from its Rata Die day number. Rata Die is a plain count of days: 0001-01-01 is day 1, and each later day is one more.
Date.fromRataDie 738945 |> Maybe.map Date.toString
--> Just "2024-02-29"
Nothing outside 1 through 3652059, which is 9999-12-31.
Reading
The year, 1 to 9999.
The month, 1 to 12.
The day of the month, counting from 1.
The month as the Time.Month type from Gren's core library, for code that
expects that type.
Date.fromString "2024-02-29" |> Maybe.map Date.monthOf
--> Just Time.Feb
Which day of the week this date fell on.
Date.fromString "2024-02-29" |> Maybe.map Date.weekday
--> Just Time.Thu
The day of the year, 1 to 366.
Date.fromString "2024-03-01" |> Maybe.map Date.ordinalDay
--> Just 61
The Rata Die day number: 0001-01-01 is day 1, and each later day is one
more. Use it to compare dates or to do arithmetic on them;
fromRataDie turns the result back into a date.
Date.fromString "0001-01-01" |> Maybe.map Date.toRataDie
--> Just 1
The calendar
Whether a year has a 29th of February.
Date.isLeapYear 2000
--> True
Date.isLeapYear 2100
--> False
A year is a leap year when it is divisible by 4, unless it is also divisible by 100, unless it is also divisible by 400. So 2000 was a leap year and 2100 will not be.
How many days a month has in a given year.
Date.daysInMonth { year = 2023, month = 2 }
--> 28
Zero for a month outside 1 to 12, so a check of the day against this value also rejects a bad month.
Arithmetic
Move a date forward, or backward with a negative number.
Date.fromString "2024-02-28"
|> Maybe.andThen (Date.addDays 1)
|> Maybe.map Date.toString
--> Just "2024-02-29"
Nothing if the result would fall outside years 1 to 9999.
How many days from the first date to the second. Negative if the second is earlier.
Maybe.map2 Date.difference
(Date.fromString "2024-02-28")
(Date.fromString "2024-03-01")
--> Just 2
Strings
Read a YYYY-MM-DD date.
Date.fromString "1979-05-27" |> Maybe.map Date.ordinalDay
--> Just 147
The whole string has to be the date, each field has to have exactly the number
of digits shown, and the date has to exist on the calendar. All of these are
Nothing:
Date.fromString "1979-5-27" --> Nothing
Date.fromString "01979-05-27" --> Nothing
Date.fromString "1979-05-27T00" --> Nothing
Date.fromString "1979-02-30" --> Nothing
Write a YYYY-MM-DD date, zero padded.
Date.fromParts { year = 7, month = 1, day = 2 }
|> Maybe.map Date.toString
--> Just "0007-01-02"