# NAME
PDL::DateTime - A piddle for keeping high precision (microsecond) timestamps
# DESCRIPTION
[PDL::DateTime](
https://metacpan.org/pod/PDL%3A%3ADateTime) is a subclass of [PDL](
https://metacpan.org/pod/PDL) piddle for storing date-time values (scalar piddles, vectors, matrices or generally ND-piddles):
- its PDL type is always `LongLong` (64-bit signed integer)
- **stored values are microseconds** since `1970-01-01T00:00:00.000000Z` (can be both positive or negative)
- it is still a piddle so you can do all usual PDL arithmetic + [PDL::DateTime](
https://metacpan.org/pod/PDL%3A%3ADateTime) defines some new methods (see below)
# LIMITATIONS
- supported datetimes are from `0001-01-01T00:00:00.000000Z` (epoch microseconds: `-62135596800000000`) to `9999-12-31T23:59:59.999999Z` (epoch microseconds: `253402300799999999`)
- leap seconds are completely ignored
- no timezone handling (module uses GMT, date time values without offset/timezone are considered to be GMT)
- this module works only on perls with 64-bit integers, check `perl -V:ivsize` (should be `ivsize='8'`)
- no chance for nanoseconds precision, maybe in a separate module e.g. `PDL::DateTime::Ns`
# SYNOPSIS
use 5.010;
use PDL;
use PDL::DateTime;
my $dt_1 = PDL::DateTime->new_sequence('2015-09-20T15:45', 5, 'hour');
say $dt_1;
# [ 2015-09-20T15:45 2015-09-20T16:45 2015-09-20T17:45 2015-09-20T18:45 2015-09-20T19:45 ]
say $dt_1->where($dt_1 > '2015-09-20T17:00');
# [ 2015-09-20T17:45 2015-09-20T18:45 2015-09-20T19:45 ]
say $dt_1->dt_hour;
# [15 16 17 18 19]
say $dt_1->dt_minute;
# [45 45 45 45 45]
say $dt_1->dt_add(year=> 4, month=>6, day=>3);
# [ 2020-03-23T15:45 2020-03-23T16:45 2020-03-23T17:45 2020-03-23T18:45 2020-03-23T19:45 ]
my $dt_2 = PDL::DateTime->new_sequence('2015-11-22T23:23:23.654321', 4, 'day');
say $dt_2;
# [ 2015-11-22T23:23:23.654321 2015-11-23T23:23:23.654321 2015-11-24T23:23:23.654321 2015-11-25T23:23:23.654321 ]
say $dt_2->dt_align('day');
# [ 2015-11-22 2015-11-23 2015-11-24 2015-11-25 ]
say $dt_2->dt_align('hour');
# [ 2015-11-22T23:00 2015-11-23T23:00 2015-11-24T23:00 2015-11-25T23:00 ]
say $dt_2->dt_align('minute');
# [ 2015-11-22T23:23 2015-11-23T23:23 2015-11-24T23:23 2015-11-25T23:23 ]
# FUNCTIONS
## new
my $p = PDL::DateTime->new($pdl_or_array_ref);
# input data = microseconds (LongLong) since 1970-01-01T00:00:00Z (positive or negative)
# input data are always converted to LongLong
## new\_from\_epoch
my $p = PDL::DateTime->new_from_epoch($pdl_or_array_ref);
# BEWARE: precision in miliseconds only!
# input data = seconds (int or double) since 1970-01-01T00:00:00Z (positive or negative)
## new\_from\_ratadie
my $p = PDL::DateTime->new_from_ratadie($pdl_or_array_ref);
# BEWARE: precision in miliseconds only!
# input data = days (int or double) since January 1, 0001 AD 00:00
See [
https://en.wikipedia.org/wiki/Rata\_Die](
https://en.wikipedia.org/wiki/Rata_Die)
## new\_from\_serialdate
my $p = PDL::DateTime->new_from_serialdate($pdl_or_array_ref);
# BEWARE: precision in miliseconds only!
# input data = days (int or double) since January 1, 0000 AD 00:00
See [
http://www.mathworks.com/help/finance/handling-and-converting-dates.html](
http://www.mathworks.com/help/finance/handling-and-converting-dates.html)
## new\_from\_juliandate
my $p = PDL::DateTime->new_from_juliandate($pdl_or_array_ref);
# BEWARE: precision in miliseconds only!
# input data = days (int or double) since November 24, 4714 BC 12:00
See [
https://en.wikipedia.org/wiki/Julian\_day](
https://en.wikipedia.org/wiki/Julian_day)
## new\_from\_datetime
my $p = PDL::DateTime->new_from_datetime($array_ref);
# input data = array of ISO 8601 date time strings
my $dt = PDL::DateTime->new_from_datetime([
[ '2015-09-20T15:45', '2015-09-20T16:45', '2015-09-20T17:45' ],
[ '2016-09-20T15:45', '2016-09-20T16:45', '2016-09-20T17:45' ],
[ '2017-09-20T15:45', '2017-09-20T16:45', '2017-09-20T17:45' ],
]);
Supported formats - see [Time::Moment](
https://metacpan.org/pod/Time%3A%3AMoment#from_string).
## new\_from\_parts
my $p = PDL::DateTime->new_from_parts($y, $m, $d, $H, $M, $S, $U);
# all arguments are either piddles or array refs
# $y .. years (1..9999)
# $m .. months (1..12)
# $d .. days (1..31)
# $H .. hours (0..23)
# $M .. minutes (0..59)
# $S .. seconds (0..59)
# $U .. microseconds (0..999999)
## new\_from\_ymd
my $p = PDL::DateTime->new_from_ymd($ymd);
# BEWARE: handles only dates!
# $ymd (piddle or array ref) with dates like:
# [ 20150831, 20150901, 20150902 ]
## new\_sequence
my $p = PDL::DateTime->new_sequence($start, $count, $unit, $step);
# $start .. ISO 8601 date time string (starting datetime) or 'now'
# $count .. length of the sequence (incl. starting point)
# $unit .. step unit 'year', 'quarter', 'month', 'week',
# 'day', 'hour', 'minute', 'second'
# $step .. how many units there are between two seq elements (default: 1)
## double\_epoch
my $dbl = $p->double_epoch;
# BEWARE: precision loss, before exporting the time is truncated to miliseconds!
# returns Double piddle
## longlong\_epoch
my $epoch = $p->longlong_epoch;
# BEWARE: precision loss, before exporting the time is truncated to seconds!
# returns LongLong piddle
# NOTE: $p->longlong_epoch is equivalent to: longlong(floor($p->double_epoch))
# 1969-12-31T23:59:58 double_epoch = -2.0 longlong_epoch = -2
# 1969-12-31T23:59:58.001 double_epoch = -1.999 longlong_epoch = -2
# 1969-12-31T23:59:58.999 double_epoch = -1.001 longlong_epoch = -2
# 1969-12-31T23:59:59 double_epoch = -1.0 longlong_epoch = -1
# 1969-12-31T23:59:59.001 double_epoch = -0.999 longlong_epoch = -1
# 1969-12-31T23:59:59.999 double_epoch = -0.001 longlong_epoch = -1
# 1970-01-01T00:00:00 double_epoch = 0.0 longlong_epoch = 0
# 1970-01-01T00:00:00.001 double_epoch = 0.001 longlong_epoch = 0
# 1970-01-01T00:00:00.999 double_epoch = 0.999 longlong_epoch = 0
# 1970-01-01T00:00:01 double_epoch = 1.0 longlong_epoch = 1
## double\_ratadie
my $dbl = $p->double_ratadie;
# BEWARE: precision loss, before exporting the time is truncated to miliseconds!
# returns Double piddle
## double\_serialdate
my $dbl = $p->double_serialdate;
# BEWARE: precision loss, before exporting the time is truncated to miliseconds!
# returns Double piddle
## double\_juliandate
my $dbl = $p->double_juliandate;
# BEWARE: precision loss, before exporting the time is truncated to miliseconds!
# returns Double piddle
## dt\_year
my $y = $p->dt_year;
# returns: $y Short piddle (values 1 .. 9999)
## dt\_quarter
my $m = $p->dt_quarter;
# returns: $m Byte piddle (values 1 .. 4)
## dt\_month
my $m = $p->dt_month;
# returns: $m Byte piddle (values 1 .. 12)
## dt\_day
my $d = $p->dt_day;
# returns: $d Byte piddle (values 1 .. 31)
## dt\_ymd
If you need all `year`, `month` and `day` values it is more effective to use one `dt_ymd` call
instead of separately calling `dt_year`, `dt_month` and `dt_day`.
my ($y, $m, $d) = $p->dt_ymd;
# returns 3 piddles: $y Short, $m Byte, $d Byte
## dt\_hour
my $H = $p->dt_hour;
# returns Byte piddle (values 0 .. 23)
## dt\_minute
my $M = $p->dt_minute;
# returns Byte piddle (values 0 .. 59)
## dt\_second
my $S = $p->dt_second;
# returns Byte piddle (values 0 .. 59)
## dt\_microsecond
my $U = $p->dt_microsecond;
# returns Long piddle (values 0 .. 999_999)
## dt\_day\_of\_week
my $wd = $p->dt_day_of_week;
# returns Byte piddle (values 1=Mon .. 7=Sun)
## dt\_day\_of\_year
my $wd = $p->dt_day_of_year;
# returns Short piddle (values 1 .. 366)
## dt\_add
my $p->dt_add($unit, $num);
# adds $num datetime units
# $num can be positive (addition) or negative (subtraction)
# $unit .. 'year', 'quarter', 'month', 'week', 'day', 'hour',
# 'minute', 'second', 'millisecond', 'microsecond'
my $p->dt_add(day => 2);
# turns e.g. 2015-08-20T23:24:25.123456Z
# into 2015-08-22T23:24:25.123456Z
my $p->dt_add(day => -2);
# turns e.g. 2015-08-20T23:24:25.123456Z
# into 2015-08-18T23:24:25.123456Z
my $p->dt_add(day => 2, year => 3, month => 1);
# turns e.g. 2015-08-20T23:24:25.123456Z
# into 2018-09-22T23:24:25.123456Z
#NOTE: supports also inplace
$p->inplace->dt_add(day => 2);
## dt\_align
my $p->dt_align($unit);
# $unit .. 'year', 'quarter', 'month', 'week', 'day', 'hour',
# 'minute', 'second', 'millisecond', 'microsecond'
my $p->dt_align('minute');
# turns e.g. 2015-08-20T23:24:25.123456Z
# into 2015-08-20T23:24:00.000000Z
my $p->dt_align($unit, $upper); #second optional param
# $upper .. 1 or 0 (default), align to upper boundary (end of period)
# only for 'year', 'quarter', 'month', 'week'
# let's have: 2015-08-20T23:24:25.123456Z
$p->dt_align('year'); # -> 2015-01-01
$p->dt_align('year', 1); # -> 2015-12-31 (the last day of year)
$p->dt_align('quarter'); # -> 2015-07-01
$p->dt_align('quarter', 1); # -> 2015-09-30 (the last day of quarter)
$p->dt_align('month'); # -> 2015-08-01
$p->dt_align('month', 1); # -> 2015-08-31 (the last day of month)
$p->dt_align('week'); # -> 2015-08-17 (Monday)
$p->dt_align('week', 1); # -> 2015-08-23 (Sunday)
#NOTE: supports also inplace
$p->inplace->dt_align('minute');
## dt\_unpdl
my $array = $p->dt_unpdl;
my $array = $p->dt_unpdl($format);
my $array = $p->dt_unpdl('%y-%m-%d %H:%M:%S');
# returns perl arrayref with ISO 8601 date time strings
my $array = $p->dt_unpdl('auto');
# uses ISO 8601 format autodetected to be as short as possible
# e.g. 2015-09-07T22:53 when all piddle values have 0 seconds and 0 microseconds
# $format 'auto' is default when dt_unpdl is called without param
my $array = $p->dt_unpdl('epoch');
# returns perl arrayref (not a piddle) with epoch seconds as double
# BEWARE: precision loss, before exporting the time is truncated to miliseconds!
my $array = $p->dt_unpdl('epoch_int');
# returns perl arrayref (not a piddle) with epoch seconds as integer values
# BEWARE: precision loss, before exporting the time is truncated to seconds!
my $array = $p->dt_unpdl('Time::Moment');
# returns perl arrayref with Time::Moment objects
See [Time::Moment](
https://metacpan.org/pod/Time%3A%3AMoment#strftime) (which we use for stringification) for supported formats.
## dt\_at
my $datetime = $p->dt_at(@coords)
#or
my $datetime = $p->dt_at(@coords, $format)
# returns ISO 8601 date time string for value at given piddle co-ordinates
# optional $format arg - same as by dt_unpdl
## dt\_set
$p->dt_set(@coords, $datetime_or_epoch);
# sets $datetime_or_epoch as value at given piddle co-ordinates
# $datetime_or_epoch can be ISO 8601 string or epoch seconds (double or int)
## dt\_diff
my $deltas = $p->dt_diff;
#or
my $deltas = $p->dt_diff($unit);
# $unit .. 'week', 'day', 'hour', 'minute', 'second', 'millisecond'
## dt\_periodicity
my $per = $p->dt_periodicity;
# estimates the periodicity by calculating the median time between observations
# returns: "microsecond", "millisecond", "second", "minute"
# "hour", "day", "week", "month", "quarter"
# or an empty string
## dt\_startpoints
Extract index values corresponding to the first observations given a period specified by `$unit`
my $end_idx = $p->dt_startpoints($unit);
# $unit .. accepts same values as dt_align
Example:
my $dt = PDL::DateTime->new_from_datetime([qw/
2015-03-24 2015-03-25 2015-03-28 2015-04-01
2015-04-02 2015-04-30 2015-05-01 2015-05-10
/]);
print $dt->dt_startpoints('month');
# prints: [0 3 6]
print $dt->dt_startpoints('quarter');
# prints: [0 3]
## dt\_endpoints
Extract index values corresponding to the last observations given a period specified by `$unit`
my $end_idx = $p->dt_endpoints($unit);
# $unit .. accepts same values as dt_align
Example:
my $dt = PDL::DateTime->new_from_datetime([qw/
2015-03-24 2015-03-25 2015-03-28 2015-04-01
2015-04-02 2015-04-30 2015-05-01 2015-05-10
/]);
print $dt->dt_endpoints('month');
# prints: [2 5 7]
print $dt->dt_endpoints('quarter');
# prints: [2 7]
## dt\_slices
Combines ["dt\_startpoints"](#dt_startpoints) and ["dt\_endpoints"](#dt_endpoints) and returns 2D piddle like this:
my $dt = PDL::DateTime->new_from_datetime([qw/
2015-03-24 2015-03-25 2015-03-28 2015-04-01
2015-04-02 2015-04-30 2015-05-01 2015-05-10
/]);
print $dt->dt_slices('month');
# [
# [0 2] ... start index == 0, end index == 2
# [3 5] ... start index == 3, end index == 5
# [6 7] ... start index == 6, end index == 7
# ]
print $dt->dt_slices('quarter');
# [
# [0 2]
# [3 7]
# ]
The piddle returned by this function can be passed to [apply\_slice](
https://metacpan.org/pod/PDL%3A%3AApply#apply_slice).
## dt\_nperiods
Calculate the number of periods specified by `$unit` in a given time series.
The resulting value is approximate, derived from counting the endpoints.
$dt->dt_nperiods($unit)
# $unit .. 'year', 'quarter', 'month', 'week', 'day', 'hour',
# 'minute', 'second', 'millisecond', 'microsecond'
## is\_increasing
print $dt->is_increasing ? "is increasing" : "no";
#or
print $dt->is_increasing(1) ? "is strictly increasing" : "no";
## is\_decreasing
print $dt->is_decreasing ? "is decreasing" : "no";
#or
print $dt->is_decreasing(1) ? "is strictly decreasing" : "no";
## is\_uniq
print $dt->is_uniq ? "all items are uniq" : "no";
## is\_regular
print $dt->is_regular ? "all periods between items are the same" : "no";
# SEE ALSO
[PDL](
https://metacpan.org/pod/PDL)
# LICENSE
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
# COPYRIGHT
2015+ KMX <
[email protected]>