ureco has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I'm new to Perl and I'm trying extract weather data from various sources and output this data into a standard format for import into a database.

One of the data sources is a CSV file which date and time stamps it's records using the local time on the server at the time it's written to the file. I'm looking to standardise the time and need to convert this time from the local time to UTC. I'm in the UK so this changes by 1 hour going forward on the last Sunday in March and back on the last Sunday in October at 2am.

The following code is what I have so far:
#! c:\xampp\perl\bin\perl use strict; use warnings; use LWP::Simple; use Time::Local; my $standard_filename = $ARGV[0]; my $extended_filename = $ARGV[1]; # Get yesterdays date time my $epoc = time(); $epoc = $epoc - 86400; my $yesterday = localtime($epoc); my ($sec, $min, $hour, $day, $month, $year) = (localtime($epoc))[0,1,2 +,3,4,5]; $month++; $year += 1900; my $mm = $month; if ($month < 10) { $month = "0" . $month; } if ($day < 10) { $day = "0". $day; } # Build the Standard and Extended filenames if not passed as parameter +s if (not defined $standard_filename) { $standard_filename = "C:\\wdisplay\\logfiles\\" . $mm . $year . "l +gcsv.csv"; } if (not defined $extended_filename) { $extended_filename = "C:\\WebHost\\customtextout.txt"; } # Load Standard File in to an Array my @standard_records; open (standard_file, "<", $standard_filename) or die "Failed to open file: $!\n"; while (<standard_file>) { chomp; push @standard_records, $_; } close standard_file; # Load Extended File into an Array my @extended_records; open (extended_file, "<", $extended_filename) or die "Failed to open file: $!\n"; while (<extended_file>) { chomp; push @extended_records, $_; } close extended_file; # Process each Standard Record foreach (@standard_records) { my @std_lines = split /\n/, $_; foreach my $line_std (@std_lines) { my @std_fields = split ",", $line_std; my $obs_day = $std_fields[0]; my $obs_month = $std_fields[1]; my $obs_year = $std_fields[2]; my $obs_hour = $std_fields[3]; my $obs_minute = $std_fields[4]; my $obs_seconds = 0; if ($obs_day eq "day") { next; } my $utc_time = timelocal($obs_seconds, $obs_minute, $obs_hour, + $obs_day, $obs_month - 1, $obs_year - 1900); my ($utc_sec, $utc_min, $utc_hour, $utc_day, $utc_month, $utc_ +year) = (gmtime($utc_time))[0,1,2,3,4,5]; $utc_month++; $utc_year += 1900; if ($obs_day < 10) { $obs_day = "0" . $obs_day; } if ($obs_month < 10) { $obs_month = "0" . $obs_month; } if ($obs_hour < 10) { $obs_hour = "0" . $obs_hour; } if ($obs_minute < 10) { $obs_minute = "0" . $obs_minute; } my $record_line = $obs_year . "," . $obs_month . "," . $obs_da +y . "," . $obs_hour . "," . $obs_minute . "," . "00" . "," . "," . $u +tc_year . "," . $utc_month . "," . $utc_day . "," . $utc_hour . "," . + $utc_min . "," . $utc_sec . "\n"; print $record_line; my $file = "C:\\WEATHER_DATA_COLLECTOR\\temp.csv"; open (TXT, ">>", $file); print TXT $record_line; close (TXT); } }
There is a bit of redundant code at the moment but the area of interest is within the foreach (@standard_records) loop and the date conversions within this section of the code. Initially I thought this was working but it does not handle the time when the clocks change correctly.

The following is a sample of the output as the clock goes back at the end of October 2014:

Local / UTC
25th 23:00 | 25th 22:00
26th 00:00 | 25th 23:00
26th 01:00 | 26th 01:00 (this should have been 00:00)
26th 01:00 | 26th 01:00 (hour should not have been repeated)
26th 02:00 | 26th 02:00
26th 03:00 | 26th 03:00

The local time goes back at 2am so 01:00 - 01:59 is repeated. UTC should be continuous, effectively 'catching-up' with the local time as it goes back. But, it jumps forward at 23:59 (missing out 00:00 - 00:59) and then going back at 2am like local time.

So, either side of the time change it looks correct it's just the transition it's not handling correctly.

I'd be very grateful for any help on what I'm doing wrong - Thanks Colin.

Replies are listed 'Best First'.
Re: Converting local time to utc time
by boftx (Deacon) on Nov 10, 2014 at 22:03 UTC

    DateTime is your friend for this task.

    You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
      Hi boftx, Unfortunately I don't have the DateTime module installed - attempted an install of this module but it appears it needs a number of prerequisite modules. I've never installed a module before so may not have done this correctly!

      Is there any other methods which can solve this problem without the need to install additional modules?

        In my experience, BotFx gave you the best method.

        I would seriously consider figuring out what the problem is with installing modules and then move forward with his advice. Dates and Times are a non-trivial PITA.

        The available modules for Perl are well worth the time to figure out how to get them to you machine.

        ...the majority is always wrong, and always the last to know about it...

        Insanity: Doing the same thing over and over again and expecting different results...

        A solution is nothing more than a clearly stated problem...otherwise, the problem is not a problem, it is simply an inconvenient fact

        I've managed to install the DateTime module but I'm still getting incorrect values returned as it moves through the Daylight Saving Time change.

        I've added the following code:
        my $dt = DateTime->new ( year => $obs_year, month => $obs_month, day => $obs_day, hour => $obs_hour, minute => $obs_minute, time_zone => 'Europe/London', ); $dt->set_time_zone( 'UTC' );
        The following is output:

        LOCAL | UTC
        2014/10/26 00:59 | 2014/10/25 23:59 <- Correct
        2014/10/26 01:00 | 2014/10/26 01:00 <- Incorrect, should be 00:00

        UTC is also actually repeating 01:00 - 01:59 so after the Daylight Saving Change it is correct again it's just the transition it appears to go wrong.

        The UTC datetime is still being moved on by 1hr - this should stay the same enabling the Local time to catch-up.

        Have I coded this correctly?

        Really appreciate your help on this - Thanks.