From 40d241b13821c034aae4248e343741603188e5a6 Mon Sep 17 00:00:00 2001 From: David Hull Date: Sat, 21 Nov 2015 20:30:56 +0000 Subject: [PATCH 1/8] Handle fmt_shift(0) -> 0. tz_shift(_, "UTC") returns 0, but fmt_shift/1 previously did not handle that. An alternative fix would be for tz_shift(_, "UTC") to return {'+', 0, 0}. --- src/localtime.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/localtime.erl b/src/localtime.erl index 42781e3..c3eee0c 100644 --- a/src/localtime.erl +++ b/src/localtime.erl @@ -205,6 +205,8 @@ fmt_shift({'+', H, M}) -> H * 60 + M; fmt_shift({'-', H, M}) -> -(H * 60 + M); +fmt_shift(0) -> + 0; fmt_shift(Any) -> throw(Any). From 7904dd3405d19b8b161f596c7293741ab19917de Mon Sep 17 00:00:00 2001 From: David Hull Date: Sat, 21 Nov 2015 20:56:10 +0000 Subject: [PATCH 2/8] tz-erl: Fix offset_minutes($o) when $o just digits is hours offset. Previouly when offset_minute's argument was just digits (for example, "+2" instead of "+2:00"), the argument was incorrectly interpreted as minutes when it should be as hours. --- db/tz-erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/db/tz-erl b/db/tz-erl index 4936553..24e0af1 100755 --- a/db/tz-erl +++ b/db/tz-erl @@ -145,15 +145,17 @@ sub process_data { } } +# Converts an offset in the format "[+-]?HH:MM" or "[+-]?HH" into minutes. +# For example, "2:00" -> 120, "-0:30" -> -30, "+5" -> 300. sub offset_minutes { my ($off, $adj) = @_; my $convert_offset = sub { - my $m = $_[0]; - if ($m =~ m/^([\+\-]?)(?:(\d+):)?(\d+)$/) { - $m = (defined $2 ? $2 : 0) * MPH + $3; - if ($1 eq '-') { $m = -$m; } - } + $_[0] =~ m/^([\+\-]?)(\d+)(?::(\d+))?$/ + or die "offset \"$_[0]\" did not match"; + my $m = $2 * MPH; + if (defined $3) { $m += $3; } + if ($1 eq '-') { $m = -$m; } return $m; }; From b36ea2cec2b5fac3dbbbc64898262057cde2f888 Mon Sep 17 00:00:00 2001 From: David Hull Date: Sat, 21 Nov 2015 21:17:01 +0000 Subject: [PATCH 3/8] Fix offsets for "GMT+X" and "GMT-X" timezones. --- include/tz_database.hrl | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/include/tz_database.hrl b/include/tz_database.hrl index 2539526..add4841 100644 --- a/include/tz_database.hrl +++ b/include/tz_database.hrl @@ -427,33 +427,33 @@ {"Eire",{"GMT","GMT"},{"IST","IST"},0,60,{last,sun,mar},{1,0},{last,sun,oct},{2,0}}, {"Etc/GMT",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, {"Etc/GMT+0",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+1",{"GMT+1","GMT+1"},undef,-1,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+10",{"GMT+10","GMT+10"},undef,-10,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+11",{"GMT+11","GMT+11"},undef,-11,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+12",{"GMT+12","GMT+12"},undef,-12,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+2",{"GMT+2","GMT+2"},undef,-2,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+3",{"GMT+3","GMT+3"},undef,-3,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+4",{"GMT+4","GMT+4"},undef,-4,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+5",{"GMT+5","GMT+5"},undef,-5,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+6",{"GMT+6","GMT+6"},undef,-6,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+7",{"GMT+7","GMT+7"},undef,-7,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+8",{"GMT+8","GMT+8"},undef,-8,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+9",{"GMT+9","GMT+9"},undef,-9,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+1",{"GMT+1","GMT+1"},undef,-60,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+10",{"GMT+10","GMT+10"},undef,-600,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+11",{"GMT+11","GMT+11"},undef,-660,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+12",{"GMT+12","GMT+12"},undef,-720,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+2",{"GMT+2","GMT+2"},undef,-120,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+3",{"GMT+3","GMT+3"},undef,-180,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+4",{"GMT+4","GMT+4"},undef,-240,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+5",{"GMT+5","GMT+5"},undef,-300,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+6",{"GMT+6","GMT+6"},undef,-360,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+7",{"GMT+7","GMT+7"},undef,-420,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+8",{"GMT+8","GMT+8"},undef,-480,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+9",{"GMT+9","GMT+9"},undef,-540,0,undef,{0,0},undef,{0,0}}, {"Etc/GMT-0",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-1",{"GMT-1","GMT-1"},undef,1,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-10",{"GMT-10","GMT-10"},undef,10,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-11",{"GMT-11","GMT-11"},undef,11,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-12",{"GMT-12","GMT-12"},undef,12,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-13",{"GMT-13","GMT-13"},undef,13,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-14",{"GMT-14","GMT-14"},undef,14,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-2",{"GMT-2","GMT-2"},undef,2,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-3",{"GMT-3","GMT-3"},undef,3,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-4",{"GMT-4","GMT-4"},undef,4,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-5",{"GMT-5","GMT-5"},undef,5,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-6",{"GMT-6","GMT-6"},undef,6,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-7",{"GMT-7","GMT-7"},undef,7,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-8",{"GMT-8","GMT-8"},undef,8,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-9",{"GMT-9","GMT-9"},undef,9,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-1",{"GMT-1","GMT-1"},undef,60,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-10",{"GMT-10","GMT-10"},undef,600,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-11",{"GMT-11","GMT-11"},undef,660,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-12",{"GMT-12","GMT-12"},undef,720,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-13",{"GMT-13","GMT-13"},undef,780,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-14",{"GMT-14","GMT-14"},undef,840,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-2",{"GMT-2","GMT-2"},undef,120,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-3",{"GMT-3","GMT-3"},undef,180,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-4",{"GMT-4","GMT-4"},undef,240,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-5",{"GMT-5","GMT-5"},undef,300,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-6",{"GMT-6","GMT-6"},undef,360,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-7",{"GMT-7","GMT-7"},undef,420,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-8",{"GMT-8","GMT-8"},undef,480,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-9",{"GMT-9","GMT-9"},undef,540,0,undef,{0,0},undef,{0,0}}, {"Etc/GMT0",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, {"Etc/Greenwich",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, {"Etc/UCT",{"UCT","UCT"},undef,0,0,undef,{0,0},undef,{0,0}}, From 031652afd49583192585b5c6f13f297ef0f06379 Mon Sep 17 00:00:00 2001 From: David Hull Date: Sat, 21 Nov 2015 21:56:04 +0000 Subject: [PATCH 4/8] tz-erl: Include month and day in future 'until' check. --- db/tz-erl | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/db/tz-erl b/db/tz-erl index 24e0af1..a5534c3 100755 --- a/db/tz-erl +++ b/db/tz-erl @@ -258,9 +258,13 @@ sub zone_line { # We ignore any zone line that has a definite until (end) time that # is in the past. if (defined $until) { - my $until_year = ($until =~ m/^(\d+)/)[0]; - if ($until_year >= $current_year) { - die "until $until not handled"; + my ($until_year, $until_month, $until_day) = split_ymd($until); + if (($until_year > $current_year) || + (($until_year == $current_year) && + (($until_month > $current_month) || + (($until_month == $current_month) && + ($until_day >= $current_day))))) { + "future until \"$until\" not handled"; } return; } @@ -429,6 +433,23 @@ INIT { %dow_from_name= map { $dow_to_name[$_] => $_ } (0..$#dow_to_name); } +sub split_ymd { + my ($ymd) = @_; + $ymd =~ m/^(\d+)(?:\s+(\w+)(?:\s+(\d+)))?/ + or die "parse \"$ymd\" for ymd failed"; + my $year = $1; + my $month = do { + if (defined $2) { + defined $mon_from_name{$2} or die "parse \"$ymd\" for month failed"; + $mon_from_name{$2}; + } else { + 0 + } + }; + my $day = defined $3 ? $3 : 0; + return ($year, $month, $day); +} + sub on_to_day_of_month { my ($on, $year, $month) = @_; From 73665bea0b2da9ab6c0e65089c872f534c20948e Mon Sep 17 00:00:00 2001 From: David Hull Date: Sat, 21 Nov 2015 22:48:36 +0000 Subject: [PATCH 5/8] tz-erl: Better document problem when a zone begins or ends using DST. Worked around 2015 Uraguay problem by allowing database to be generated for 2016 instead (with "make DATE=2016-01-01"). --- db/Makefile | 3 ++- db/tz-erl | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/db/Makefile b/db/Makefile index 0da5f4c..44071f0 100644 --- a/db/Makefile +++ b/db/Makefile @@ -1,9 +1,10 @@ TZDIR=tzdata TZ_FILES=$(addprefix $(TZDIR)/, africa antarctica asia australasia backward etcetera europe northamerica southamerica) +tzout: DATE := $(shell date +%F) tzout: $(TZ_FILES) TZ_VERSION=`perl -n -e 'm/^VERSION\s*=\s*(\S+)/ and print $$1;' $(TZDIR)/Makefile`; \ - ./tz-erl --version $$TZ_VERSION -o $@ $^ + ./tz-erl --version $$TZ_VERSION --date $(DATE) -o $@ $^ $(TZ_FILES): $(TZDIR) diff --git a/db/tz-erl b/db/tz-erl index a5534c3..3f41e21 100755 --- a/db/tz-erl +++ b/db/tz-erl @@ -292,7 +292,10 @@ sub zone_line { $rule1 = $rule2 = RULE_NULL; } elsif (scalar(@rules) == 1) { # One active rule. This is a year that DST started or stopped - # being observed + # being observed. erlang_localtime doesn't handle this. If DST + # stopped being observed in this year, don't output a DST rule. + # If DST started being observed, do. (Except that we don't handle + # this yet.) print STDERR Data::Dump::dump(\@rules), "\n"; die "one rule for $name"; $name1 = zonename($format, $rules[0]->[RULE_LETTERS], undef); From 1cb03033855352dbcab741e167e01325817509ad Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Sat, 5 Dec 2015 17:27:21 +0800 Subject: [PATCH 6/8] Fix GMT+X / GMT-X issue --- include/tz_database.hrl | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/include/tz_database.hrl b/include/tz_database.hrl index add4841..7e6ac2f 100644 --- a/include/tz_database.hrl +++ b/include/tz_database.hrl @@ -427,33 +427,33 @@ {"Eire",{"GMT","GMT"},{"IST","IST"},0,60,{last,sun,mar},{1,0},{last,sun,oct},{2,0}}, {"Etc/GMT",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, {"Etc/GMT+0",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+1",{"GMT+1","GMT+1"},undef,-60,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+10",{"GMT+10","GMT+10"},undef,-600,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+11",{"GMT+11","GMT+11"},undef,-660,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+12",{"GMT+12","GMT+12"},undef,-720,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+2",{"GMT+2","GMT+2"},undef,-120,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+3",{"GMT+3","GMT+3"},undef,-180,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+4",{"GMT+4","GMT+4"},undef,-240,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+5",{"GMT+5","GMT+5"},undef,-300,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+6",{"GMT+6","GMT+6"},undef,-360,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+7",{"GMT+7","GMT+7"},undef,-420,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+8",{"GMT+8","GMT+8"},undef,-480,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT+9",{"GMT+9","GMT+9"},undef,-540,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+1",{"GMT+1","GMT+1"},undef,60,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+10",{"GMT+10","GMT+10"},undef,600,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+11",{"GMT+11","GMT+11"},undef,660,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+12",{"GMT+12","GMT+12"},undef,720,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+2",{"GMT+2","GMT+2"},undef,120,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+3",{"GMT+3","GMT+3"},undef,180,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+4",{"GMT+4","GMT+4"},undef,240,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+5",{"GMT+5","GMT+5"},undef,300,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+6",{"GMT+6","GMT+6"},undef,360,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+7",{"GMT+7","GMT+7"},undef,420,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+8",{"GMT+8","GMT+8"},undef,480,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT+9",{"GMT+9","GMT+9"},undef,540,0,undef,{0,0},undef,{0,0}}, {"Etc/GMT-0",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-1",{"GMT-1","GMT-1"},undef,60,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-10",{"GMT-10","GMT-10"},undef,600,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-11",{"GMT-11","GMT-11"},undef,660,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-12",{"GMT-12","GMT-12"},undef,720,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-13",{"GMT-13","GMT-13"},undef,780,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-14",{"GMT-14","GMT-14"},undef,840,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-2",{"GMT-2","GMT-2"},undef,120,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-3",{"GMT-3","GMT-3"},undef,180,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-4",{"GMT-4","GMT-4"},undef,240,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-5",{"GMT-5","GMT-5"},undef,300,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-6",{"GMT-6","GMT-6"},undef,360,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-7",{"GMT-7","GMT-7"},undef,420,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-8",{"GMT-8","GMT-8"},undef,480,0,undef,{0,0},undef,{0,0}}, - {"Etc/GMT-9",{"GMT-9","GMT-9"},undef,540,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-1",{"GMT-1","GMT-1"},undef,-60,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-10",{"GMT-10","GMT-10"},undef,-600,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-11",{"GMT-11","GMT-11"},undef,-660,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-12",{"GMT-12","GMT-12"},undef,-720,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-13",{"GMT-13","GMT-13"},undef,-780,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-14",{"GMT-14","GMT-14"},undef,-840,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-2",{"GMT-2","GMT-2"},undef,-120,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-3",{"GMT-3","GMT-3"},undef,-180,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-4",{"GMT-4","GMT-4"},undef,-240,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-5",{"GMT-5","GMT-5"},undef,-300,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-6",{"GMT-6","GMT-6"},undef,-360,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-7",{"GMT-7","GMT-7"},undef,-420,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-8",{"GMT-8","GMT-8"},undef,-480,0,undef,{0,0},undef,{0,0}}, + {"Etc/GMT-9",{"GMT-9","GMT-9"},undef,-540,0,undef,{0,0},undef,{0,0}}, {"Etc/GMT0",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, {"Etc/Greenwich",{"GMT","GMT"},undef,0,0,undef,{0,0},undef,{0,0}}, {"Etc/UCT",{"UCT","UCT"},undef,0,0,undef,{0,0},undef,{0,0}}, From 38de0fb581c0441b733786f4badfaae5832a04c2 Mon Sep 17 00:00:00 2001 From: Jack Tang Date: Mon, 11 Jan 2016 10:50:37 +0800 Subject: [PATCH 7/8] get_timezone supports binary input --- src/localtime.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/localtime.erl b/src/localtime.erl index c3eee0c..6a79da1 100644 --- a/src/localtime.erl +++ b/src/localtime.erl @@ -224,14 +224,18 @@ tr_char([H|T], From, To, Acc) -> end. -define(SPACE_CHAR, 32). -get_timezone(TimeZone) -> +get_timezone(TimeZone) when is_binary(TimeZone) -> + get_timezone(erlang:binary_to_list(TimeZone)); +get_timezone(TimeZone) when is_list(TimeZone) -> TimeZoneNoSpaces = tr_char(TimeZone, ?SPACE_CHAR, $_), case dict:find(TimeZoneNoSpaces, ?tz_index) of error -> TimeZoneNoSpaces; {ok, [TZName | _]} -> TZName - end. + end; +get_timezone(_) -> + throw({error, "Timezone should be string/binary"}). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). From a8e18a22867b624ccbe4f53ee188833e7a9efe29 Mon Sep 17 00:00:00 2001 From: slepher Date: Fri, 29 Jan 2016 17:33:54 +0800 Subject: [PATCH 8/8] update signal --- src/localtime.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/localtime.erl b/src/localtime.erl index 6a79da1..f6e0eac 100644 --- a/src/localtime.erl +++ b/src/localtime.erl @@ -17,6 +17,7 @@ ,tz_name/2 ,tz_shift/2 ,tz_shift/3 + ,fmt_shift/1 ]). % utc_to_local(UtcDateTime, Timezone) -> LocalDateTime | [LocalDateTime, DstLocalDateTime] | {error, ErrDescr}