I pull a date from a file and I'm creating a datetime object by using this command and then adding 1 to it to get the next date.
my $parseit = DateTime::Format::Strptime->new(pattern => '%Y%m%d');
my $lastdate = "20190115";
my $tempdate = $parseit->parse_datetime($lastdate);
my $date_up1 = $tempdate->add(days => 1);
but in printing out the variable $date_up1 I always get it in the form %Y-%m-%d. How can I just get it returned in the pattern that I selected.
strptime and thus DateTime::Format::Strptime by default only dictates how you parse the input into a DateTime object. DateTime objects default to a specific stringification, which you are seeing. In order to stringify it in a certain way, you can use its strftime method.
print $date_up1->strftime('%Y%m%d');
While DateTime::Format::Strptime can be used to both parse and format date times, it doesn't set itself as the default formatter for the DateTime objects it creates as you expect. You can do that explicitly by adding the following:
$tempdate->set_formatter($parseit);
After cleaning up your code, it looks like this:
my $date = "20190115";
my $format = DateTime::Format::Strptime->new(
pattern => '%Y%m%d',
on_error => 'croak',
);
my $dt = $format->parse_datetime($date);
$dt->set_formatter($format);
$dt->add( days => 1 );
say $dt;
Alternatively, all of the following work without setting the formatter:
$format->format_datetime($dt)
$dt->strftime("%Y%m%d") (Most flexible, but introduces duplicate code in this case)
$dt->ymd("") (Simplest in this case)
Related
I'm trying to localize times that are in UTC when the only thing I know about the destination time is the longitude and latitude. I've come up with something that works, but feels kludgy:
# We need to get localized time for display purposes.
state $moduleGeoLocation = require Geo::Location::TimeZone;
my $gltzobj = Geo::Location::TimeZone->new();
my $tzName = $gltzobj->lookup( lon => $params->{'longitude'}, lat => $params->{'latitude'} );
say "TimeZone: " . $tzName;
So far so good. Here's where the kludge comes in. I'm parsing a time using Time::Piece's strptime but I don't have a GMT offset for the timezone, so after I parse the UTC time in Time::Piece, I'm sending it over to DateTime to do the time zone calculation. It seems rather clunky to be using both DateTime and Time::Piece:
# Get TimeZoneOffset.
state $moduleDateTime = require DateTime;
state $moduleDateTimeTimeZone = require DateTime::TimeZone;
my $timeZone = DateTime::TimeZone->new( 'name' => $tzName );
my $timePiece = Time::Piece->strptime($hour->{'time'}, '%Y-%m-%dT%H:%M:%SZ');
my $time = DateTime->from_epoch( 'epoch' => $timePiece->epoch, 'time_zone' => $timeZone );
Is there a better way to accomplish what I'm doing? I'm aiming for the fastest possible way to get to the result of a localized time.
Your question boils down to the following:
How do I create a DateTime object from a timestamp using the %Y-%m-%dT%H:%M:%S format. I have the appropriate time zone as a DateTime::TimeZone object.
To parse a date-time into a DateTime, one should first look for an appropriate DateTime::Format:: module.
DateTime::Format::Strptime would be the most similar to your current attempt.
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S',
strict => 1,
time_zone => $tz,
on_error => 'croak',
);
my $dt = $format->parse_datetime($ts);
You could also use DateTime::Format::ISO8601, although you couldn't use it as a validator since it doesn't accept only the stated format.
use DateTime::Format::ISO8601 qw( );
( my $dt = DateTime::Format::ISO8601->parse_datetime($ts) )
->set_time_zone('floating')
->set_time_zone($tz);
Given that the latter solution overrides any explicitly-provided time zone, I'd use the first solution for clarity.
Following situation.
I have a file named 2018_12_03_FileName.log. Now I get the date from the file (2018_12_03).
I want to convert the string to a DateTime object, which works too.
$chars =~s/_//g;
$chars = Time::Piece->strptime("$chars", "%Y%m%d");
$chars = $chars->strftime("%d/%m/%Y");
Output
03/12/2018
After that I want to get the date today - 14 days. But here is one of my two problems. I tried many things, but couldn't find any real solution working for me.
my $day14 = DateTime->now();
$day14 -= (2 * ONE_WEEK);
Error:
Cannot subtract 1209600 from a DateTime object (DateTime=HASH(0x6f2d84)). Only a DateTime::Duration or DateTime object can be subtracted from a DateTime object.
Now the second problem is, I want to compare these two dates and look if the file date is in range or not.
my $cmp = DateTime->compare($chars, $day14);
Error:
Argument "15/07/2019" isn't numeric in numeric eq (==) at
A DateTime object can only be compared to another DateTime object (03/12/2018, 15/07/2019).
So how can I subtract 14 days from the today date and how can I compare these two dates after?
You're slightly muddling up two Date/Time ecosystems that don't work well together.
You can do this using Time::Piece and Time::Seconds.
use feature 'say';
use Time::Piece;
use Time::Seconds;
my $chars = '2018_12_03';
my $tp = Time::Piece->strptime($chars, '%Y_%m_%d');
my $date14 = $tp - (2 * ONE_WEEK);
say $tp->strftime('%d/%m/%Y');
say $date14->strftime('%d/%m/%Y');
Output:
03/12/2018
19/11/2018
Or you can do it using DateTime and friends.
use feature 'say';
use DateTime;
use DateTime::Format::Strptime;
my $date_parser = DateTime::Format::Strptime->new(
pattern => '%Y_%m_%d',
on_error => 'croak',
);
my $chars = '2018_12_03';
my $dt = $date_parser->parse_datetime($chars);
my $date14 = $dt->clone->subtract( weeks => 2 );
say $dt->strftime('%d/%m/%Y');
say $date14->strftime('%d/%m/%Y');
Output:
03/12/2018
19/11/2018
As for your last question, you can compare either Time::Piece objects or DateTime objects using the standard Perl comparison operators (<, ==, >=, etc). But you have to compare two objects of the same type.
Just another option, Time::Moment makes date math really simple:
use strict;
use warnings;
use Time::Moment;
my $chars = Time::Moment->from_object(Time::Piece->strptime("$chars", "%Y%m%d"));
my $day14 = Time::Moment->now_utc->minus_days(14)->at_midnight;
print $day14->strftime('%d/%m/%Y');
These objects can be compared with normal operators like with DateTime and Time::Piece. Just keep in mind that, since you only care about the day and not time of day, all math should be done according to the same time zone, of which the easiest is of course UTC.
I need to convert the following strings to DateTime objects so I can compare them:
2016-06-30T09:00:00-04:00
2016-07-01T15:37:25
Both objects should use the EST timezone. How can I do this?
First of all, EST is used to refer to a multitude of different time zone offsets. Presumably, you meant UTC-05:00.
Second of all, if one is to perform datetime arithmetic, one rarely wants to deal with offsets; one almost always wants time zones associated with a geographical location (such as America/New_York). America/New_York would be a suitable choice for use below, but I used the more flexible local instead.
use strict;
use warnings;
use feature qw( say );
use DateTime::Format::Strptime qw( );
my $format1 = DateTime::Format::Strptime->new(
pattern => "%Y-%m-%dT%H:%M:%S%Z",
on_error => "croak",
);
my $format2 = DateTime::Format::Strptime->new(
pattern => "%Y-%m-%dT%H:%M:%S",
time_zone => "local",
on_error => "croak",
);
my $dt1 = $format1->parse_datetime('2016-06-30T09:00:00-04:00');
my $dt2 = $format2->parse_datetime('2016-07-01T15:37:25');
Then, you can do whatever you want. You mentioned you wanted to compare them, which can be done using numerical comparison operators (e.g. $dt1 < $dt2).
The following example converts the timestamps into RFC3339 timestamps (the timestamp format used by internet standards):
$dt1->set_time_zone("UTC");
say "dt1: " . $dt1->strftime("%Y-%m-%dT%H:%M:%SZ");
$dt2->set_time_zone("UTC");
say "dt2: " . $dt2->strftime("%Y-%m-%dT%H:%M:%SZ");
I need to parse some time string that comes in a format like ddmmyyyyhhmmssXXX. The XXX part is millisecond. In the below code Im ignoring the millisecond part. It works but I get the error:
garbage at end of string in strptime: 293 at /usr/local/lib64/perl5/Time/Piece.pm line 482.
Whats the proper format that I should use.
$time = '11032014182819802';
$format = '%d%m%Y%H%M%S';
$t = Time::Piece->strptime($time, $format);
Time::Piece->strptime(substr($time, 0, -3), $format);
since Time::Piece does not support milliseconds.
If you care about the milliseconds and want to preserve them, you'll need to look into using something else (e.g., DateTime::Format::Strptime).
You can use DateTime::Format::Strptime if you want to parse milliseconds, then you may try this:
my $Strp = new DateTime::Format::Strptime(
pattern => '%d%m%Y%H%M%S%3N',
);
my $date = $Strp->parse_datetime("11032014182819802");
print $date->millisecond ,"\n";
In my perl program, I am calculating warranty start date from warranty end date The difference is 3 years. Following is the chunk of code that I have written so far:
use Time::ParseDate;
use DateTime;
use POSIX;
use DateTime::Duration;
my $warranty_expiration_string = "20-May-07";
my $epoch = parsedate($warranty_expiration_string);
my $warranty_expiration = strftime "%D", localtime($epoch); # returns: 05/20/07
How do I deduct 3 years from $warranty_expiration to get $warranty_start date?
I tried,
$warranty_start->subtract(DateTime::Duration->new('years' => 3));
..but it did not work.
I don't understand all the different date/time modules being mixed. You only need some of them, not all of them. If you want to do date math using DateTime anyway, you want something like this:
use DateTime;
use DateTime::Format::Strptime;
my $dateparser = DateTime::Format::Strptime->new( pattern => '%d-%b-%y' );
my $warranty_expiration = $dateparser->parse_datetime($warranty_expiration_string);
my $warranty_start = $warranty_expiration->clone->subtract( years => 3);
Most of the DateTime::Format::* modules are meant to be used with DateTime and I prefer to use those if I can.
You may also want to read more about the ongoing DateTime project and the list of recommended modules at:
http://datetime.perl.org
$warranty_expiration isn't a DateTime, it's a string. You want to do something like:
my $warranty_expiration = DateTime->from_epoch(
epoch => $epoch,
time_zone => 'local',
);
my $warranty_start = $warranty_expiration->clone->subtract(years => 3);
and then you can use $warranty_expiration->strftime("%D") and $warranty_start->strftime("%D") as formatted strings. Also, if you use one of the DateTime::Format modules instead of Time::ParseDate, you will get back a DateTime directly from the parser instead of having to use from_epoch.
No need to instantiate DateTime::Duration for this as the calculation methods expect those parameters directly:
use DateTime;
my $dt = DateTime->now->subtract(years => 3);
print "$dt\n";
Result:
2009-08-30T14:36:27