Perl - How to convert a date? - perl

How to convert date format YYYY-MM-DDTHH:MM:SSZ to YYYY-MM-DD HH:MM + 8 hours?
For example:
Input: 2011-07-07T18:05:45Z
Output: 2011-07-08 02:05

Let's start with Rahul's snippet, and add in the date math and output formatting...
use DateTime;
use DateTime::Format::ISO8601;
use DateTime::Format::Strptime;
my $string = '2011-07-07T18:05:45Z';
my $dt = DateTime::Format::ISO8601->parse_datetime( $string );
die "Impossible time" unless $dt;
my $formatter = new DateTime::Format::Strptime(pattern => '%Y-%m-%d %T');
$dt->add( hours => 8 )->set_formatter($formatter);
print "$dt\n";
I've added the use of DateTime::Format::Strptime, in order to specify the desired output format.
Then I've added three more lines:
First I create a formatter, and feed it the output pattern I desire.
Next I add eight hours to the original date, and I assign the output
formatter by chaining the set_formatter() call to the add() call.
Then I print it.

Are you using the DateTime modules?
Specifically, here's a link to DateTime::Format::ISO8601 that reads/writes ISO 8601 format you mentioned as your input.

If you don't have DateTime, you surely have Time::Piece:
use strict;
use warnings;
use Time::Piece;
use Time::Seconds qw(ONE_HOUR);
my $str = '2011-07-07T18:05:45Z';
my $t = Time::Piece->strptime($str, "%Y-%m-%dT%TZ");
$t += 8 * ONE_HOUR;
print $t->strftime("%Y-%m-%d %H:%M"),"\n";

Taken From
How can I validate a "yyyy-MM-dd'T'HH:mm:ssZ" date/timestamp in UTC with Perl?
use DateTime;
use DateTime::Format::ISO8601;
my $string = '2010-02-28T15:21:33Z';
my $dt = DateTime::Format::ISO8601->parse_datetime( $string ); die "Impossible time" unless $dt;

It doesn't work, result is 2010-02-28T15:21:33
Then, do it the hard way...
use Time::Local
use warnings;
use strict;
$time = '2010-02-28T15:21:33Z';
my ($year, month, day) = split (/-/, $time)
$year -= 1900; #Year is an offset of 1900
$month -= 1; #Months are 0 - 11
#Now split the time off of the day (DDTHH:MM:SS)
$day = substr($day, 0, 2);
time = substr($day, 3)
#Now split the time
(my $hour, $minute, $second) = split(/:/, $time);
$second =~ s/Z$//; #Remove Z
my $time_converted = timelocal($second, $minute, $hour, $day, $month, $year);
#Now you have the time, Add eight hours
my $hours_in_seconds = 8 * 60 * 60;
$time_converted += $hours_in_seconds;
# Almost done: Convert time back into the correct array:
($second, $minute, $hour, $day, $month, $year) = localtime($time_converted);
$year += 1900;
$month += 1;
# Now, reformat:
my $formatted_time = sprint (%04d-%02d-%02d %02d:%02d),
$year, $month, $day, $hour, $minute;

Related

Perl convert YYYYMMDD to 16digit epoch time

Is there a perl function that converts YYYYMMDD to a 16digit epoch time?
I'm currently calling the date command:
date -d 20160219 +%s%6N
for the conversion but my script takes a long time to go through the millions of dates in my data set.
By "16digit epoch time", I mean a 16-digit decimal integer representing microseconds since the epoch. For example, 2016-02-19 00:00:00 UTC is 1455840000 seconds after the epoch, so the result I want is "1455840000000000".
You can also use Time::Moment. In the interest of full disclosure, I am the author of Time::Moment.
use v5.10;
use Time::Moment;
say Time::Moment->from_string('20160219' . 'T00Z')
->strftime('%s%6N');
Output:
1455840000000000
Time::Piece can do all the things you want. After that just right pad 0's to epoch, so that it becomes 16 digits.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my $t = Time::Piece -> strptime("20160219",
"%Y%m%d");
my $epoch = $t -> epoch;
# make $epoch 16 digits
$epoch .= "0" x (16 - length($epoch));
print "$epoch\n";
You can use the Time::Local core module:
use Time::Local 'timelocal';
$date = "20160218";
($year, $month, $day) = unpack "A4A2A2", $date;
$time = timelocal(0, 0, 0, $day, $month-1, $year);
$time .= '000000';
Wow, I just actually wrote a script to take the timestamp where it takes a timestamp where it's in seconds since epoch and convert it to human readable format. Not what you asked, but perhaps you can reverse what I wrote.
my $ts = $ARGV[0]; #2012-09-04-20:24:19.870 1030825460
my $newTs = toTimeString($ts);
print "\n".$newTs."\n";
## Convert from time since epoch in seconds to string YYYY-MM-DD-HH:mm:ss.sss
sub toTimeString{
my $ts=shift;
$ts += 432000; # Add 5 days to make computation easy.
my $sec = $ts-int($ts/60)*60; # Seconds
$ts = int($ts/60); # Convert to minutes
my $min = $ts%60; # minutes
$ts = int($ts/60); # Convert to hours.
my $hr = $ts%24; # hours;
$ts = int($ts/24); # Convert to days.
my $yr;
my $mo;
my $day;
my $days = [ [31,28,31,30,31,30,31,31,30,31,30,31],
[31,29,31,30,31,30,31,31,30,31,30,31]
];
our $cumDays = [ [0,31,59,90,120,151,181,212,243,273,304,334],
[0,31,60,91,121,152,182,213,244,274,305,335]
];
if($ts<366){$yr=1980;}
else{
$yr = 1981 + int(($ts-366)/1461)*4;
$ts = $ts - 366 - int(($ts-366)/1461)*1461;
if($ts < 1095){$yr += int($ts/365); $ts = $ts - int($ts/365)*365;}
else{$yr = $yr+3; $ts=$ts-1095;}
}
my $leap=0;
my $i;
if($yr%4 == 0){$leap=1;}
for($i=0;$i<12;$i++){
if($ts > $cumDays->[$leap]->[$i]){
$mo=$i+1;
}
}
$day=$ts-$cumDays->[$leap]->[$mo-1]+1;
my $ret;
$ret = sprintf("%04d",$yr)."-".sprintf("%02d",$mo)."-".sprintf("%02d",$day)."-";
$ret = $ret.sprintf("%02d",$hr).":".sprintf("%02d",$min).":".sprintf("%02d",int($sec));
$ret = $ret."\.".sprintf("%03d",int($sec*1000)%1000);
return ($ret)
}

convert date format from 9/15/12 to 255 in perl

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);

Convert Old Unix Date to Perl and compare

Requirement - I have file name called "Rajesh.1202242219". Numbers are nothing but a date "date '+%y''%m''%d''%H''%M'" format.
Now I am trying to write a perl script to extract the numbers from file name and compare with current system date and time and based on output of this comparison, print some value using perl.
Approach:
Extract the Digit from File name:
if ($file =~ /Rajesh.(\d+).*/) {
print $1;
}
Convert this time into readable time in perl
my $sec = 0; # Not Feeded
my $min = 19;
my $hour = 22;
my $day = 24;
my $mon = 02 - 1;
my $year = 2012 - 1900;
my $wday = 0; # Not Feeded
my $yday = 0; # Not Feeded
my $unixtime = mktime ($sec, $min, $hour, $day, $mon, $year, $wday, $yday);
print "$unixtime\n";
my $readable_time = localtime($unixtime);
print "$readable_time\n";
find Current time and compare...
my $CurrentTime = time();
my $Todaydate = localtime($startTime);
But the problem here is, I am not getting solution of how to extract 2 digit from $1 and assign to $sec, $min, etc. Any help?
Also, if you have good approach for this problem statement, Please share with me
I like to use time objects to simplify the logic. I use Time::Piece here because it is simple and light weight (and part of the core). DateTime can be another choice.
use Time::Piece;
my ( $datetime ) = $file =~ /(\d+)/;
my $t1 = Time::Piece->strptime( $datetime, '%y%m%d%H%M' );
my $t2 = localtime(); # equivalent to Time::Piece->new
# you can do date comparisons on the object
if ($t1 < $t2) {
# do something
print "[$t1] < [$t2]\n";
}
Might as well teach DateTime::Format::Strptime to make the comparison much simpler:
use DateTime qw();
use DateTime::Format::Strptime qw();
if (
DateTime::Format::Strptime
->new(pattern => '%y%m%d%H%M')
->parse_datetime('Rajesh.1202242219')
< DateTime->now
) {
say 'filename timestamp is earlier than now';
} else {
say 'filename timestamp is later than now';
};
my ($year, $month, $day, $hour, $min) = $file =~ /(\d{2})/g;
if ($min) {
$year += 100; # Assuming 2012 and not 1912
$month--;
# Do stuff
}
I think unpack might be a better fit.
if ( my ( $num ) = $file =~ /Rajesh.(\d+).*/ ) {
my ( $year, $mon, $day, $hour, $min ) = unpack( 'A2 A2 A2 A2 A2', $num );
my $ts = POSIX::mktime( 0, $min, $hour, $day, $mon - 1, $year + 100 );
...
}
Using a module that parses dates might be nice. This code will parse the date and return a DateTime object. Refer to the documentation to see the many ways to manipulate this object.
use DateTime::Format::Strptime;
my $date = "1202242219";
my $dt = get_obj($date);
sub get_obj {
my $date = shift;
my $strp = DateTime::Format::Strptime->new(
pattern => '%y%m%d%H%M'
);
return $strp->parse_datetime($date);
}

How do I convert local time to a Unix timestamp in Perl?

For example: from date: 10/02/2010
How do I convert an equal timestamp for 10/02/2010 00:00:00 in Perl?
I can't use local time or time .. is there another way to achieve this?
You can use the Time::Local core module:
use Time::Local 'timelocal';
my ($d, $m, $y) = split '/', '10/02/2010';
my $time = timelocal(0, 0, 0, $d, $m-1, $y);
Note that the month argument for timelocal() is in the range 0..11.
Without localtime():
use Time::Local;
$time = timelocal($sec, $min, $hour, $mday, $mon, $year);
(See perldoc.)
A standard way would be something like:
use POSIX;
use strict;
use warnings;
my $sec = 0;
my $min = 0;
my $hour = 0;
my $day = 10;
my $mon = 2 - 1;
my $year = 2010 - 1900;
my $wday = 0;
my $yday = 0;
my $unixtime = mktime ($sec, $min, $hour, $day, $mon, $year, $wday, $yday);
print "$unixtime\n";
my $readable_time = localtime($unixtime);
print "$readable_time\n"
(From Converting Unix time and readable time with Perl)
You could use Date::Parse:
use Date::Parse;
print str2time('10/02/2010 00:00:00');
On my machine this prints 1285970400, which corresponds to October 2nd, 2010 (I live in +1 GMT with +1 Wintertime.)
I think you want the built-in module Time::Local.
The DateTime module should be helpful here. In particular, I believe the DateTime::Format::Natural module can parse a user-supplied date string. From there, you have a DateTime object and can print it out or transform it as you like.
Depending on where your initial date is coming from you might be able to parse it using
Date::Manip
and calling
ParseDate("10/02/2010")
You can then take that output and convert it into whatever format you wish.

How can I change the timezone of a datetime value in Perl?

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