Perl timezone conversion - but only using core modules - perl

Is it possible to do timezone conversion for an 'arbitrary' date in the future using just Perl core modules?
Lets say I have the Day:month:year, hr:min in timezone X (assume its a fully qualified timezone like America/New_York) and I need to convert that to timezone Y (say Asia/Kolkata)
Some notes:
1) This is for OSX (Lion or above)
2) I know how to do it using DateTime and DateManip, but these are not core modules, and require a C compiler to be present to be installed. I am trying to distribute my program to 'non technical' users - they can do perl module installs with help, but fall apart when trying to get XCode, command line tools etc working. Some have tried to install DateTime but they got caught in errors/dependencies and gave up.
3) I tried using a combination of tzset; and ENV TZ - but that can't be used for arbitrary dates - only works with local time (Which means, I can convert 'now time' to any timezone)

Use the POSIX core module. Example:
use POSIX;
$ENV{TZ} = 'Europe/Madrid';
$time_t = POSIX::mktime( 10, 30, 17, 4, 4, 113 );
print POSIX::ctime($time_t); #<-- prints: Sat May 4 17:30:10 2013
$ENV{TZ} = 'Europe/London';
print POSIX::ctime($time_t); #<-- prints: Sat May 4 16:30:10 2013

Related

Error when dealing with old and historical dates in Perl Time::Piece

It seems Time:Piece gives me this error:
Error parsing time at /usr/lib/perl5/site_perl/Time/Piece.pm line 481.
after the line where I use strptime with some old dates.
My code contains this:
my $ddate = "$month / $day / $year";
my $tmp = Time::Piece->strptime( $ddate, "%m / %d / %Y");
and $$date takes dates from a database using DBI that contains historical and old dates (dates back to the 10th and 9th centuries AD). How can I deal with this if there is any solution?
You'll have to use something other than Time::Piece if you want to deal with timestamps before 1970.
If you want to use the Gregorian calendar, you can use DateTime.
If you want to use the Julian calendar, you can use DateTime::Calendar::Julian.
The Gregorian calendar was introduced in September, 1582, and it was used universally by 1918. In between, calendar usage varied by country.
$ perl -MDateTime::Calendar::Julian -E'
say
DateTime::Calendar::Julian->new(year => 1013, month => 2, day => 22)
->strftime("%a");
'
Sun
Alternatively, Date::Convert looks promising if you're just care about dates (not timestamps).

strftime '%z', (localtime) is not working as expected in solaris machines

I tried this code in linux machines,
my $sysdate = strftime "%Y%m%d%T", (localtime);
my $daylight_saving_time = strftime '%z', (localtime);
i get below output,
sysdate = 2013051402:12:02
daylight_saving_time = -0400
I tried same in solaries machines, i got this
sysdate = 2013051402:12:02
daylight_saving_time = %z
Anyone know the change to be done to get the daylight saving in solaries machines.
Thanks in Advance.
The issue is that POSIX::strftime just calls your system's strftime(3), so
you get whatever that is - or - is not. %z is not part of the POSIX.1 standard
and is not consistent across systems. On other older versions of OSes, like HPUX, %z, is
the same as %Z (time zone name). This is only for older versions.
On Solaris 8, 9 strftime does not support %z - with Solaris 10 it does.
This holds on more moderns versions Solaris 10 & Solaris 11:
%z Replaced by offset from UTC in ISO 8601:2000 standard format
(+hhmm or -hhmm), or by no characters if no time zone is deter-
minable. For example, "-0430" means 4 hours 30 minutes behind UTC
(west of Greenwich). If tm_isdst is zero, the standard time off-
set is used. If tm_isdst is greater than zero, the daylight sav-
ings time offset if used. If tm_isdst is negative, no characters
are returned.
So, this a C library function issue, perl sits on top of those libraries. I do not have a workaround.
Maybe the Date::Manip::TZ works on Solaris:
use Date::Manip::TZ;
my $tz = new Date::Manip::TZ;
say "tz: $tz";

Determining Local Time in another Timezone

How can I determine the current date and time of various countries using a PERL script that executes on a server in the US? For example, getDTnow() should determine the current date and time on the server and use that to return the date and time of various countries.
P.S: It would be great if this can be done using only the built-in functions, without any external modules.
Conclusion: Date maths is [use swear word here] complicated and easy to get wrong. Other perl gurus on IRC, groups and other parts of the net confirmed what Ether had been advicing me - use DateTime. DVK's solution is also pretty neat for those of you who don't mind messing with the perl environment. (Note: Though on windows, the caveats section of the Time::Piece docs says one should be careful while 'Setting $ENV{TZ} in Threads on Win32').
DateTime is a wonderful library that can use standard timezones to do everything you desire and more:
use DateTime;
# returns local time in Italy
my $dt = DateTime->now(time_zone => 'Europe/Rome');
# prints time in desired format
print "The current date and time in Italy is: ", $dt->strftime('%Y-%m-%d %T');
You can control which timezone localtime returns in via TZ environmental variable:
local $ENV{TZ} = ":/usr/share/lib/zoneinfo/Asia/Tokyo";
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime();
print "$sec,$min,$hour,$mday,$mon,$year,$wday,$yday\n"'
# Prints 40,58,4,12,0,111,3,11
local $ENV{TZ} = ":/usr/share/lib/zoneinfo/Europe/London";
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime();
print "$sec,$min,$hour,$mday,$mon,$year,$wday,$yday\n"'
# Prints 41,58,19,11,0,111,2,10
Unfortunately, the path above is different on different Unixes (/usr/share/lib/zoneinfo on Solaris, /usr/share/zoneinfo on Linux). Since there appear to be no other variations, a slightly portable version would check which of the 2 directories exists and use that - but this obviously only works on Solaris and Linux and may be other unixes. No idea about Windows/MacOS/whatnot.
Valid locations for TZ can be found here: http://www.timezoneconverter.com/cgi-bin/tzref.tzc (but not all of them would necessarily be available on your system - check the above directory).
Please see http://en.wikipedia.org/wiki/Tz_database for more info on TZ database.
You could always store the variation from your timezone in a hash where the key is the timezone and the value is the adjustment from the current time. then when you pass the current time it should return the local time for that zone.

How can I use Perl to do datetime comparisons and calculate deltas?

I extracted year, month, day, hour, minute, second, and millisecond data from human readable text (it wasn't in a timestamp format, but rather something like "X started at HH:MM:SS.SSS on DD MMM YYYY and ended at HH:MM:SS.SSSS on DD MMM YYYY"), so for each recorded event, I have each of the values.
However, I need to turn those into some kind of timestamp so I can do math on it - I want to determine how long the event lasted (end time - start time). I was hoping the time function would take parameters so I can create two arbitrary times, but that doesn't appear to be the case.
If possible, I would like to stick with functions available in the core Perl libraries or scripts that I can add to the project, since getting CPAN modules installed on the target machines would just make a headache for everyone, if it is even possible to get the modules through the security restrictions.
You want the CPAN module DateTime. Here's an introduction.
On a Debian GNU/Linux or Ubuntu system, simply run:
apt-get install libdatetime-perl
to install the module.
You can do it with Time:Local. It's basically the reverse of the built in "localtime" function, so you can generate a timestamp from a standard date.
In terms of built-ins these may be helpful:
POSIX (for mktime and strftime)
Time::Piece, Time::Local and Time::Seconds. These are all standard in Perl 5.10, but may not be available by default on earlier systems.
That said, time/date calculations are complex. If the only obstacle is a few headaches installing modules (rather than a company policy forbidding them), I would really recommend looking at CPAN.
Edit: I see from your comment on another post that there are company restrictions. You should update your original post, since there's a big difference between "headaches" and "security restrictions." In any case, DateTime and Date::Manip are worth looking at. Even if you don't install them, you can get a lot out of reading their source.
If you were only interested in comparing times,
my $ts1 = sprintf( '%4.4d%2.2d%2.2d%2.2d%2.2d%3.3d',
$year1, $month1, $mday1, $hour1, $min1, $sec1, $ms1 );
to
my $ts2 = sprintf( '%4.4d%2.2d%2.2d%2.2d%2.2d%3.3d',
$year2, $month2, $mday2, $hour2, $min2, $sec2, $ms2 );
using cmp would be sufficient.
To do arithmetic on these times, use Time::Local to get seconds since epoch and then add the $ms1/1000 to that value.
my $time1 = timelocal($sec1, $min1, $hour1, $mday1, $mon1, $year1) + $ms1/1000;
You can use POSIX::mktime to turn broken-up time into a timestamp. Be aware that the month is 0-based, and the year is 1900-based, so adjust accordingly. :-)
use POSIX qw(mktime);
$timestamp = mktime($sec, $min, $hour, $day, $month - 1, $year - 1900);

What's the opposite of the localtime function in Perl?

In Perl, localtime takes a Unix timestamp and gives back year/month/day/hour/min/sec etc. I'm looking for the opposite of localtime: I have the parts, and I'd like to build a unix timestamp from them.
You can use the timelocal function in the Time::Local CPAN module.
NAME
Time::Local - efficiently compute time
from local and GMT time
SYNOPSIS
$time = timelocal($sec,$min,$hour,$mday,$mon,$year);
$time = timegm($sec,$min,$hour,$mday,$mon,$year);
DESCRIPTION
This module provides functions that
are the inverse of built-in perl
functions localtime() and gmtime().
They accept a date as a six-element
array, and return the corresponding
time(2) value in seconds since the
system epoch (Midnight, January 1,
1970 GMT on Unix, for example). This
value can be positive or negative,
though POSIX only requires support for
positive values, so dates before the
system's epoch may not work on all
operating systems.
It is worth drawing particular
attention to the expected ranges for
the values provided. The value for the
day of the month is the actual day (ie
1..31), while the month is the number of months since January (0..11). This
is consistent with the values returned
from localtime() and gmtime().
Note: POSIX::mktime is a just a wrapper around your C library's mktime() function. Time::Local is a pure-Perl implementation, and always returns results matching Perl's localtime. Also, Time::Local offers gmtime, while mktime only works in local time. (Well, you could try changing $ENV{TZ}, but that doesn't work on some systems.)
POSIX::mktime
DateTime on CPAN might of of some use. It also has a lot of time manipulation/translation methods.
Just create the DateTime using your parts and call $datetime->formatter("%s") ;