[PUP-5871] Add Timestamp and Timespan to Puppet type system Created: 2016/02/10  Updated: 2018/08/13  Resolved: 2016/09/02

Status: Closed
Project: Puppet
Component/s: None
Affects Version/s: None
Fix Version/s: PUP 4.8.0

Type: Improvement Priority: Normal
Reporter: Henrik Lindberg Assignee: John Duarte
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
is blocked by PUP-6647 Function dispatch taking zero argumen... Closed
relates to PUP-6686 Timespan sometimes parses fractions i... Closed
relates to PUP-6689 Timespan and Timestamp should inherit... Closed
relates to PUP-6688 Update language specification with Ti... Resolved
relates to PUP-6681 Add documentation for Timespan.new an... Closed
relates to PUP-6685 Remove underscore from Timespan/Times... Closed
Acceptance Criteria:
  • That Timestamp describes a point in time
  • That Timespan describes a duration
  • That the max resolution of the types are nano seconds (in practice, the resolution depends on the platform)
  • That arithmetic can be performed on time values
  • That time values can be compared
  • That the types allow constraining the acceptable values
Epic Link: 4.x Type System
Story Points: 2
Sprint: Language 2016-08-24, Language 2016-09-07
Release Notes: New Feature
Release Notes Summary: The type system has been extended with two types that helps working with time related values. The Timestamp type is a point in time, and the Timespan is a duration. Both support nano second resolution if available on the platform. Values can be created with the new function and multiple time formats are supported. Arithmetic and comparison operations can be performed on time values.


Timestamp and Timespan are very useful types to have. They should be represented in the Puppet type system. We will make design Pcore serialization so that these types can be serialized with a precise definition.

Both are expressed in nano seconds. Timestamp is a timespan since the Epoch (i.e. 1970::01::01:00::00::000....). Both values are signed; for Timestamp a negative value is before the Epoch.

The runtime objects for these types should respond to normal arithmetic. (Exception you cannot add or multiply two Timstamps, but all other combinations and operators work except shift).

The default string format is ISO 8601 with ms - i.e "YYYYMMDDThh:mm:ss.sss UTC", and the default for timespan is a variation of ISO 8601 "nYnMnDTnHnMnSn.sss" where the YMDTHS may be given in lower case. The M ambiguity is resolved by, if there is only one M i lower case and no 'T' then it denotes minutes otherwise 'T' must be used and an m/M before T is months and minutes if after.

Our default output should use lower case as that is more readable.

We also need to add a formatting function, we could possibly extend sprintf. And we need to be able to create these types from both String and Numeric representation. The formatting is a separate ticket; that should be added when planning work on this ticket.

Comment by Thomas Hallgren [ 2016/08/16 ]

I think the arithmetic for Timestamp should be limited so that multiplication, division, and addition is illegal. They all require a fixed starting point which in turn opens up a can of worms since you need to consider regional differences. And even if we do sort that out (or simply claim that it's the Epoch), what's the point in allowing it? I find no viable use-case. This means that the only possible arithmetic involving Timestamp is:

Timestamp + Timespan = Timestamp
Timestamp - Timespan = Timestamp
Timestamp - Timestamp = Timespan

Comment by Thomas Hallgren [ 2016/08/16 ]

I think the timespan format must be simplified to only reflect days, hours, minutes, seconds, milliseconds, and nanoseconds. Reason being that if we mix in year, month, and day in month, it gets complicated since leap years will make their values differ depending on when the timespan is applied. If the largest digit signifies days, we don't have that problem.

Comment by Thomas Hallgren [ 2016/08/16 ]

Regarding format characters.

I'd like to avoid using the ISO-8601 way of describing formats since there's no implementation backing it. Instead, I'd like to use the commonly used formats for strftime (Ruby, C++).

  • %a – Abbreviated weekday (Sun.)
  • %A – Full weekday (Sunday)
  • %b – Abbreviated month (Jan.)
  • %B – Full month (January)
  • %d – Day of Month [01,31]
  • %e – Day of Month [1,31]
  • %h – Same as %b
  • %H – 24 clock hour [00,23]
  • %I – 12 clock hour [01,12]
  • %j – Day of year [1,366]
  • %m – Month [01,12]
  • %M – Minute [00,59]
  • %n – New Line
  • %p – AM/PM in locale representation
  • %r – Time with AM/PM, same as %I:%M:%S %p
  • %R – Same as %H:%M
  • %S – Second [00,61]
  • * %L - Millisecond of the second
  • %N - Fractional seconds digits. Default is nanoseconds but it can also be written as %3N (milliseconds), %6N (microseconds) %9N (nanoseconds).
  • %t – Tab character
  • %T – Same as %H:%M:%S
  • %y – Year [00,99]
  • %Y – 4 digits year. (2009)
  • %Z – Time Zone
  • %% – Percent symbol

For Timespan I propose the following format characters

  • %D - Number of days
  • %H - Number of hours (can't have 12 hour am/pm here, since the actual time is unknown)
  • %M - Minute of the hour
  • %S - Second of the minute
  • %L - Millisecond of the second
  • %N - Fractional seconds digits. Default is nanoseconds but it can also be written as %3N (milliseconds), %6N (microseconds) %9N (nanoseconds).
  • %% – Percent symbol

I'd like us to avoid lower case letters since they conflict with format characters commonly used by various DateTime implementations.

Comment by Henrik Lindberg [ 2016/08/17 ]
  • Using the formats from strftime is fine.
    • How does the proposal that we use days as the largest "digit" affect this? Was that only for the default?
  • What does "avoid smallcaps" mean? Did you mean avoid "lower case" ? ("smallcaps" is a term for captial letters set at the height of lower case letters) - confused... Can you elaborate?
Comment by Thomas Hallgren [ 2016/08/22 ]

I meant lower case letters, not smallcaps.

Comment by Thomas Hallgren [ 2016/08/22 ]

Regarding "largest digit", I think the largest format character that is present will represent a total rather than "something of something else". Let's assume that we have a timespan of 7 days, 2 hours, 25 minutes and 3 seconds:

x.strftime('%D-%H:%M:%S') => 7-02:25:03
x.strftime('%H:%M:%S') => 170:25:03
x.strftime('%M:%S') => 10225:03
x.strftime('%S') => 613503
x.strftime('%L') => 613503000


Comment by Henrik Lindberg [ 2016/08/22 ]

The timespan and "largest format character" makes sense. I now understand what you mean by not formatting by unit > day as those are problematic (except possibly week (which may not even exist in stftime) as they need a calendar/date to make sense.

Still do not understand what "avoid lower case letters" means.

Comment by Thomas Hallgren [ 2016/08/22 ]

It's the simplest explanation of them all. I don't want us to use the characters 'd', 'h', 'm', or 'l', since they conflict with characters used by strftime on Timestamp.

Comment by Henrik Lindberg [ 2016/08/22 ]

ok - now I understand - the comment was just a motivation for the choice of (upper case) format characters for TimeSpan.

Comment by Thomas Hallgren [ 2016/08/29 ]

Formatting was added as part of this ticket since it makes writing tests a lot easier and since formatter and parser for Timespan share 80% of the code.

Comment by Henrik Lindberg [ 2016/08/30 ]

Merged to master at: 1850ba9

Comment by John Duarte [ 2016/09/02 ]

These functions are tested quite robustly via the spec tests.
A sanity check was performed on Windows 2012r2 to ensure that there were no fundamental surprises on that platform.

Using puppet-agent at SHA e15c8b5 containing puppet at SHA 78e897b the sanity check passed on Windows 2012r2.

Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
C:\cygwin64\home\Administrator>type test.pp
type test.pp
$t0 =Timestamp.new.strftime("%N"); notice("Timestamp formatted in %N: $t0")
$t1 =Timestamp.new.strftime("%F"); notice("Timestamp formatted in %F: $t1")
$t2 =Timestamp.new.strftime("%c"); notice("Timestamp formatted in %c: $t2")
$t3 =Timespan({ hours => 3, minutes => 20, seconds => 30}); notice("Timespan: $t3")
$t4 =Timespan({ hours => 3, minutes => 20, seconds => 30}).strftime("%H:%M:%S"); notice("Timespan formatted in %H:%M:%S : $t4")
$t5 =(Timespan({days => 3}) + Timespan({hours => 12})); notice("Timespan additon: $t5")
C:\cygwin64\home\Administrator>puppet apply test.pp
puppet apply test.pp
Notice: Scope(Class[main]): Timestamp formatted in %N: 056160000
Notice: Scope(Class[main]): Timestamp formatted in %F: 2016-09-02
Notice: Scope(Class[main]): Timestamp formatted in %c: Fri Sep  2 19:12:50 2016
Notice: Scope(Class[main]): Timespan: 0-03:20:30
Notice: Scope(Class[main]): Timespan formatted in %H:%M:%S : 03:20:30
Notice: Scope(Class[main]): Timespan additon: 3-12:00:00
Notice: Compiled catalog for xn0ppo8t8yw4jig.delivery.puppetlabs.net in environment production in 0.11 seconds
Notice: Applied catalog in 0.04 seconds

Generated at Thu Nov 14 00:38:13 PST 2019 using JIRA 7.7.1#77002-sha1:e75ca93d5574d9409c0630b81c894d9065296414.