Time-Related Data Types
Rel has support for time-related data types.
Specifically, it comes with the data types Date
and DateTime
.
The Date
data type can only be used for dates whereas DateTime
is a timestamp and includes date, time, and time zone information.
Date
properties, such as date_year
, do not require a time zone.
You can think of Date
as a unit of time, counted out day by day.
By contrast, DateTime
properties, such as datetime_year
,
do require a time zone. This is because you can interpret the 24-hour
time component as relative to a time zone, giving different results for the year/month/day/hour/minute
in some cases.
Parts of the DateTime
value, specifically the seconds, are not time zone dependent.
As a consequence, the relation datetime_second
requires no time zone information.
Date
Calendar date.
Construction
Date
constants can be specified directly in YYYY-MM-DD
format:
// read query
def output = {1776-07-04; 2020-02-29}
The utility parse_date[string, format]
parses a string into a date, using
the same formats accepted by the Julia language (opens in a new tab).
Date
values are also generated when loading a CSV file with a column that has been specified for containing dates.
See CSV Import for details.
Type Relation: Date(x)
Date(x)
tests whether x
has the data type Date
.
// read query
def d = 2021-12-14
// The Date relation is contained in the built-in std::datetime module.
from ::std::datetime import Date
ic { Date(d) }
Noteworthy Operations
Accessors:
See also:
Examples
// read query
def d = parse_date["1616-4-23", "Y-m-d"]
def output:tuple = date_year[d], date_month[d], date_day[d]
def output:monthname = date_monthname[d]
def output:dayname = date_dayname[d]
def output:week = date_week[d]
def output:dayofyear = date_dayofyear[d]
def output:dayofweek = date_dayofweek[d]
To compute the number of days between two dates:
// read query
def d1 = parse_date["2014-1-29", "Y-m-d"]
def d2 = parse_date["2014-2-28", "Y-m-d"]
from ::std::datetime import Day
def output = x : date_add[d1, x] = d2 and Day(x)
DateTime
A point in time, time zone agnostic, with nanosecond resolution. When working with datetimes, do not consider time zones or leap seconds.
Construction
DateTime
types can be specified directly as YYYY-MM-DDThh:mm:ss<timezone>
,
where <timezone>
is Z
, or +
or -
followed by hh:mm
. For more details on time zones, see List of TZ Database Time Zones (opens in a new tab).
For example:
// read query
def output = {
2021-10-12T01:22:31+10:00;
1955-11-12T22:04:00-08:00;
1970-01-01T00:00:00Z
}
They can be parsed from strings with parse_datetime[string, format]
.
// read query
def output = {
parse_datetime["2018-06-12 13:00 +00:00", "YYYY-mm-dd HH:MM zzzz"];
parse_datetime["2018-03-11 01:00 America/New_York", "Y-m-d H:M Z"]
}
Currently, not all ISO DateTime
formats are supported.
For instance, if your DateTime
is specified as y-m-dTH:M:S.sZ
, some workaround is needed:
// read query
def date_string = "2022-01-02T18:07:27.963Z"
def date_trimmed = substring[date_string, 1, num_chars[date_string]-1]
def output = parse_datetime[date_trimmed, "y-m-dTH:M:S.s"]
In this example, the time zone Z
needs to be removed before parsing (y-m-dTH:M:S.s
format is allowed).
Type Relation: DateTime(x)
DateTime(x)
tests whether x
has the data type DateTime
.
// read query
def dt = 2021-10-12T01:22:31+10:00
// The DateTime relation is contained in the built-in std::datetime module.
ic { std::datetime::DateTime(dt) }
Noteworthy Operations
Examples
// read query
def dt = parse_datetime["2021-01-01 01:00:00", "Y-m-d H:M:S"]
def output:one = datetime_year[dt, "Europe/Berlin"]
def output:two = datetime_year[dt, "America/New_York"]
def output:three = datetime_year[dt, "-03:00"]
Note that the resolution of datetime_now
is milliseconds, which will be
sufficient for most use cases:
// read query
@inline
def datetime_to_milliseconds(datetime, v) =
datetime_to_nanoseconds(datetime, ns) and
v = trunc_divide[ns, 10^6]
from ns
def dt = unix_epoch + ^Day[1]
def output = datetime_to_milliseconds[dt]
ic datetime_ic { output = 24 * 60 * 60 * 1000 }
Date and Time Periods
Construction
Time period:
^Nanosecond[n]
, ^Microsecond[n]
, ^Millisecond[n]
, ^Second[n]
, ^Minute[n]
, and ^Hour[n]
.
Date period:
^Day[n]
, ^Week[n]
, ^Month[n]
, and ^Year[n]
.
For all constructors the variable n
is an integer indicating how many multiples of the time/date periods are requested:
// read query
def output {^Second[100]; ^Hour[2]; ^Day[1]; ^Month[24]}
Note that all of time and date period constructors are
contained in the built-in std::datetime
module.
Type Relations
The following unary relations check whether a variable has the specific date-period-like or time-period-like data type.
Time period:
std::datetime::Nanosecond(x)
, std::datetime::Microsecond(x)
, std::datetime::Millisecond(x)
, std::datetime::Second(x)
,
std::datetime::Minute(x)
, and std::datetime::Hour(x)
.
// read query
def R {^Nanosecond[100]; ^Microsecond[10]; ^Minute[1]}
from ::std::datetime import Nanosecond, Minute
def output(x): R(x) and (Nanosecond(x) or Minute(x))
Note that all of time and date period type relations are
contained in the built-in std::datetime
module.
Date period:
std::datetime::Day(x)
, std::datetime::Week(x)
, std::datetime::Month(x)
, and std::datetime::Year(x)
.
// read query
def R {^Day[100]; ^Week[52]; ^Month[1]; ^Year[2000]}
from ::std::datetime import Week, Year
def output(x) = R(x) and (Week(x) or Year(x))
Noteworthy Operations
Examples
Time periods can be added or subtracted from Date
and DateTime
.
Rel follows the Julia period-arithmetic conventions (opens in a new tab).
Adding Year
, Day
, and Second
periods to a Date
or DateTime
is straightforward, since these periods have a well-defined time duration.
Adding Month
periods, on the other hand, is different.
It usually advances to the same date (and time) in the corresponding new month, so that
adding months to the 10th of a month always results in the 10th of another month.
For leap years, however, you can choose the 28th, as adding months to the 29th would give a nonexisting date:
// read query
def output:jan = parse_date["2014-1-29", "Y-m-d"] // Not a leap year
def output:feb = output:jan + ^Month[1]
def output:mar = output:feb + ^Month[2]
You can compare this to:
// read query
def output:jan = parse_date["2016-1-29", "Y-m-d"] // A leap year
def output:feb = output:jan + ^Month[1]
def output:mar = output:feb + ^Month[2]
Note that adding day and month periods is therefore not associative:
// read query
def d = parse_date["2014-1-29", "Y-m-d"]
def output:one = (d + ^Day[1]) + ^Month[1]
def output:two = (d + ^Month[1]) + ^Day[1]
Next: Value Types