Ответ: Perl подход для разрешения неоднозначных данных, хранящихся по местному времени, без ссылки на состояние летнего времени - PullRequest
0 голосов
/ 26 апреля 2020

Это продолжение ответа на предыдущий вопрос , где @ikegami предоставляет пример преобразования местного времени с помощью CST / CDT в UT C (Z).

Он взял мой пример, вставленный в комментарий с ограниченным форматированием, и создал следующие исходные данные:

11/03/19 01:00:00AM CDT
11/03/19 01:59:58AM CDT
11/03/19 01:59:59AM CDT
11/03/19 01:00:00AM CST
11/03/19 01:00:01AM CST
11/03/19 01:59:59AM CST

Исходные данные в комментарии были на самом деле такими (но не были читаемыми, потому что я не мог выяснить, как вставить многострочный кодовый блок в комментарий):

11/03/19 01:00:00AM CDT
11/03/19 01:30:00AM CDT
11/03/19 01:50:00AM CDT
11/03/19 01:00:00AM CDT
11/03/19 01:30:00AM CDT
11/03/19 01:50:00AM CDT
11/03/19 02:00:00AM CST
11/03/19 02:10:01AM CST

Важным отличием является то, что тег DST выводится из даты и времени, поэтому он остается CDT, когда время между 01:00 и 02:00 независимо от того, является ли это «первым» или «вторым» появлением этого часа по местному времени. Это неопределенность, которая должна быть решена. Данные выше уже являются выводом другого скрипта. Исходные исходные данные выглядят следующим образом:

11/03/19 01:00:00A
11/03/19 01:30:00A
11/03/19 01:50:00A
11/03/19 01:00:00A
11/03/19 01:30:00A
11/03/19 01:50:00A
11/03/19 02:00:00A

Чтобы решить эту проблему, моей первой мыслью является обнаружение записи времени, которая предшествует предыдущей записи, и установление для флага DST значения Standard для этой записи и последующий, пока не изменится вычисленный флаг DST.

Это лучший подход или есть другие «встроенные» возможности или библиотечные функции, которые могут уже решить эту проблему?

1 Ответ

0 голосов
/ 26 апреля 2020

Я написал этот вопрос, но не опубликовал его до разработки решения. Вот то, что я придумал для двусмысленности - я надеюсь, что это помогает кому-то еще. Особая благодарность @ikegami за предоставление примеров использования Strptime и strftime для создания окончательного вывода в формате UT C. Это весь сценарий, начиная с формата отметки времени, например «11/03/19 01: 00: 00A». В этом примере записи данных обрезаются до отметки времени, удаляя следующие данные.

#!/usr/bin/perl

# Parse local time stamped data files save in UTC Time


use strict;
use warnings;
use File::Basename;   
use Time::Piece;    
use Time::Local;     
use feature qw( say );
use DateTime::Format::Strptime qw( );

STDOUT->autoflush(1);

my $format = DateTime::Format::Strptime->new(
   pattern   => '%m/%d/%y %I:%M:%S%p %Z',
   locale    => 'en',
   zone_map  => { CST => '-0600', CDT => '-0500' }, # Handle non-standard time zone names.
   time_zone => 'America/Chicago',                  # Optional. Convert result to this tz.
   strict    => 1,
   on_error  => 'croak',
);


# Input file from command line.
open (INFILE, $ARGV[0])
  or die "Error cannot open $ARGV[0]: $!";

print "Opening $ARGV[0] for input     ";           


my $base = fileparse($ARGV[0],'\..*');
my $outfname = $base.'.csv';

if ( defined $ARGV[1] )  {    # If a 2nd paramater is given, use it for the output file
   open (OUT, '>'.$ARGV[1]); 
 } else {
   open (OUT, '>'.$outfname);    # Else name the output file with .csv
   print "opening $outfname for output\n";
 }


 my $prev_outtemp = 0;            
 my $lastparsedtime = 0;  
 my $ForceStdTime = 0;

 while (<INFILE>) {
  chomp;
  my $workline = $_;   
  $workline=~ s/^\s+|\s+$//g;   # trim leading/trailing space
  my @pline = split(/ /,$workline);    #split input line into elements @pline[0] @pline[1] etc            

  $pline[1] =~ s/A/AM/;     # change A and M to AM/PM
  $pline[1] =~ s/P/PM/;

  my $timestampstr =  $pline[0]." ".$pline[1];

                                                    #    12/31/09 08:40:00AM
  my $parsedtime = Time::Piece->strptime($timestampstr, '%m/%d/%y %I:%M:%S%p');

  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =  localtime($parsedtime);      

  my $tltime = timelocal( $sec, $min, $hour, $mday, $mon, $year );
    $year += 1900;
    $mon += 1;

  my ($dst) = (localtime($tltime))[8]; # fetch daylight savings time flag

  if ( $parsedtime < $lastparsedtime ) {      # Time discontinuity
            $ForceStdTime  = 1;               # force standard time during 1 hour in spring
        }            
  $lastparsedtime = $parsedtime;

  my $dtlocaltag = "";
  if ($dst and not $ForceStdTime) {
        $dtlocaltag = "\"$pline[0] $pline[1] CDT\"";
            }
   else {
        $dtlocaltag =  "\"$pline[0] $pline[1] CST\"";
            }

    print "$dtlocaltag   ";          

  if (not $dst) {
      $ForceStdTime = 0;        # reset force flag once localtime sees standard time
      }             

   my $dt = $format->parse_datetime($dtlocaltag);
   my $epoch = $dt->epoch;
   my $local_dt_str = $dt->strftime("%Y-%m-%dT%H:%M:%S%z");
   $dt->set_time_zone('UTC');
   my $utc_dt_str = $dt->strftime("%Y-%m-%dT%H:%M:%SZ");
   print " $local_dt_str $utc_dt_str\n";
   print OUT "\"$utc_dt_str\"";

   #  --- snip --- processing of rest of data in text input line....

   print OUT "\n";

}  # end of while loop

close (INFILE);
close(OUT);

Файл входных данных выглядит следующим образом:

11/03/19 12:50:00A
11/03/19 01:00:00A
11/03/19 01:10:00A
11/03/19 01:20:00A
11/03/19 01:30:00A
11/03/19 01:40:00A
11/03/19 01:50:00A
11/03/19 01:00:00A
11/03/19 01:10:00A
11/03/19 01:20:00A
11/03/19 01:30:00A
11/03/19 01:40:00A
11/03/19 01:50:00A
11/03/19 02:00:00A
11/03/19 02:10:01A
03/08/20 12:50:00A
03/08/20 01:00:00A
03/08/20 01:10:00A
03/08/20 01:20:00A
03/08/20 01:30:00A
03/08/20 01:40:00A
03/08/20 01:50:00A
03/08/20 03:00:00A
03/08/20 03:10:00A

Файл выходных данных выглядит следующим образом:

"2019-11-03T05:50:00Z"
"2019-11-03T06:00:00Z"
"2019-11-03T06:10:00Z"
"2019-11-03T06:20:00Z"
"2019-11-03T06:30:00Z"
"2019-11-03T06:40:00Z"
"2019-11-03T06:50:00Z"
"2019-11-03T07:00:00Z"
"2019-11-03T07:10:00Z"
"2019-11-03T07:20:00Z"
"2019-11-03T07:30:00Z"
"2019-11-03T07:40:00Z"
"2019-11-03T07:50:00Z"
"2019-11-03T08:00:00Z"
"2019-11-03T08:10:01Z"
"2020-03-08T06:50:00Z"
"2020-03-08T07:00:00Z"
"2020-03-08T07:10:00Z"
"2020-03-08T07:20:00Z"
"2020-03-08T07:30:00Z"
"2020-03-08T07:40:00Z"
"2020-03-08T07:50:00Z"
"2020-03-08T08:00:00Z"
"2020-03-08T08:10:00Z"
...