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?