Skip to content
Discussions/App Development/`day` parameter overflow for the `date` function, simple leap year handlingForum ↗

`day` parameter overflow for the `date` function, simple leap year handling

App Development10 posts611 views20 likesLast activity Sep 2020
GY
gyorgybalazsiOP
Aug 2020

Mod note: As of SDK 1.5.0 Scenarios have been superseded by the more powerful Daml Script. We now recommend using that for all purposes. For more information, and to learn how to use Script please check out @Andreas’ post on our blog.

I’ve just realized and thought it might be interesting for other community members as well that for the date function there is no invalid day parameter because the function handles overflow in the following way:

daml> date 2019 Feb 29
2019-03-01
daml> date 2020 Aug 32
2020-09-01

I came across this behavior when I set out to implement a function which adds years to a date preserving month and day and started to think about leap year handling.

The simple solution for this is that you don’t care about leap years:

addYearsToDate : Int -> Date -> Date
addYearsToDate yearsToAdd initDate = 
  date resultYear initMonth initDay
  where 
    (initYear, initMonth, initDay) = toGregorian initDate
    resultYear = initYear + yearsToAdd

yearsToAddTest = scenario do 
  assert $ isLeapYear 2020
  assert $ addYearsToDate 4 (date 2020 Feb 29) == date 2024 Feb 29
  assert $ addYearsToDate 5 (date 2020 Feb 29) == date 2025 Feb 29
  assert $ addYearsToDate 5 (date 2020 Feb 29) == date 2025 Mar 1

If I add 5 years to 2020 Feb 29, the result is 2025 Mar 1.

BE
bernhard
Aug 2020

DAML internally uses unix time (micros since Unix Epoch). There are some fun looking DAML function to convert between a number of days since epoch and dates: daml/Date.daml at 1872c668a554e2ec7cff8bc8838e3895a253962f · digital-asset/daml · GitHub

I wonder whether these are standard algorithms, or are adapted from some other Standard Library. Does anyone here know the source of all the magic numbers?

GY
gyorgybalazsi
Aug 2020

It turns out that according to Hungarian law, the simple leap year handling cited above is not OK, I had to implement a more complex function for this:

addYearsToDate : Int -> Date -> Date
addYearsToDate yearsToAdd initDate = 
  if (not . isLeapYear) resultYear &&
     initMonth == Feb && 
     initDay == 29 
  then date resultYear Feb 28
  else date resultYear initMonth initDay
  where 
    (initYear, initMonth, initDay) = toGregorian initDate
    resultYear = initYear + yearsToAdd

addingYearsTest = scenario do 
  assert $ isLeapYear 2020
  assert $ addYearsToDate 4 (date 2020 Feb 29) == date 2024 Feb 29
  assert $ addYearsToDate 5 (date 2020 Feb 29) == date 2025 Feb 28
GA
Gary_Verhaegen
Aug 2020

I would consider this a bug. The documentation:

-- Given the three values (year, month, day), constructs a Date
-- value. date (y, m, d) turns the year y, month m, and day d
-- into a Date value.
date: Int -> Month -> Int -> Date

does not mention any kind of “overflow” behaviour, and I would be surprised by it. I would much prefer if it threw an error on invalid inputs.

@bernhard I think we should update either the documentation to mention the overflow behaviour, or the implementation to throw on invalid inputs. Happy to do either.

BE
bernhard
Aug 2020

Yes, makes sense. I guess the easiest way is to convert to check that calling toGregorian on the result of date gives the original inputs. Quite wasteful, though. Mind opening a ticket for discussion?

GY
gyorgybalazsi
Aug 2020

The similar function in Haskell doesn’t overflow, in case of a too big day number returns the last day of the month:

Prelude Data.Time> fromGregorian 2020 12 40
2020-12-31
Prelude Data.Time> fromGregorian 2020 11 40
2020-11-30
GY
gyorgybalazsi
Aug 2020
Gary_Verhaegen:

date (y, m, d)

This is also incorrect in the docs, the above function would be of type

date : (Int, Month, Int) -> Date

which is not the case. The docs should say date y m d turns the year y, month m, and day d into a Date value.

GA
Gary_Verhaegen
Aug 2020

Good catch!

GA
Gary_Verhaegen
Aug 2020

Opened a PR for the minor docstring typo, which I don’t expect to be controversial, and an issue to further discuss how to handle bad inputs.

SO
Sofia_Faro
Sep 2020

I merged a PR to add the missing underflow/overflow check on date:

github.com/digital-asset/daml

Fix missing date under/overflow in DA.Date.date

digital-asset:masterdigital-asset:fix-da-date
opened 12:37PM - 14 Sep 20 UTC
← Back to Discussions