Language Change Specification for Date and Time

LCS Number: LCS-2016-11
Version: 2
Date: 17-Nov-2016
Author: Rob Gaddi
Email: RobGaddi
Source Doc: DateTime
Summary: Provide system date/time mechanism in std.env

Voting Results: Cast your votes here


  1. Morten Zilmer - 2016-11-18
  2. Martin Zabel - 2016-11-18
  3. Thomas Preusser - 2016-11-21
  4. Kevin Jennings - 2016-11-22
  5. Ryan Hinton - 2016-12-14 - Version 1
  6. Jim Lewis - 2016-12-17 - Version 1
  7. Brent Hayhoe - 2016-11-20 - Version 1
  8. Rob Gaddi - 2017-01-17 - Version 2
  9. Daniel Kho - 2017-01-19 - Version 2
  10. Patrick Lehmann - 2017-01-19 - version 2



Details of Language Change

Changes are shown in red font.

Section 16.5 Standard environment package

Bottom page 274

package ENV is

procedure STOP;

procedure FINISH;


type DAYOFWEEK is (

-- Calendar date/time, broken into parts.  Second accomodates both
-- single and double leap-seconds.  Dayofyear accomodates leap days.
-- Month 0 is January, 1 is February, 11 is December. Year is absolute
-- in AD, 1900 represents the year 1900.
type TIME_RECORD is record
 microsecond : INTEGER range 0 to 999_999
 second : INTEGER range 0 to 61;
 minute : INTEGER range 0 to 59;
 hour : INTEGER range 0 to 23;
 day : INTEGER range 1 to 31;
 month : INTEGER range 0 to 11;
 year : INTEGER range 1 to 4095;
 weekday : DAYOFWEEK;
 dayofyear : INTEGER range 0 to 365;
end record TIME_RECORD;

-- Current local time broken into parts.
-- Minimum legal resolution is 1 second.
impure function LOCALTIME return TIME_RECORD;

-- Current UTC time broken into parts.
-- Minimum legal resolution is 1 second.
impure function GMTIME return TIME_RECORD;

-- Number of seconds since midnight, Jan 1 1970, UTC.
-- Minimum legal resolution is 1 second.
impure function EPOCH return REAL;

-- Time conversion functions from epoch time.

-- Time conversion function from time in parts.
-- EPOCH and GMTIME accept TREC in local time.
-- LOCALTIME accepts TREC in UTC.
function EPOCH(TREC: TIME_RECORD) return REAL;

-- Time increment/decrement.  DELTA argument is in seconds.
-- Returned TIME_RECORD is in local time or UTC per the TREC.

-- Time difference in seconds.  TR1, TR2 must both be in local
-- time, or both in UTC.
function "-"(TR1, TR2: TIME_RECORD) return REAL;

-- Conversion between real seconds and VHDL TIME.  SECONDS_TO_TIME
-- will cause an error if the resulting REAL_VAL would be less than
-- TIME'LOW or greater than TIME'HIGH.

-- Convert TIME_RECORD to a string in ISO 8601 format.
-- TO_STRING(x)    => "1973-09-16T01:03:52"
-- TO_STRING(x, 6) => "1973-09-16T01:03:52.000001"
function TO_STRING(TREC: TIME_RECORD, FRAC_DIGITS: INTEGER range 0 to 6 := 0) return STRING;

end package ENV;
Middle page 275

The function RESOLUTION_LIMIT returns the value of the resolution limit (see

The function EPOCH translates a TIME_RECORD representing local time into so-called "epoch time", i.e. the number of seconds since midnight, Jan 1 1970 UTC, or when called without arguments returns the current time in the same form. The minimum resolution allowed is one second.

The function LOCALTIME translates an epoch time into a TIME_RECORD representing a time in the local timezone, translates a TIME_RECORD representing a time in UTC into one in the local timezone, or when called without arguments returns a TIME_RECORD representing the current time in the local timezone. Local timezone is determined in a manner determined by the host system. The minimum resolution allowed is one second.

The function GMTIME translates an epoch time into a TIME_RECORD respresenting a time in UTC, translates a TIME_RECORD representing a time in the local timezone into one in UTC, or when called without arguments returns a TIME_RECORD representing the current time in UTC. The minimum resolution allowed is one second.

Objects of type TIME_RECORD support addition and subtraction of numbers of seconds as represented by REAL numbers, and the difference between two TIME_RECORDS can be taken as a number of seconds, represented as a REAL.

The TO_STRING operation returns the string representation (see 5.7) of the value of its actual parameter of type TIME_RECORD. The resulting string will specify the time in ISO 8601 format and will consist of the concatenation of the following, in order:

  • The YEAR element expressed in 4 decimal digits
  • A '-' character
  • The MONTH element + 1 expressed in 2 decimal digits (01 through 12)
  • A '-' character
  • The DAY element expressed in 2 decimal digits
  • A 'T' character
  • The HOUR element expressed in 2 decimal digits
  • A ':' character
  • The MINUTE element expressed in 2 decimal digits
  • A ':' character
  • The SECOND element expressed in 2 decimal digits
  • The MICROSECOND element expressed in a manner defined by the DIGITS parameter.

The DIGITS parameter specifies the number of digits that the MICROSECOND element will be truncated to. If the DIGITS parameter is zero (the default) then the MICROSECOND element will be absent in the resultant string. If DIGITS is 1-6, the MICROSECOND element will appear as a '.' character followed by DIGITS of the most significant decimal digits

The function TIME_TO_SECONDS translates objects of type TIME into a REAL approximation of the number of seconds. TIME_TO_SECONDS(1 sec) returns REAL'(1.0).

The function SECONDS_TO_TIME translates a REAL number of seconds into an approximation in type TIME. SECONDS_TO_TIME(1.0) returns TIME'(1 sec). Neither TIME_TO_SECONDS(SECONDS_TO_TIME(x)) nor SECONDS_TO_TIME(TIME_TO_SECONDS(x)) is guaranteed to yield an output that is exactly equal to the input.

Notes 1-3 are unchanged

Note 4 - Simulators should return the current system time whenever the EPOCH, LOCALTIME, or GMTIME functions are called without argument. Synthesis tools should return the time of compilation, which may be treated as a constant.

-- RobGaddi - 2016-11-17


Why is 'day' a 1-31 integer range but month is 0-11 range? -- Kevin Jennings

-- Kevin Jennings - 2016-11-17

Because that's what C time.h does, and I'm a slave to tradition. Although I have changed the definition of year to be absolute rather than based on 1900.

-- Rob Gaddi - 2016-11-17

Does the EPOCH() function return the number of seconds between the current local time and 1970-01-01T00:00:00+00:00 or between the current UTC time and 1970-01-01T00:00:00+00:00? For example, what is returned if the current time is 1970-01-01T02:00:00+01:00?

-- Martin Zabel - 2016-11-18

1970-01-01T02:00:00+01:00, 1970-01-01T01:00:00+00:00, and 1969-12-31T17:00:00-8:00 all occur simultaneously at 3 different points on the globe. EPOCH() will tell them all the same thing, that the time is 3600.0

-- Rob Gaddi - 2016-11-18

Small concern over the increment/decrement - 1us (1e-6) is not exactly representable as a double (and hence very likely as a REAL) - if I increment by 1e-6 a few million times, will we 'miss a tick'? The record representation implies "perfect" tracking of time, which conflicts with the "REAL" approximation(!)

Perhaps inc and dec could be done in terms of 2 TIME_RECORDs? Or in terms of the existing time-type: variable t : time_record := (some initialiser); t := t + 1 us; t := t + 1 s; t := t + 1 ns; -- error? Should the fractional part be smaller? Windows already claims a sub-us precision time of day ( for example

-- Martin Thompson - 2016-11-18

Don't forget that subprograms without parameters have no parenthesis in VHDL! () doesn't exist.

-- Tristan Gingold - 2016-11-18

The TIME_RECORD is great! I am not so in favor of the REAL-based offset computations though. Should we not just use the existing TIME type for this? If REAL should be mandated by some practical considerations, I would nonetheless like to see TIME_RECORD +/- TIME functions added.

-- Thomas Preusser - 2016-11-21

Epoch times are REAL not TIME because the resolution of TIME is sliding; it can be as little as 1 fs, which means that even if you had guaranteed 64-bit TIME you'd still have a maximum of about 9000 seconds, which doesn't even get you out of Jan 1, 1970.

They're not INTEGERs because we only have a guarantee of signed 31 bits for INTEGER, which gives you the well known epoch problem in 2038. I don't want to make a language change that MAY start seeing implementation in 2018 with a timebomb in only 20 years. 64-bit INTEGERs would fix that, and have been proposed, but aren't a done deal.

REALs are 64-bit floats, which can store up to a 53-bit integer. If you don't need all those bits for the integer part then you can start giving some up for fractional parts. For any of our lifetimes, this will mean at least microsecond resolution. But the point isn't the subsecond resolution, it's getting enough bits for the seconds. The subseconds just come along for free, and if you use REALs are impossible to make people not use.

-- Rob Gaddi - 2016-11-25

We actually discussed adding TIME_RECORDs on last week's call, and nixed the idea. We'd need a different type of record: a TIMEDELTA_RECORD with signed fields and no dayofweek or dayofyear.

The problem with trying to work with calendar time is that it's fundamentally hard. What seems like it should be trivial to calculate keeps turning out to have leap things and strange length months and time zones.

As written now, everything is implementable by calls to the standard C library, which on most platforms treats time_t as a 64 bit integer, which we don't have a concept of in VHDL so we'll use REALs instead. They're exact so long as you're working with 1 second as your minimum resolution.

The only reason I bothered to add the conversions between VHDL time and calendar time at all is that they just felt like they should be there. The arithmetic is only really there to perform manual timezone adjustments. Maybe all that and a mathable TIMEDELTA_RECORD should all go off in an open source package instead. That would be a lot more nimble than anything baked into the standard, and any of that is going to be implemented in pure VHDL anyhow. Keep the things in std.env to only those things that will be implemented outside of the language.

While we're at it, maybe we should strike the INTEGER microsecond field and replace it with a REAL subsecond range 0 to 1. That's always going to be a weird non-decimal-perfect part coming in and out of epoch seconds anyhow. Everyone knows floating point gets wonky, an argument could definitely be made for just acknowledging that and rubbing the user's face in it rather than faking precision that may not be available.

-- Rob Gaddi - 2016-11-25

As a proud pedant, I thought that I'd get in before anyone else. As the year is now absolute, does it take account of the missing calendar days (e.g. 1752 for Britain) when people thought part of their lives had been stolen! More importantly and less trivial is: as the 'year' is integer based 0 to 4095, when is year zero - 1 AD or 1 BC? Which leads to: how is 2016 represented internally in the 'year' element, 2015 or 2016?

-- Brent Hayhoe - 2016-12-20

What if at least one integer LCS proposal is successful? Should we reconsider the real value? I would like to see 2016 as 2016 -> no need for biasing. Time and date calculations should be non IEEE and not POSIX based. There are better date/time implementations smile

-- Patrick Lehmann - 2016-12-31

Based on several comments, I've updated: year to begin in 1 rather than 0, the TIME_RECORD comments to more fully describe the month and year, and the argument-free functions to remove the illegal empty (). I'm considering this to be a primarily typo-correcting change, and not rolling the revision.

-- Rob Gaddi - 2017-01-10

Edit | Attach | Print version | History: r29 < r28 < r27 < r26 < r25 | Backlinks | Raw View | Raw edit | More topic actions...
Topic revision: r26 - 2017-03-16 - 11:08:55 - BrentHahoe
Copyright © 2008-2021 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback