Reputation: 151
I have this format for a timestamp:
2019-01-24T00:00:05.011719
How do I round this up to the nearest 5 minute value? in perl
Edit: Okay the question is pretty vague and lazy. But the question stays, just want to add information to my problem.
I don't know what kind of format that is, so if I know i can google, what function or method converts those kind of date formats.
20190124000000 isn't this the correct 5 min value? I wanted it to be like that format by the way. But its too easy to be the correct approach, it makes me suspicious like answering math questions back in college. Anyway, I can just use regex for that.
Upvotes: 0
Views: 1494
Reputation: 5962
The problem can be broken down to:
N
time unitsX
time units since epoch"
X
by N
gives you the # of the period the timestamp lies inX
by N
gives you the offset inside that period0
(zero) then the timestamp is at the start of that periodYour requirements are
N
is 5 minutes, or 300 seconds1970-01-01T00:00:00Z
Just using core Perl, i.e. Time::Piece, a solution would be:
#!/usr/bin/perl
use warnings;
use strict;
use constant PERIOD => 5 * 60; # 5 minutes
use Time::Piece;
# timezone for Time::Piece->new()
$ENV{TZ} = "UTC";
while (<DATA>) {
chomp;
my($iso8601, $fractional) = split(/\./);
# NOTE: time is interpreted as UTC
my $t = Time::Piece->strptime($iso8601, '%FT%T');
# calculate multiple of PERIOD and offset in that period
my $index = int($t->epoch / PERIOD);
my $offset = $t->epoch % PERIOD + "0.${fractional}";
# round up to next PERIOD unless time is exactly multiple of PERIOD
$index++ if $offset > 0;
# convert index back to epoch and generate new Time::Piece object
# NOTE: timezone offset is set to $ENV{TZ} timezone
my $t2 = Time::Piece->new($index * PERIOD, 0);
print "$_ -> ", $t2->strftime('%FT%T'), "\n";
}
exit 0;
__DATA__
2019-01-24T00:00:00.000000
2019-01-24T00:00:05.011719
2019-01-24T00:04:59.999999
2019-01-24T00:05:00.000000
2019-07-24T00:00:00.000000
2019-07-24T00:00:05.011719
2019-07-24T00:04:59.999999
2019-07-24T00:05:00.000000
Test run:
$ perl dummy.pl
2019-01-24T00:00:00.000000 -> 2019-01-24T00:00:00
2019-01-24T00:00:05.011719 -> 2019-01-24T00:05:00
2019-01-24T00:04:59.999999 -> 2019-01-24T00:05:00
2019-01-24T00:05:00.000000 -> 2019-01-24T00:05:00
2019-07-24T00:00:00.000000 -> 2019-07-24T00:00:00
2019-07-24T00:00:05.011719 -> 2019-07-24T00:05:00
2019-07-24T00:04:59.999999 -> 2019-07-24T00:05:00
2019-07-24T00:05:00.000000 -> 2019-07-24T00:05:00
Upvotes: 1
Reputation: 385916
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%FT%T.%6N',
time_zone => 'UTC', # Or 'local' or 'America/Toronto' or '-0500'
on_error => 'croak',
);
my $dt = $format->parse_datetime('2019-01-24T00:00:05.011719');
$dt->set_formatter($format); # Set default stringification format.
$dt->truncate( to => 'second' )->add( seconds => 1 ) if $dt->nanosecond;
$dt->truncate( to => 'minute' )->add( minutes => 1 ) if $dt->second;
$dt->add( minutes => 5 - ( $dt->minute % 5 ) ) if $dt->minute % 5;
say $dt; # 2019-01-24T00:05:00.000000
Note that the format used by 2019-01-24T00:00:05.011719
is ambiguous if the associated time zone observes DST (because of the repeated hour in the fall).
Aside from that, the above code correctly handles discontinuities in the passing of time such as the ones that occur at DST changes, as long as the discontinuity starts and ends at a rounding point.
Upvotes: 3