Time resolution

In the following code snippet:

template Asset
  with
    issuer : Party
    owner  : Party
    name   : Text
    timeOfCreation: Time
    timeOfCreationText: Text
  where
    ensure name /= ""
    signatory issuer
    observer owner
    choice Give : ContractId Asset
      with newOwner : Party
      controller owner
      do
        now <- getTime
        let newTime = addRelTime now (convertMicrosecondsToRelTime 23123)
        create this with owner = newOwner; timeOfCreation = newTime; timeOfCreationText = show newTime

timeOfCreationText shows microsecond resolution on the Navigator when I add the 23123 microseconds to now , whereas it shows millisecond resolution without this line.

  1. Am I correct in my understanding that Time in Daml can support upto microsecond resolution?
  2. How do we extract in Daml the day, hour, minute, second, millisecond and microsecond values from a Time value?
1 Like

Hi @meet ,

  1. Please see Time — Daml SDK 2.0.0 documentation (ledger-time and record-time), it states there that time is recorded in microsecond resolution. The Built-in types table here Reference: data types — Daml SDK 2.0.0 documentation also shows that Time has microsecond precision.

  2. There is a convertRelTimeToMicroseconds function you can use to convert the time to microseconds. You could then use mod microseconds 1000 to get the microseconds part. The number of hours, minutes, seconds in a time can be calculated by dividing the microseconds respectively by 3.600.000.000, 60.000.000, 1.000.000. The number of days by dividing by 86.400.000.000. (I’m not aware of easy helper functions for this, please correct me if there are)

3 Likes

Thank you @Raymond_Roestenburg!

Re #2, shouldn’t I mod by 100000 to get the microseconds :thinking:

Also, since convertRelTimeToMicroseconds converts RelTime not Time into microseconds, would the following be the right way to go about it?

now <- getTime
let 
   nowRelTime = subTime now (time (date 1970 Jan 1) 0 0 0)
   nowInMicroseconds = convertRelTimeToMicroseconds nowRelTime
   onlyTheMicroseconds = nowInMicroseconds - (floor ((intToDecimal nowInMicroseconds)/100000.0)) * 100000

If you want a separate milliseconds part and microseconds part, you should mod 1000 for just the microseconds remainder. (the remainder of microseconds separately from the number of milliseconds). I might have assumed incorrectly that you wanted to separate milliseconds and microseconds parts.

There is DA.Time.wholeDays, but there doesn’t seem to be an equivalent for other units.

1 000 000.

This is a bit sad but it does look like the only possible approach at the moment given that Time is opaque.

You could replace (date 1970 Jan 1) with (DA.Date.toDateUTC now) to avoid a magic number.

Alternatively, you could go through show, but that’s definitely not better:

toMs : Time -> Int
toMs time =
  case DA.Text.splitOn "." (show time) of
    [] -> error "can't happen, splitOn always returns at least 1 element"
    [_] -> 0 -- No ms part printed
    [_, ms] -> foldl f 0 $ zip [100000, 10000, 1000, 100, 10, 1] (DA.List.dropSuffix ["Z"] $ DA.Text.explode ms)
    _ -> error "can't happen, no more than one . in a date"
  where f acc (m, c) = acc + m * case c of
          "0" -> 0
          "1" -> 1
          "2" -> 2
          "3" -> 3
          "4" -> 4
          "5" -> 5
          "6" -> 6
          "7" -> 7
          "8" -> 8
          "9" -> 9
          _ -> error "can't happen"

I don’t think this is safe. Don’t you need to account for leap seconds and such?

Again I don’t think this is a good idea - I haven’t looked at the implementation, but there can be a danger of overflow here. I.e. x number of microseconds in a 32 or 64 bit value max. I’m not certain what guarantees there are that this won’t change.

Fair points!

RelTime is a duration, not a time, so leap seconds don’t apply. It quite literally is a number of milliseconds.

Whether the conversion from Time to RelTime is safe is a different matter, which is partly why I suggested converting after removing the same date, which would leave you with just the “time of day” and thus a maximum of 8.6e10 which very comfortably fits in 64 bits. This also avoids leap years and leap seconds. Plus, bear in mind that the question was how to get the milliseconds, so seconds, leap or not, are basically ignored.

I’m not sure what the internal representation of Time is, but on the JSON conversion side we limit the range to 0000-01-01T00:00:00Z to 9999-12-31T23:59:59.999999Z, which, assuming we start at 0, still only reaches ~3.2e17, compared to 9.22e18 for a signed, 64-bits integer, so I wouldn’t worry about overflow.

1 Like