I started using the CalendarLib library and while it will definitely make do for my needs, I discovered something strange, which I want to discuss here. Maybe there is something I don’t understand, maybe this behaviour is wrong.
I discovered that if I use Date.add
to add one month to a date, if my initial date is at the end of a long month, I may end up not in the next month, but one month after that, for example
utop # let start = Date.make 2018 1 31 in
Date.add start (Date.Period.month 1) |> Printer.Date.sprint "%F";;
- : string = "2018-03-03"
Because I need a slightly different behavior, I implemented this for my own use:
module DateUtil = struct
open CalendarLib
let to_string date = Printer.Date.sprint "%F" date
let first_of_month date =
Date.(make (year date) (int_of_month (month date)) 1)
(* Makes sure to stay within the boundaries of the next month.
So if called on 2018-01-31, it will return 2018-02-28 *)
let plus_one_month date =
let open Date in
let one_month = Period.month 1 in
let first_of_this_month = first_of_month date in
let first_of_next_month = add first_of_this_month one_month in
let last_of_next_month =
make (year first_of_next_month)
(int_of_month (month first_of_next_month))
(days_in_month first_of_next_month)
in
let after_one_month = add date one_month in
if compare after_one_month last_of_next_month > 0 then last_of_next_month
else after_one_month
end
When called with 2018-01-31 it works like this:
utop # DateUtil.to_string (DateUtil.plus_one_month (CalendarLib.Date.make 2018 1 31));;
- : string = "2018-02-28"
But then I noticed the odd thing… CalendarLib doesn’t behave like above when called from the beginning of the month:
utop # let start = Date.make 2018 2 1 in
Date.add start (Date.Period.month 1) |> Printer.Date.sprint "%F";;
- : string = "2018-03-01"
If it were consistent, I would expect to get something like “2018-03-04”…
So, what’s going on here? Are there some rules about date arithmetic that I’m not aware of?