[contrib/timezone-gen] Fix timezone gen (#2215)

* [contrib/timezone-gen] Move timezone-gen.pl to own folder

* [contrib/timezone-gen] Add fixTzstr

* [contrib/timezone-gen] Add tests and zone data getter
 - tests.pl can be used to verify that the generated timezone conf
   will produce the correct datetimes by testing them against
   what the system's `date` says
 - build-zonedata.pl will download the latest tzdb data and build
   the posix timezone data files. It only builds what is needed
   rather than adding extraneous "right/" and "posix/" timezones.
   FreeSWITCH doesn't seem to be able to use the "right/"
   timezone files.
 - data/ is where the various files needed to generate the
   timezones gets stored
This commit is contained in:
Douglas Vought 2023-09-05 16:11:01 -04:00 committed by GitHub
parent 0df47beebe
commit 07f192ca03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 165 additions and 3 deletions

View File

@ -0,0 +1,28 @@
#!/usr/bin/perl
use strict;
use warnings;
my $remote_version = `wget --quiet https://data.iana.org/time-zones/tzdb/version --output-document -` =~ s/\n//r;
my $local_version;
if ( open my $in, "<data/version" ) {
$local_version = do { local $/; <$in> };
close $in;
}
my $up_to_date = defined($local_version) && $local_version eq $remote_version;
if ( ! $up_to_date ) {
open my $out, ">data/version";
print $out $remote_version;
close $out;
}
$local_version = $remote_version;
`wget --quiet --timestamping --directory-prefix=data https://data.iana.org/time-zones/tzdb-latest.tar.lz`;
`tar --extract --file=data/tzdb-latest.tar.lz --directory=data`;
`make DESTDIR=../ TZDIR=zones-$local_version --directory=data/tzdb-$local_version posix_only`;
print("Yay. Now you can run\n ./timezone-gen.pl --base=data/zones-$local_version --output=timezones-$local_version.conf.xml")

View File

@ -0,0 +1,4 @@
tzdb-*
zones-*
version
tzdb-latest.tar.lz

View File

@ -0,0 +1,61 @@
#!/usr/bin/perl
sub fixTzstr {
# switch_time.c expects POSIX-style TZ rule, but it won't process quoted TZ
# rules that look like this: <-04>4 or <-04>4<-03>
# See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
# Instead it defaults to UTC for these values. Here we process the quoted
# values and convert them into letters. If the zone name has "GMT", we use
# that as the replacement prefix, otherwise a default "STD" is used. Zones
# that have a quoted suffix have their suffix replaced with "DST".
my ($tzstr, $name) = @_;
if ( $tzstr =~ /(<(?<std>[^>]+)>)([^<]+)(?<dst><.+>)?(?<rest>.+)?/ ) {
my ($tzprefix, $tzsuffix, $tzrest, $offset, $offsetprefix) = ("") x 5;
if ( defined($+{std}) ) {
my $std = $+{std};
if ( lc($name) =~ m/gmt/) {
$tzprefix = "GMT";
} else {
$tzprefix = "STD";
}
if ( $std =~ m/\+/ ) {
$offset = sprintf "%d", $std =~ s/\+//r;
$offsetprefix = "-";
} else {
$offset = sprintf "%d", $std =~ s/\-//r;
}
my @chars = split(//, $offset);
if ( @chars > 2 ) {
my $hours = $chars[-3];
if ( defined( $chars[-4] ) ) {
$hours = $chars[-4].$hours;
}
$offset = $hours.":".$chars[-2].$chars[-1];
}
$offset = $offsetprefix.$offset;
}
if ( defined($+{dst}) ) {
$tzsuffix = "DST";
}
if ( defined($+{rest}) ) {
$tzrest = $+{rest};
}
return $tzprefix.$offset.$tzsuffix.$tzrest;
}
return $tzstr;
}
1;

View File

@ -0,0 +1,65 @@
#!/usr/bin/perl
=pod
Tests to verify that the provided modifications to timezone formats produce
the correct results. The first set of tests verify the fixTzstr subroutine
converts the quoted values to something that won't make FreeSWITCH default to
UTC.
The second set of tests confirms that those timezone changes actually produce
the correct timestamps.
Make sure FreeSWITCH already has already loaded the timezones.conf.xml that you
want to test.
To run tests:
TIMEZONES_XML_PATH=path/to/timezones.conf.xml prove tests.pl
=cut
use strict;
use warnings;
use Test::More;
use ESL;
use XML::LibXML::Reader;
require "./fix-tzstr.pl";
use Env qw(TIMEZONES_XML_PATH);
die "The TIMEZONES_XML_PATH environment variable must be set to test timezones." unless ( defined($TIMEZONES_XML_PATH) );
ok( fixTzstr("<-02>2", "doesntmatterhere") eq "STD2" );
ok( fixTzstr("EST5EDT,M3.2.0,M11.1.0", "US/Eastern") eq "EST5EDT,M3.2.0,M11.1.0" );
ok( fixTzstr("<+11>-11", "GMT-11") eq "GMT-11" );
ok( fixTzstr("<-02>2<-01>,M3.5.0/-1,M10.5.0/0", "America/Godthab") eq "STD2DST,M3.5.0/-1,M10.5.0/0" );
my $test_count = 4;
my $tz_fmt = "%Y-%m-%d %H:%M:%S";
my $c = new ESL::ESLconnection("127.0.0.1", "8021", "ClueCon");
$c->api("reloadxml")->getBody();
my $epoch = $c->api("strepoch")->getBody();
run_tests($epoch);
run_tests("1699613236"); # testing DST, add more epochs as needed
sub run_tests {
my $epoch = shift;
my $reader = XML::LibXML::Reader->new(location => $TIMEZONES_XML_PATH);
while ($reader->read) {
my $tag = $reader;
if ( $tag->name eq "zone" && $tag->hasAttributes() ) {
my $zn = $tag->getAttribute("name");
my $cmd = `TZ='$zn' date +'$tz_fmt' --date='\@$epoch'`;
my $sys_time = $cmd =~ s/\n//r;
my $fs_time = $c->api("strftime_tz $zn $epoch|$tz_fmt")->getBody();
ok ( $sys_time eq $fs_time, $zn ) or diag(
" (sys) $sys_time\t(fs) $fs_time"
);
$test_count++;
}
}
}
done_testing($test_count);

View File

@ -1,10 +1,12 @@
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use XML::Entities;
use HTML::Entities;
require "./fix-tzstr.pl";
my $base = "/usr/share/zoneinfo";
my $output = "timezones.conf.xml";
@ -18,7 +20,7 @@ my $res = GetOptions(
"base=s" => \$base,
"debug+" => \$debug,
"help" => \$help,
"output" => \$output
"output=s" => \$output
);
if ( !$res || $help ) {
print "$0 [--base=/usr/share/zoneinfo] [--output=timezones.conf.xml] [--debug] [--help]\n";
@ -64,7 +66,9 @@ foreach my $name ( sort( keys(%name_to_file) ) ) {
next;
}
$zones{$name} = pop(@strings);
my $tzstr = fixTzstr( pop(@strings), $name );
$zones{$name} = $tzstr;
}
open( my $out, ">$output" );
@ -83,7 +87,7 @@ foreach my $zone ( sort( keys(%zones) ) ) {
}
$lastprefix = $newprefix;
print $out "\t<zone name=\"$zone\" value=\"$str\" />\n";
print $out " " x 8, "<zone name=\"$zone\" value=\"$str\" />\n";
}
print $out " " x 4, "</timezones>\n";
print $out "</configuration>\n";