Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Re: convert UTC time to system time zone's time with perl default method

by Marshall (Canon)
on Nov 27, 2010 at 07:07 UTC ( [id://873970]=note: print w/replies, xml ) Need Help??


in reply to convert UTC time to system time zone's time with perl default method

What you want to do is actually very straightforward.

But, first let me congratulate you on using UTC as the "standard time" that your system operates in. This is the right way to go. Also, using a text based representation of this time for transfer between systems is better than using the epoch time since the representation of the epoch can vary from system to system.

Epoch time (in seconds), like a GMT or UTC time is unambiguous, it is a steady onward marching time - there isn't any jump forward or backward stuff - well except for leap seconds). Exactly what date/time is considered to be "zero" - the beginning of time varies from system to system. Jan 1, 1970 is a common value. You convert the UTC time to this system specific number and then convert that number into the local time.

Time::Local will already be on every system. The normal time functions would not have the precision implied by your time format. However, all you really need to do is figure out how the time zone change will affect the date and the hour - minutes,seconds and fraction of seconds will remain the same. So:
-convert date/hour into the system's "epoch" time using timegm().
-convert that epoch time into local time zone using localtime()
code is shown below...

I am in the PST time zone - this date/time was during PDST and the local time is -7 hours from UTC. If I change the month to 12 instead of 09- the time difference will be -8 hours and localtime() automatically adjusts for that. Note that both gmtime and localtime() handle leap years.

Anyway, your code doesn't need to know what time zone this code is actually running in or whether it is DST or not - localtime() will take care of that.

Note: I did have to consider some quirky historical stuff about the std I/F to the time functions - years are 1900 based and month numbering starts at zero instead of 1 because that makes it easier to use the month value as an index into some text describing the month. But this is easily handled once you know about it.

Update:I didn't talk about going in the other direction (local->UTC) or doing "time math". There is some weirdness about that..for example when we leap backwards, at say 2AM, the time 1:15AM will occur twice on that local day although these two occurrences of 1:15AM represent 2 different UTC times. And you have to decide which of the 2 possible UTC times you mean (or accept default of the earliest one). Similarly, when we leap forward, one hour of local times "go missing" and the day only has 23 local hours. Important to note is that UTC time always has 24 hour days. And there is a direct mapping between the systems idea of "epoch time" and UTC. Do duration calculations using UTC. Anyway if your application is sensitive to these special 23 and 25 hour local days, you have some thinking to do - that's true whether you use some module or not.

#!/usr/bin/perl -w use strict; use Time::Local; #needed for timegm() my $t = '2010-09-06 15:06:53.512999'; #( it is UTC time ) my $newstring = UTC2LocalString($t); print " UTC: $t\n"; print "Local: $newstring\n"; =PRINTS: UTC: 2010-09-06 15:06:53.512999 Local: 2010-09-06 08:06:53.512999 =cut sub UTC2LocalString { my $t = shift; my ($datehour, $rest) = split(/:/,$t,2); my ($year, $month, $day, $hour) = $datehour =~ /(\d+)-(\d\d)-(\d\d)\s+(\d\d)/; # proto: $time = timegm($sec,$min,$hour,$mday,$mon,$year); my $epoch = timegm (0,0,$hour,$day,$month-1,$year); # proto: ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = # localtime(time); my ($lyear,$lmonth,$lday,$lhour,$isdst) = (localtime($epoch))[5,4,3,2,-1]; $lyear += 1900; # year is 1900 based $lmonth++; # month number is zero based #print "isdst: $isdst\n"; #debug flag day-light-savings time return ( sprintf("%04d-%02d-%02d %02d:%s", $lyear,$lmonth,$lday,$lhour,$rest) ); }
  • Comment on Re: convert UTC time to system time zone's time with perl default method
  • Download Code

Replies are listed 'Best First'.
Re^2: convert UTC time to system time zone's time with perl default method
by Anonymous Monk on Feb 24, 2017 at 20:09 UTC
    This is nice solution, except many timezones are new half hour, if even 15 minutes, so you should probably populate minutes as well, not just hours.
      Wow, I hadn't thought about this code for 7 years!

      Yes, there is some wild time weirdness out there and time zone offsets of 30 minutes for sure exist. If I remember right, there used to be a couple of places in the US that used a 30 minute offset for daylight savings time? I hadn't heard of a 15 minute time zone (or daylight time) difference before, but humans are capable of doing some amazingly stupid things! I guess that sort of confusion could be nice as an "excuse" if you are late to an appointment and the other side of the street is using local time 15 minutes ahead of you!

      If I were re-doing this, I would add the minutes and seconds into the code, leaving out only the fractions of seconds which cannot be represented by epoch time. Epoch is basically a 32 bit int representing number of seconds since an arbitrary date that is related to the release of Unix. So the fractions of a second cannot be represented.

      After 7 years, the algorithm remains sound: a) Convert the UTC date/time string into standard epoch seconds. Then, b) ask the local system what local time that epoch time means? I think the code is clear enough that I can leave that straight-forward extension to the reader...The epoch->local time standard function would handle this 30 minute stuff when given enough precision in the epoch time. So a 30 minute offset doesn't fundamentally change anything except the need for more precision.

      My advice to always log data in UTC remains. Local time is a presentation thing for management.

        Epoch is basically a 32 bit int representing number of seconds since an arbitrary date that is related to the release of Unix. So the fractions of a second cannot be represented.

        Or 64 bits on newer systems, see the Year 2038 problem. Also, the way I look at it is that the concept of "Unix time" (non-leap seconds since Jan 1st, 1970 00:00:00 UTC) is portable, which is separate from the storage of that value. For example, it's possible to get Unix time with fractional seconds (this is on Linux):

        $ perl -MTime::HiRes=gettimeofday -e 'printf "%d.%06d\n", gettimeofday +' 1488014647.609340

        Personally I'd always advocate using an appropriate module to do date/time math instead of homegrown solutions, usually DateTime.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://873970]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (3)
As of 2024-04-25 09:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found