I am trying to do some date calculation relative to the current local time.
For example:
use feature qw(say);
use strict;
use warnings;
use Time::Piece;
my $fmt = '%Y-%m-%d_%H:%M:%S';
my $timestamp = "2015-04-12_11:07:27";
# This gives incorrect $t1 relative to localtime
my $t1 = Time::Piece->strptime( $timestamp, $fmt );
my $t2 = localtime;
say "Local time: " . localtime;
say "Local time epoch: " . time;
say $t1->epoch();
say $t2->epoch();
my $timestamp1 = $t1->strftime( $fmt );
my $timestamp2 = $t2->strftime( $fmt );
say $timestamp1;
say $timestamp2;
my $delta = $t2 - $t1;
say $delta;
A sample output:
Local time: Sun Apr 12 12:21:49 2015
Local time epoch: 1428834109
1428836847
1428834109
2015-04-12_11:07:27
2015-04-12_12:21:49
-2738
Which clearly gives the a wrong time difference of -2738. ( It should be a positive number)
If the date-time you parse has no time zone information, it's assumed to be UTC. You can see this by adding the following two lines in your script:
say "tzo1 = ",$t1->tzoffset;
say "tzo2 = ",$t2->tzoffset;
In Paris, the above outputs the following:
tzo1 = 0
tzo2 = 7200
You can override the default to be the local time zone by using the undocumented feature of using localtime instead of Time::Piece as the invocant.
$ perl -MTime::Piece -E'
say Time::Piece->strptime("2015-04-12_11:07:27", "%Y-%m-%d_%H:%M:%S")->tzoffset;
say localtime ->strptime("2015-04-12_11:07:27", "%Y-%m-%d_%H:%M:%S")->tzoffset;
'
0
7200
Doing that minor change gives the answer you were expecting.
$ perl -MTime::Piece -E'
say localtime->strptime("2015-04-12_11:07:27", "%Y-%m-%d_%H:%M:%S") - localtime;
'
5524
I think this can be done using Date::Time :
use feature qw(say);
use strict;
use warnings;
use DateTime;
use DateTime::Format::Strptime;
use DateTime::Duration;
my $strp = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d_%H:%M:%S',
time_zone => 'local',
);
my $timestamp = "2015-04-12_11:07:27";
my $dt1 = $strp->parse_datetime( $timestamp );
my $dt2 = DateTime->now();
say $dt2->subtract_datetime_absolute( $dt1 )->seconds();
Related
I have a script which will print Start & End time of previous hour of UTC/GMT.
#!/usr/local/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);
my ($tmp_date, $tmp_hour, $Start, $End);
my $date = strftime '%Y-%m-%d', gmtime();
print "Date:$date\n";
my $hour = strftime '%H', gmtime();
print "Hour:$hour\n";
if ($hour == "00"){
$tmp_date = $date-1;
$tmp_hour = "23";
} else {
$tmp_hour = $hour-1;
$tmp_date = $date;
}
$a = length($tmp_hour);
if ($a == 1 ){
$tmp_hour="0".$tmp_hour;
}
$Start = $tmp_date.".".$tmp_hour."00";
$End = $tmp_date.".".$hour."05";
if ($End =~ /0005/){
$tmp_date = `TZ=GMT-12 date +%Y%m%d`;
$End =$tmp_date.".".$hour."05";
}
print "Start:$Start, End:$End\n";
For example, lets say now UTC time is: Wed Jun 10 10:18:57 UTC 2020
This should print Start & End time as 2020-06-10.0900 2020-06-10.1005 respectively.
This script is working as expected. But when Daylight savings happens will there be any impact on fetching Start & End time?
I want experts suggestions how can I avoid unnecessary if statements and achieve it by the use of Perl module itself.
PS: Perl version: v5.10.1. Please suggest Perl modules which comes with standard Perl installation (Ex: POSIX, Time::Local etc.) for solution of above problem.
As you're using gmtime(), any DST changes will have no effect at all.
I'm not sure why your end time ends with '05', I would have thought that the end of the hour comes at '00'.
Here's how I'd write it with Time::Piece and Time::Seconds.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Time::Piece;
use Time::Seconds;
my $end = gmtime->truncate(to => 'hour');
my $start = $end - ONE_HOUR;
my $format = '%Y-%m-%d %H:%M:%S';
say 'Start: ', $start->strftime($format);
say 'End: ', $end->strftime($format);
If you really want the end time to be five past the hour, then add this line after the ONE_HOUR line:
$end += (5 * ONE_MINUTE);
You can, of course, use any of the standard strftime() sequences to change the format of the output.
I searched through all the possible questions but couldn't find the answer,
so can Perl experts help me on this one?
I have two timestamps like 05/25/2011 05:22:03 PM and 05/25/2011 05:34:08 PM. They are stored in string form.
my $str1 = '05/25/2011 05:22:03';
my $str2 = '05/25/2011 05:34:08';
The latter being the time of a job ending and former being the time it started.
How do I find out the difference in dates and time? The dates are the same in this case but they could differ as well.
I recommend that you use the Time::Piece module. It has been a core module since the release of version 9.5 of Perl 5, so it shouldn't need installing.
This code demonstrates
use strict;
use warnings;
use Time::Piece;
my $str1 = 'Execution started at 05/25/2011 05:22:03 PM';
my $str2 = 'Execution completed at 05/25/2011 05:34:08 PM';
my #times = map Time::Piece->strptime(/(\d.+M)/, '%m/%d/%Y %H:%M:%S %p'), $str1, $str2;
my $delta = $times[1] - $times[0];
print $delta->pretty;
output
12 minutes, 5 seconds
You can take advantage of DateTime and its subtract_datetime() method, which returns a DateTime::Duration object.
use Date::Parse;
use DateTime;
my $t1 = '05/25/2011 05:22:03';
my $t2 = '05/25/2011 05:34:08';
my $t1DateTime = DateTime->from_epoch( epoch => str2time( $t1 ) );
my $t2DateTime = DateTime->from_epoch( epoch => str2time( $t2 ) );
my $diff = $t2DateTime->subtract_datetime( $t1DateTime );
print "Diff in minutes: " . $diff->in_units('minutes') . "\n";
print "Diff in hours: " . $diff->in_units('hours') . "\n";
print "Diff in months: " . $diff->in_units('months') . "\n";
I was wondering if someone could show me how to convert 9/15/12 to 255 format.
Something in php from getdate array you can get ydate.
I think you're asking how you can get the 1 <= yday <= 366 day representation of a date, similar to yday in php's getdate(). As is common in PERL, there's more than one way to do it. The simplest mechanism would be to use localtime() for today's date:
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
If you want to do it for a different date, I'd probably use the TimeDate CPAN module
use Date::Parse;
print time2str("%j",str2time("9/15/2012"));
I think Tawnos's mentioning php's yday from getdate() is on target. Here's one option (assuming 2012 is the year in your date string):
use strict;
use warnings;
use Date::Calc qw/Day_of_Year/;
my $date = '9/15/12';
my ( $month, $day, $year ) = split '/', $date;
my $doy = Day_of_Year( "20$year", $month, $day ) - 1;
print $doy; # (0 - 365)
Output:
258
Do you want the day of the year? That would be day 258 (0-based) or the 259th day (1-based), though. Using only core Perl:
use Time::Local qw( timegm );
my $date = '9/15/12';
my ($m,$d,$y) = split(qr{/}, $date);
my $epoch = timegm(0,0,0, $d,$m-1,$y);
my $yday = ( gmtime($epoch) )[7]; # 258 (0-based index)
Note that timegm+gmtime is applicable no matter the time zone of the date.
Here's another way, using the core module Time::Piece :
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
my $t = Time::Piece->strptime(shift,"%m/%d/%y");
print $t->yday, "\n";
The day output is zero-relative: January 01 = 0.
my $dt = '9/15/2012';
my ( $m, $d, $y ) = split( '/', $dt );
my $t = POSIX::mktime( 0, 0, 0, $d, $m - 1, $y - 1900 );
say [ localtime $t ]->[7] - 3;
Julian date (minus 1) is slot 7 in the list return from localtime and gmtime.
Why subtract 3? I don't know; Sept 15 is 259 Julian. Still it performs the function mapping '9/15/2012' to 255.
You can use only function strftime from core module POSIX.
use strict;
use POSIX qw(strftime);
print strftime "%j", localtime(time);
I need some help with date calculations in perl with dates for the format "2012-02-03 00:00:00". In particular is there a tool I could use to just increment the days and it switches to month and year correctly? Thanks.
See DateTime.
#!/usr/bin/env perl
use strict; use warnings;
use DateTime;
my $ts = '2012-02-03 00:00:00';
my ($y, $m, $d) = ($ts =~ /([0-9]{4})-([0-9]{2})-([0-9]{2})/);
my $dt = DateTime->new(year => $y, month => $m, day => $d);
$dt->add( months => 2, days => 3 );
print $dt->strftime('%Y-%m-%d %H:%M:%S'), "\n";
It's actually a little cleaner to use a DateTime::Format class, and you get error checking for free.
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d %H:%M:%S',
time_zone => 'local',
on_error => 'croak',
);
my $ts = '2012-02-03 00:00:00';
my $dt = $format->parse_datetime($ts);
$dt->add( months => 2, days => 3 );
print $format->format_datetime($dt), "\n";
The Time::Piece module is a standard part of the Perl installation and probably does all that you need.
This program uses your example date and adds two months and three days, then a further 400 days. Two alternative ways of displaying the values are shown
use strict;
use warnings;
use Time::Piece;
use Time::Seconds 'ONE_DAY';
my $format = '%Y-%m-%d %H:%M:%S';
my $dt = Time::Piece->strptime('2012-02-03 00:00:00', $format);
$dt = $dt->add_months(2);
$dt += 3 * ONE_DAY;
print $dt->strftime($format), "\n";
$dt += 400 * ONE_DAY;
printf "%s %s\n", $dt->ymd, $dt->hms;
output
2012-04-06 00:00:00
2013-05-11 00:00:00
This is all perfectly possible within core using the POSIX time-handling functions.
The standard POSIX::mktime function already copes with denormalised values, and can correct for days/months out of range. Additionally, POSIX::strftime actually calls this on the given values before formatting them, so it will adjust correctly.
use POSIX qw( strftime mktime );
use POSIX::strptime qw( strptime );
my $format = "%Y-%m-%d %H:%M:%S";
my #t = strptime( "2012-02-03 00:00:00", $format );
#t = #t[0..5]; # Throw away wday and yday
$t[3] += 3; # mday
$t[4] += 2; # mon
say strftime $format, #t;
$t[3] += 400; # mday
say strftime $format, #t;
Gives
2012-04-06 00:00:00
2013-05-11 00:00:00
Using this function:
perl -e 'use Time::Local; print timelocal("00","00","00","01","01","2000"),"\n";'
It will return an epochtime - but only in GMT - if i want the result in GMT+1 (which is the systems localtime(TZ)), what do i need to change?
Thanks in advance,
Anders
use DateTime;
my $dt = DateTime->now;
$dt->set_time_zone( 'Europe/Madrid' );
There is only one standard definition for epochtime, based on UTC, and not different epochtimes for different timezones.
If you want to find the offset between gmtime and localtime, use
use Time::Local;
#t = localtime(time);
$gmt_offset_in_seconds = timegm(#t) - timelocal(#t);
While Time::Local is a reasonable solution, you may be better off using the more modern DateTime object oriented module. Here's an example:
use strict;
use DateTime;
my $dt = DateTime->now;
print $dt->epoch, "\n";
For the timezones, you can use the DateTime::TimeZone module.
use strict;
use DateTime;
use DateTime::TimeZone;
my $dt = DateTime->now;
my $tz = DateTime::TimeZone->new(name => "local");
$dt->add(seconds => $tz->offset_for_datetime($dt));
print $dt->epoch, "\n";
CPAN Links:
DateTime
You just need to set the timezone. Try:
env TZ=UTC+1 perl -e 'use Time::Local; print timelocal("00","00","00","01","01","2000"),"\n";'
Time::Local::timelocal is the inverse of localtime. The result will be in your host's local time:
$ perl -MTime::Local -le \
'print scalar localtime timelocal "00","00","00","01","01","2000"'
Tue Feb 1 00:00:00 2000
Do you want the gmtime that corresponds to that localtime?
$ perl -MTime::Local' -le \
'print scalar gmtime timelocal "00","00","00","01","01","2000"'
Mon Jan 31 23:00:00 2000
Do you want it the other way around, the localtime that corresponds to that gmtime?
$ perl -MTime::Local -le \
'print scalar localtime timegm "00","00","00","01","01","2000"'
Tue Feb 1 01:00:00 2000
An other example based on DateTime::Format::Strptime
use strict;
use warnings;
use v5.10;
use DateTime::Format::Strptime;
my $s = "2016-12-22T06:16:29.798Z";
my $p = DateTime::Format::Strptime->new(
pattern => "%Y-%m-%dT%T.%NZ",
time_zone => "UTC"
);
my $dt = $p->parse_datetime($s);
$dt->set_time_zone("Europe/Berlin");
say join ' ', $dt->ymd, $dt->hms; # shows 2016-12-22 07:16:29
The Algorithm
If you want to change a time value from one timezone to another timezone, you must be able to indicate both timezones.
After all, if you set if you want to convert "12:30" to GMT or US/Eastern or Venezuelan time, which means adding/subtracting some amount of hours or hours and minutes, you need to know what timezone is the starting time zone, otherwise, the calculation won't know how much to add or subtract.
If you use DateTime->now;, the timezone is defaulted to the system-time, which may not be the timezone you want to convert from.
In the below code, I demonstrate how to initialize the datetime object to the right starting timezone (fromtimezone) and how to convert that time to the ending timezone (totimezone)...
Working Code
I could not find a Perl sandbox online with the DateTime CPAN module installed.
use strict;
use DateTime;
sub convertTimeZonesForTime {
my ($args) = #_;
my $time = $args->{time};
my $date = $args->{date};
my $totimezone = $args->{totimezone};
my $fromtimezone = $args->{fromtimezone};
my $format = $args->{format} || '%H:%M:%S';
my ($year, $month, $day) = map {int $_} split('-', $date);
my ($hour, $minute, $second) = map {int $_} split(':', $time);
$year ||= 1999 if !defined $year;
$month ||= 1 if !defined $month;
$day ||= 1 if !defined $day;
$hour ||= 12 if !defined $hour;
$minute ||= 30 if !defined $minute;
$second ||= 0 if !defined $second;
my $dt = DateTime->new(
year=>$year,
month=>$month,
day=>$day,
hour=>$hour,
minute=>$minute,
second=>$second,
time_zone => $fromtimezone,
);
my $formatter = new DateTime::Format::Strptime(pattern => $format);
$dt->set_formatter($formatter);
$dt->set_time_zone($totimezone);
return "$dt";
}
print(convertTimeZonesForTime({
'totimezone'=>'America/Denver',
'fromtimezone'=>'US/Eastern',
'time'=>'12:30:00',
}));
Output:
10:30:00