Dealing with time

The datatype for time in Shyft is just a number, and with the SI-unit [s] (Second).

This is similar to for example distance, measured in SI-unit [m] (Meter).

So that the well known equation:

\[velocity = \frac{distance}{time}\]

Can be naturally expressed, with SI-units. From this it also follows that time can be used as any number, and adheres to the rules of a number.

The internal representation in c++ is std::chrono::microseconds, which is a high performance 64bit integer representation of time up to microsecond resolution.

When using python float/int are automagically converted to the internal representation.

Time as in the context of when values or entities are observed, related to for example measurements, is then understood as the distance in [s] from a well known point in time, the Epoch.

The Calendar class takes care of converting to/from calendar units (e.g. year,month,day,hour..), and time. It also provides functions to do calendar artihmethics, like adding a number of logical calendar months to a time.

UTC / GMT and DST

Coordinated Universal Time (UTC) was introduced as the more accurate replacement of Greenwich Mean Time (GMT). In 1963, the concept of UTC was established as the primary international standard which would denote how other countries would regulate their time in relation to UTC.

The primary reason why UTC was considered to be a more accurate system was the fact that it used the rotation of Earth and atomic clocks for measurements. Moreover, to maintain the consistent time system, UTC does not observe Daylight Saving Time (DST), the shifting of time by an hour in summer and winter so that darkness falls at a later clock time.

Although GMT and UTC share the same current time in practice, there is a basic difference between the two:

  1. GMT is a time zone officially used in some European and African countries.

  2. UTC is not a time zone, but a time standard that is the basis for civil time and time zones worldwide. This means that no country or territory officially uses UTC as a local time.

Neither UTC nor GMT ever change for Daylight Saving Time (DST). However, some of the countries that use GMT switch to different time zones during their DST period. For example, the United Kingdom is not on GMT all year, it uses British Summer Time (BST), which is one hour ahead of GMT, during the summer months.

UTC represents absolute time and therefore should be used for all program internal date/time representations.

For more information, please check Wikipedia - Coordinated Universal Time.

Epoch Timestamp

Date and time are very difficult to deal with correctly. Instead of using a datetime object, it is easier to use Unix time (also known as Epoch time, POSIX time, seconds since the Epoch or UNIX Epoch time) which is represented as a a number.

The Epoch timestamp is the number of seconds that have elapsed since the Unix epoch, minus leap seconds. The Unix epoch is 1970-01-01 00:00:00.0 UTC.

Within Shyft, time is represented as number, with fixed digits expressed down to microseconds.

For more information, please check Wikipedia - Epoch (computing).

Python datetime vs. epoch

The following example outlines the issues when dealing with Python native datetime objects.

# -*- coding: utf-8 -*-
"""
This script contains an example showing the complexity of correctly handling
timezone and UTC using Python datetime objects.
"""

from datetime import datetime

from pytz import utc, timezone

# Express this point in time in Python datetime
# Most often, users leave out the time zone information, which is ambiguous.
my_day = datetime(2018, 3, 15)
print(f"{str(my_day)}       is tz unaware, but is assumed to be your local computer tz!")
# this is converted to the UTC based epoch timestamp
my_day_ts = my_day.timestamp()
print(f"{str(my_day)}       in your local computer tz as UTC epoch timestamp: {my_day_ts}")

print("===================")
# EXAMPLE: Convert timestamp back to datetime
print("WARNING: The reverse operation also depends on your local computer tz!")
# convert into tz unaware object, but UTC values
a = datetime.fromtimestamp(my_day_ts)
print(f"datetime.fromtimestamp(a)       : {str(a)} (in local computer TZ value)")
b = datetime.utcfromtimestamp(my_day_ts)
print(f"datetime.utcfromtimestamp(a)    : {str(b)} (UTC converted from local computer TZ)")
c = datetime.fromtimestamp(my_day_ts, utc)
print(f"datetime.fromtimestamp(a, utc)  : {str(c)} (UTC converted from local computer TZ)")
d = datetime.fromtimestamp(my_day_ts, timezone("America/La_Paz"))
print(f"datetime.fromtimestamp(a, LaPaz): {str(d)} (La Paz converted value, relative to local computer TZ)")
print("WARNING: The timestamp again depends on your local computer tz, if no timezone is defined!")
print(f"{str(a)}       as UTC epoch timestamp: {a.timestamp()}")
print(f"{str(b)}       as UTC epoch timestamp: {b.timestamp()} (!!!!)")
print(f"{str(c)} as UTC epoch timestamp: {c.timestamp()}")
print(f"{str(d)} as UTC epoch timestamp: {d.timestamp()}")

# PITFALL:
print("===================")
print("WARNING: Setting a time zone on an unaware datetime object does not convert!")
my_day_utc = utc.localize(my_day)
print(f"{str(my_day_utc)} the day set as UTC time zone (NOT a conversion!)")
my_day_utc_ts = my_day_utc.timestamp()
print(f"{str(my_day_utc)} as epoch timestamp: {my_day_utc_ts} (!!!!)")

print("===================")
print("The epoch time stamp uniquely identifies a point in time!")
print("Python datetime objects must be time zone aware to do the same and is cumbersome.")

Warning

When DST is applied ins Europe/Oslo, we either move an hour in March from 02:00 to 03:00, missing the hour 02:00 and in October we go back at 03:00 to 02:00, leading to 03:00 happening again an hour later. So, 03:00 in October DST change leads to two exactly identical datetime objects! This cannot be represented correctly with lists of datetime objects!

Shyft uses UTC epoch timestamps internally exclusively, and provides an intelligent Calendar avoiding this issue.

For more information, please check Wikipedia - Daylight saving time.