How do I get yesterday's date using localtime? - perl

How do I tweak this to get yesterday's date using localtime?
use strict;
sub spGetCurrentDateTime;
print spGetCurrentDateTime;
sub spGetCurrentDateTime {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my #abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $currentDateTime = sprintf "%s %02d %4d", $abbr[$mon], $mday, $year+1900; #Returns => 'Aug 17 2010'
return $currentDateTime;
}
~

use DateTime qw();
DateTime->now->subtract(days => 1);
The expression on the second line returns a DateTime object.

As tempting as it is to just subtract a day's worth of seconds from the current time, there are times when this will yield the wrong answer (leap seconds, DST, and possibly others). I find it easier to just let strftime (available in the Perl 5 core module POSIX) take care of all of that for me.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
use POSIX qw/strftime/;
#2010-03-15 02:00:00
my ($s, $min, $h, $d, $m, $y) = (0, 0, 0, 15, 2, 110);
my $time = timelocal $s, $min, $h, $d, $m, $y;
my $today = strftime "%Y-%m-%d %T", localtime $time;
my $yesterday = strftime "%Y-%m-%d %T", $s, $min, $h, $d - 1, $m, $y;
my $oops = strftime "%Y-%m-%d %T", localtime $time - 24*60*60;
print "$today -> $yesterday -> $oops\n";

The DST problem can be worked around by taking 3600s from midday today instead of the current time:
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
sub spGetYesterdaysDate;
print spGetYesterdaysDate;
sub spGetYesterdaysDate {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my $yesterday_midday=timelocal(0,0,12,$mday,$mon,$year) - 24*60*60;
($sec, $min, $hour, $mday, $mon, $year) = localtime($yesterday_midday);
my #abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $YesterdaysDate = sprintf "%s %02d %4d", $abbr[$mon], $mday, $year+1900;
return $YesterdaysDate;
}
In light of the "unspecified" documented behaviour of the strftime solution suggested by Chas, this approach might be better if you're not able to test for expected-but-not-guaranteed results across multiple platforms.

use Time::Piece.
use strict;
use warnings;
use 5.010;
# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;
my $yesterday = localtime() - ONE_DAY;
say $yesterday->strftime('%b %d %Y');
Note that this can go wrong in certain borderline cases, such as the start of daylight saving time.
The following version does behave correct in such cases:
use strict;
use warnings;
use 5.010;
# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;
my $now = localtime();
my $yesterday = $now - ONE_HOUR*($now->hour + 12);
say $yesterday->strftime('%b %d %Y');
Alternatively, you can use the DateTime module as described in a different answer. That is not a core module, though.

Solution suggested by most users is wrong!
localtime(time() - 24*60*60)
The worst thing you can do is to assume that 1 day = 86400 seconds.
Example: Timezone is America/New_York, date is Mon Apr 3 00:30:00 2006
timelocal gives us 1144038600
localtime(1144038600 - 86400) = Sat Apr 1 23:30:00 EST 2006
oops!
The right and the only solution is to let system function normalize values
$prev_day = timelocal(0, 0, 0, $mday-1, $mon, $year);
Or let datetime frameworks (DateTime, Class::Date, etc) do the same.
That's it.

localtime(time() - 24*60*60)

my $yesterday = time();
$yesterday = $yesterday - (24*60*60);
24 as 24 hours, 60 as 60 minutes in hour and 60 as 60 seconds in minute
time() will return actual timestamp, and 246060 will remove seconds for exactly one day
After this simply do:
localtime($yesterday);

This is how I do it.
#!/usr/bin/perl
use POSIX qw(strftime);
$epoc = time();
$epoc = $epoc - 24 * 60 * 60;
$datestring = strftime "%F", localtime($epoc);
print "Yesterday's date is $datestring \n";

Related

How to make Time::Piece respect DST when converting localtime to UTC?

I want to convert a timestamp from localtime to GMT. I have legacy code that does this "manually" with Time::Local::timelocal() and gmtime. It works, but I don't like it and wanted to use Time::Piece instead. I used this answer to do so (albeit they are converting the other way round, but I was able to replace + with - :-)).
This is my code:
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Local;
use Time::Piece;
use POSIX qw(strftime);
sub local_to_utc_timelocal
{
my $local_ts = shift;
my ( $year, $mon, $mday, $hour, $min, $sec )
= ( $local_ts =~ /(\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/ );
my $local_secs = Time::Local::timelocal( $sec, $min, $hour, $mday, $mon - 1, $year );
return POSIX::strftime( '%y-%m-%d %H:%M:%S', gmtime($local_secs) );
}
sub local_to_utc_timepiece
{
my $local_ts = shift;
my $local_tp = Time::Piece->strptime( $local_ts, '%y-%m-%d %H:%M:%S' );
my $utc_tp = $local_tp - localtime->tzoffset(); # ***
return $utc_tp->strftime('%y-%m-%d %H:%M:%S');
}
my $local;
# DST in effect (April 19 2016):
$local = '16-04-19 14:30:00';
print "DST in effect:\n";
printf("utc(%s) = %s (using timelocal)\n", $local, local_to_utc_timelocal($local));
printf("utc(%s) = %s (using timepiece)\n\n", $local, local_to_utc_timepiece($local));
# DST NOT in effect (Feb 19 2016):
$local = '16-02-19 14:30:00';
print "DST NOT in effect:\n";
printf("utc(%s) = %s (using timelocal)\n", $local, local_to_utc_timelocal($local));
printf("utc(%s) = %s (using timepiece)\n", $local, local_to_utc_timepiece($local));
Unfortunately the Time::Piece code doesn't seem to work properly wrt DST. I'm living in Germany, so currently (spring/summer, DST in effect) we are UTC+2. For "April 19 2016" the above code gives:
DST in effect:
utc(16-04-19 14:30:00) = 16-04-19 12:30:00 (using timelocal)
utc(16-04-19 14:30:00) = 16-04-19 12:30:00 (using timepiece)
which is correct. But for "Feb 19 2016" (when we are UTC+1) the same code gives:
DST NOT in effect:
utc(16-02-19 14:30:00) = 16-02-19 13:30:00 (using timelocal)
utc(16-02-19 14:30:00) = 16-02-19 12:30:00 (using timepiece)
That is: The gmtime(Time::Local::timelocal($timestamp)) gives correct 1 hour offset while Time::Piece still gives 2 hours offset.
Is this a bug in Time::Piece or do I use it wrongly?
I know there are plenty of ways to convert localtime to UTC but I'd like to use Time::Piece because of its simplicity. Plus I cannot use DateTime because that would involve deploying it on a dozen of production machines.
Problem 1
localtime returns now, so localtime->tzoffset() returns the offset for now. Change
my $utc_tp = $local_tp - localtime->tzoffset();
to
my $utc_tp = $local_tp - $local_tp->tzoffset();
However, that leaves $utc_tp flagged as a localtime, so you really want:
my $utc_tp = gmtime( $local_tp->epoch );
Problem 2
Despite its name, $local_tp is not a local time, so $local_tp->tzoffset() is zero. Change
Time::Piece->strptime( $local_ts, '%y-%m-%d %H:%M:%S' )
to
localtime->strptime( $local_ts, '%y-%m-%d %H:%M:%S' );

How to get the system clock in format in Perl?

I want to get the system clock (time and date) and display it in a human-readable format in Perl.
The format like 2014-09-12 15:13:56
#!/usr/local/bin/perl
my %months = qw(Jan Feb Mar Apr May Jun Jul
Aug Sep Oct Nov Dec);
#weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear,
$daylightSavings) = localtime();
$year = 1900 + $yearOffset;
$now = "$year-$months-$dayOfMonth $hour:$minute:$second";
print $now;
When you run the program, you should see a much more readable date and time like this:
2014--12 16:57:15
how to get convert the month to number ?
Using Time::Piece (core module since perl v5.9.5)
use Time::Piece;
my $dt = localtime;
print $dt->ymd, " ", $dt->hms, "\n";
using DateTime
use DateTime;
my $dt = DateTime->now();
print $dt->ymd, " ", $dt->hms, "\n";
It's easier using a Perl module (POSIX doesn't requires installation):
use POSIX qw/strftime/;
my $now_string = strftime "%Y-%m-%d %H:%M:%S", localtime;
print $now_string, "\n"; #<-- prints: 2014-09-12 11:09:45 (with my local time)
Regarding to your code, there is a typo:
$now = "$year-$months-$dayOfMonth $hour:$minute:$second";
should be:
$now = "$year-$month-$dayOfMonth $hour:$minute:$second";
Be sure to write use strict; and use warnings; in the top place of your script. It prevents you from errors like that.
I like to put these date and time tasks into functions for reuse.
Here is my approach:
use strict;
use warnings;
my $time_stamp = getTodaysDateTime();
print "Program Started: $time_stamp \n";
# do some processing
$time_stamp = getTodaysDateTime();
print "Program Ended: $time_stamp \n";
# return date in specific format
# ex: 2014-09-12 14:11:43
sub getTodaysDateTime {
my ($sec,$min,$hour,$mday,$mon,$year,
$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon += 1;
return sprintf("%d-%02d-%02d %02d:%02d:%02d",
$year,$mon,$mday,$hour,$min,$sec);
}

Perl - How to convert a date?

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;

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