alerted to buginess in README. added notes & zic reference. removed local_to_utc conversion for now.
Time comparisons are patently wrong. They do not consider s/w/u/g/z time modifiers, so the comparisons can be off by hours. At least 24 hours within any time or zone change, ezic will give unreliable results. I think a solution can involve "flattening" the data, starting from the earliest time possible for each zone/rule, going forward. There exist ambiguous local times for zones and rules. For example, on Nov 7th, in Los Angeles, the times from 1:00:00am to 1:59:59am were repeated, once for PDT and once for PST. Given a local time in that zone between those times, we cannot determine what UTC time is unambiguously. Similar for Zone transitions where fall-back occurs.
This commit is contained in:
20
README.md
20
README.md
@@ -4,6 +4,18 @@ ezic is a set of erlang utilities for the Olsen timezone database files. See the
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Status
|
||||||
|
------
|
||||||
|
|
||||||
|
2010.11.07
|
||||||
|
|
||||||
|
* The time comparisons don't consider time relativity, so they're plain wrong. Within at least 24 hours of timechange or zone change, the results cannot be trusted.
|
||||||
|
|
||||||
|
2010.11.04
|
||||||
|
|
||||||
|
* ezic is "apparently functional". It has been tested very lightly, a bit by unit tests, but mostly by inspection. It's probably not fit for production use.
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -18,14 +30,6 @@ To maintain ezic's status as a public domain work, all contributions must also b
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Status
|
|
||||||
------
|
|
||||||
|
|
||||||
2010.11.04
|
|
||||||
|
|
||||||
* ezic is "apparently functional". It has been tested very lightly, a bit by unit tests, but mostly by inspection. It's probably not fit for production use.
|
|
||||||
|
|
||||||
|
|
||||||
API
|
API
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
204
notes/local_to_utc.svg
Normal file
204
notes/local_to_utc.svg
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="744.09448819"
|
||||||
|
height="1052.3622047"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.47pre4 r22446"
|
||||||
|
sodipodi:docname="local_to_utc.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 526.18109 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||||
|
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||||
|
id="perspective10" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="409.57174"
|
||||||
|
inkscape:cy="630.26905"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1280"
|
||||||
|
inkscape:window-height="750"
|
||||||
|
inkscape:window-x="-1"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
x="57.984795"
|
||||||
|
y="67.666504"
|
||||||
|
id="text2816"
|
||||||
|
sodipodi:linespacing="100%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2818"
|
||||||
|
x="57.984795"
|
||||||
|
y="67.666504">local_to_utc(LocalDatetime, TimeZoneName)</tspan></text>
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="100%"
|
||||||
|
id="text3613"
|
||||||
|
y="103.6665"
|
||||||
|
x="97.984795"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
y="199.6665"
|
||||||
|
x="97.984795"
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3649" /></text>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 223.24371,165.44825 273.37654,0"
|
||||||
|
id="path3678" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 424.10879,239.18938 270.87616,0"
|
||||||
|
id="path3680" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
x="53.375641"
|
||||||
|
y="208.75282"
|
||||||
|
id="text3682"
|
||||||
|
sodipodi:linespacing="100%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3684"
|
||||||
|
x="53.375641"
|
||||||
|
y="208.75282">Los Angeles</tspan></text>
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
id="path3686"
|
||||||
|
sodipodi:cx="503.05597"
|
||||||
|
sodipodi:cy="166.4584"
|
||||||
|
sodipodi:rx="6.0609155"
|
||||||
|
sodipodi:ry="6.0609155"
|
||||||
|
d="m 509.11688,166.4584 a 6.0609155,6.0609155 0 1 1 -12.12183,0 6.0609155,6.0609155 0 1 1 12.12183,0 z"
|
||||||
|
transform="matrix(0.50000002,0,0,0.50000002,245.52798,82.219044)" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
x="468.22345"
|
||||||
|
y="131.70966"
|
||||||
|
id="text3688"
|
||||||
|
sodipodi:linespacing="100%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3690"
|
||||||
|
x="468.22345"
|
||||||
|
y="131.70966">2:00am Nov 7th, 2010, PDT-7</tspan></text>
|
||||||
|
<path
|
||||||
|
transform="matrix(0.50000002,0,0,0.50000002,173.52798,155.96017)"
|
||||||
|
d="m 509.11688,166.4584 a 6.0609155,6.0609155 0 1 1 -12.12183,0 6.0609155,6.0609155 0 1 1 12.12183,0 z"
|
||||||
|
sodipodi:ry="6.0609155"
|
||||||
|
sodipodi:rx="6.0609155"
|
||||||
|
sodipodi:cy="166.4584"
|
||||||
|
sodipodi:cx="503.05597"
|
||||||
|
id="path3692"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="100%"
|
||||||
|
id="text3694"
|
||||||
|
y="281.70966"
|
||||||
|
x="216.22345"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
y="281.70966"
|
||||||
|
x="216.22345"
|
||||||
|
id="tspan3696"
|
||||||
|
sodipodi:role="line">1:00am Nov 7th, 2010, PST-8</tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#1b13ff;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 496.99505,164.94317 -72.2259,73.74114"
|
||||||
|
id="path3698" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:9,9;stroke-dashoffset:0"
|
||||||
|
d="m 424,141.36218 0,124"
|
||||||
|
id="path3700" />
|
||||||
|
<path
|
||||||
|
id="path3702"
|
||||||
|
d="m 498,141.36218 0,124"
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:9,9;stroke-dashoffset:0" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
x="149"
|
||||||
|
y="397.36218"
|
||||||
|
id="text3704"
|
||||||
|
sodipodi:linespacing="100%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3706"
|
||||||
|
x="149"
|
||||||
|
y="397.36218" /></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
|
||||||
|
x="157"
|
||||||
|
y="388.36218"
|
||||||
|
id="text3708"
|
||||||
|
sodipodi:linespacing="100%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3710"
|
||||||
|
x="157"
|
||||||
|
y="388.36218">On Nov 7th, 2010, 1:00am to 1:59am will happen twice.</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="157"
|
||||||
|
y="404.36218"
|
||||||
|
id="tspan3716" /><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="157"
|
||||||
|
y="420.36218"
|
||||||
|
id="tspan3712">There is no definitive answer to:</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="157"
|
||||||
|
y="436.36218"
|
||||||
|
id="tspan3718"> "What was UTC time given that in Los Angeles </tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="157"
|
||||||
|
y="452.36218"
|
||||||
|
id="tspan3714"> on Nov 7th, 2010, it was 1:30am"</tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="157"
|
||||||
|
y="468.36218"
|
||||||
|
id="tspan3724" /><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="157"
|
||||||
|
y="452.36218"
|
||||||
|
id="tspan3726" /></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.6 KiB |
313
notes/zic.8.txt
Normal file
313
notes/zic.8.txt
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
NAME
|
||||||
|
|
||||||
|
zic - time zone compiler
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
zic [ --version ] [ -v ] [ -d directory ] [ -l localtime ] [
|
||||||
|
-p posixrules ] [ -L leapsecondfilename ] [ -s ] [ -y
|
||||||
|
command ] [ filename ... ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Zic reads text from the file(s) named on the command line
|
||||||
|
and creates the time conversion information files specified
|
||||||
|
in this input. If a filename is -, the standard input is
|
||||||
|
read.
|
||||||
|
|
||||||
|
These options are available:
|
||||||
|
|
||||||
|
--version
|
||||||
|
Output version information and exit.
|
||||||
|
|
||||||
|
-d directory
|
||||||
|
Create time conversion information files in the named
|
||||||
|
directory rather than in the standard directory named
|
||||||
|
below.
|
||||||
|
|
||||||
|
-l timezone
|
||||||
|
Use the given time zone as local time. Zic will act as
|
||||||
|
if the input contained a link line of the form
|
||||||
|
|
||||||
|
Link timezone localtime
|
||||||
|
|
||||||
|
-p timezone
|
||||||
|
Use the given time zone's rules when handling POSIX-
|
||||||
|
format time zone environment variables. Zic will act
|
||||||
|
as if the input contained a link line of the form
|
||||||
|
|
||||||
|
Link timezone posixrules
|
||||||
|
|
||||||
|
-L leapsecondfilename
|
||||||
|
Read leap second information from the file with the
|
||||||
|
given name. If this option is not used, no leap second
|
||||||
|
information appears in output files.
|
||||||
|
|
||||||
|
-v Complain if a year that appears in a data file is
|
||||||
|
outside the range of years representable by time(2)
|
||||||
|
values. Also complain if a time of 24:00 (which cannot
|
||||||
|
be handled by pre-1998 versions of zic) appears in the
|
||||||
|
input.
|
||||||
|
|
||||||
|
-s Limit time values stored in output files to values that
|
||||||
|
are the same whether they're taken to be signed or
|
||||||
|
unsigned. You can use this option to generate SVVS-
|
||||||
|
compatible files.
|
||||||
|
|
||||||
|
-y command
|
||||||
|
Use the given command rather than yearistype when
|
||||||
|
checking year types (see below).
|
||||||
|
|
||||||
|
Input lines are made up of fields. Fields are separated
|
||||||
|
from one another by any number of white space characters.
|
||||||
|
Leading and trailing white space on input lines is ignored.
|
||||||
|
An unquoted sharp character (#) in the input introduces a
|
||||||
|
comment which extends to the end of the line the sharp
|
||||||
|
character appears on. White space characters and sharp
|
||||||
|
characters may be enclosed in double quotes (") if they're
|
||||||
|
to be used as part of a field. Any line that is blank
|
||||||
|
(after comment stripping) is ignored. Non-blank lines are
|
||||||
|
expected to be of one of three types: rule lines, zone
|
||||||
|
lines, and link lines.
|
||||||
|
|
||||||
|
Names (such as month names) must be in English and are case
|
||||||
|
insensitive. Abbreviations, if used, must be unambiguous in
|
||||||
|
context.
|
||||||
|
|
||||||
|
A rule line has the form
|
||||||
|
|
||||||
|
Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
Rule US 1967 1973 - Apr lastSun 2:00 1:00 D
|
||||||
|
|
||||||
|
The fields that make up a rule line are:
|
||||||
|
|
||||||
|
NAME Gives the (arbitrary) name of the set of rules this
|
||||||
|
rule is part of.
|
||||||
|
|
||||||
|
FROM Gives the first year in which the rule applies. Any
|
||||||
|
integer year can be supplied; the Gregorian calendar
|
||||||
|
is assumed. The word minimum (or an abbreviation)
|
||||||
|
means the minimum year representable as an integer.
|
||||||
|
The word maximum (or an abbreviation) means the
|
||||||
|
maximum year representable as an integer. Rules can
|
||||||
|
describe times that are not representable as time
|
||||||
|
values, with the unrepresentable times ignored; this
|
||||||
|
allows rules to be portable among hosts with
|
||||||
|
differing time value types.
|
||||||
|
|
||||||
|
TO Gives the final year in which the rule applies. In
|
||||||
|
addition to minimum and maximum (as above), the word
|
||||||
|
only (or an abbreviation) may be used to repeat the
|
||||||
|
value of the FROM field.
|
||||||
|
|
||||||
|
TYPE Gives the type of year in which the rule applies.
|
||||||
|
If TYPE is - then the rule applies in all years
|
||||||
|
between FROM and TO inclusive. If TYPE is something
|
||||||
|
else, then zic executes the command
|
||||||
|
yearistype year type
|
||||||
|
to check the type of a year: an exit status of zero
|
||||||
|
is taken to mean that the year is of the given type;
|
||||||
|
an exit status of one is taken to mean that the year
|
||||||
|
is not of the given type.
|
||||||
|
|
||||||
|
IN Names the month in which the rule takes effect.
|
||||||
|
Month names may be abbreviated.
|
||||||
|
|
||||||
|
ON Gives the day on which the rule takes effect.
|
||||||
|
Recognized forms include:
|
||||||
|
|
||||||
|
5 the fifth of the month
|
||||||
|
lastSun the last Sunday in the month
|
||||||
|
lastMon the last Monday in the month
|
||||||
|
Sun>=8 first Sunday on or after the eighth
|
||||||
|
Sun<=25 last Sunday on or before the 25th
|
||||||
|
|
||||||
|
Names of days of the week may be abbreviated or
|
||||||
|
spelled out in full. Note that there must be no
|
||||||
|
spaces within the ON field.
|
||||||
|
|
||||||
|
AT Gives the time of day at which the rule takes
|
||||||
|
effect. Recognized forms include:
|
||||||
|
|
||||||
|
2 time in hours
|
||||||
|
2:00 time in hours and minutes
|
||||||
|
15:00 24-hour format time (for times after noon)
|
||||||
|
1:28:14 time in hours, minutes, and seconds
|
||||||
|
- equivalent to 0
|
||||||
|
|
||||||
|
where hour 0 is midnight at the start of the day,
|
||||||
|
and hour 24 is midnight at the end of the day. Any
|
||||||
|
of these forms may be followed by the letter w if
|
||||||
|
the given time is local "wall clock" time, s if the
|
||||||
|
given time is local "standard" time, or u (or g or
|
||||||
|
z) if the given time is universal time; in the
|
||||||
|
absence of an indicator, wall clock time is assumed.
|
||||||
|
|
||||||
|
SAVE Gives the amount of time to be added to local
|
||||||
|
standard time when the rule is in effect. This
|
||||||
|
field has the same format as the AT field (although,
|
||||||
|
of course, the w and s suffixes are not used).
|
||||||
|
|
||||||
|
LETTER/S
|
||||||
|
Gives the "variable part" (for example, the "S" or
|
||||||
|
"D" in "EST" or "EDT") of time zone abbreviations to
|
||||||
|
be used when this rule is in effect. If this field
|
||||||
|
is -, the variable part is null.
|
||||||
|
|
||||||
|
A zone line has the form
|
||||||
|
|
||||||
|
Zone NAME GMTOFF RULES/SAVE FORMAT [UNTILYEAR [MONTH [DAY [TIME]]]]
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00
|
||||||
|
|
||||||
|
The fields that make up a zone line are:
|
||||||
|
|
||||||
|
NAME The name of the time zone. This is the name used in
|
||||||
|
creating the time conversion information file for the
|
||||||
|
zone.
|
||||||
|
|
||||||
|
GMTOFF
|
||||||
|
The amount of time to add to UTC to get standard time
|
||||||
|
in this zone. This field has the same format as the
|
||||||
|
AT and SAVE fields of rule lines; begin the field with
|
||||||
|
a minus sign if time must be subtracted from UTC.
|
||||||
|
|
||||||
|
RULES/SAVE
|
||||||
|
The name of the rule(s) that apply in the time zone
|
||||||
|
or, alternately, an amount of time to add to local
|
||||||
|
standard time. If this field is - then standard time
|
||||||
|
always applies in the time zone.
|
||||||
|
|
||||||
|
FORMAT
|
||||||
|
The format for time zone abbreviations in this time
|
||||||
|
zone. The pair of characters %s is used to show where
|
||||||
|
the "variable part" of the time zone abbreviation
|
||||||
|
goes. Alternately, a slash (/) separates standard and
|
||||||
|
daylight abbreviations.
|
||||||
|
|
||||||
|
UNTILYEAR [MONTH [DAY [TIME]]]
|
||||||
|
The time at which the UTC offset or the rule(s) change
|
||||||
|
for a location. It is specified as a year, a month, a
|
||||||
|
day, and a time of day. If this is specified, the
|
||||||
|
time zone information is generated from the given UTC
|
||||||
|
offset and rule change until the time specified. The
|
||||||
|
month, day, and time of day have the same format as
|
||||||
|
the IN, ON, and AT fields of a rule; trailing fields
|
||||||
|
can be omitted, and default to the earliest possible
|
||||||
|
value for the missing fields.
|
||||||
|
|
||||||
|
The next line must be a "continuation" line; this has
|
||||||
|
the same form as a zone line except that the string
|
||||||
|
"Zone" and the name are omitted, as the continuation
|
||||||
|
line will place information starting at the time
|
||||||
|
specified as the "until" information in the previous
|
||||||
|
line in the file used by the previous line.
|
||||||
|
Continuation lines may contain "until" information,
|
||||||
|
just as zone lines do, indicating that the next line
|
||||||
|
is a further continuation.
|
||||||
|
|
||||||
|
A link line has the form
|
||||||
|
|
||||||
|
Link LINK-FROM LINK-TO
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
Link Europe/Istanbul Asia/Istanbul
|
||||||
|
|
||||||
|
The LINK-FROM field should appear as the NAME field in some
|
||||||
|
zone line; the LINK-TO field is used as an alternate name
|
||||||
|
for that zone.
|
||||||
|
|
||||||
|
Except for continuation lines, lines may appear in any order
|
||||||
|
in the input.
|
||||||
|
|
||||||
|
Lines in the file that describes leap seconds have the
|
||||||
|
following form:
|
||||||
|
|
||||||
|
Leap YEAR MONTH DAY HH:MM:SS CORR R/S
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
Leap 1974 Dec 31 23:59:60 + S
|
||||||
|
|
||||||
|
The YEAR, MONTH, DAY, and HH:MM:SS fields tell when the leap
|
||||||
|
second happened. The CORR field should be "+" if a second
|
||||||
|
was added or "-" if a second was skipped. The R/S field
|
||||||
|
should be (an abbreviation of) "Stationary" if the leap
|
||||||
|
second time given by the other fields should be interpreted
|
||||||
|
as UTC or (an abbreviation of) "Rolling" if the leap second
|
||||||
|
time given by the other fields should be interpreted as
|
||||||
|
local wall clock time.
|
||||||
|
|
||||||
|
EXTENDED EXAMPLE
|
||||||
|
Here is an extended example of zic input, intended to
|
||||||
|
illustrate many of its features.
|
||||||
|
|
||||||
|
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
|
||||||
|
Rule Swiss 1940 only - Nov 2 0:00 1:00 S
|
||||||
|
Rule Swiss 1940 only - Dec 31 0:00 0 -
|
||||||
|
Rule Swiss 1941 1942 - May Sun>=1 2:00 1:00 S
|
||||||
|
Rule Swiss 1941 1942 - Oct Sun>=1 0:00 0
|
||||||
|
Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S
|
||||||
|
Rule EU 1977 only - Sep lastSun 1:00u 0 -
|
||||||
|
Rule EU 1978 only - Oct 1 1:00u 0 -
|
||||||
|
Rule EU 1979 1995 - Sep lastSun 1:00u 0 -
|
||||||
|
Rule EU 1981 max - Mar lastSun 1:00u 1:00 S
|
||||||
|
Rule EU 1996 max - Oct lastSun 1:00u 0 -
|
||||||
|
|
||||||
|
# Zone NAME GMTOFF RULES FORMAT UNTIL
|
||||||
|
Zone Europe/Zurich 0:34:08 - LMT 1848 Sep 12
|
||||||
|
0:29:44 - BMT 1894 Jun
|
||||||
|
1:00 Swiss CE%sT 1981
|
||||||
|
1:00 EU CE%sT
|
||||||
|
|
||||||
|
Link Europe/Zurich Switzerland
|
||||||
|
|
||||||
|
In this example, the zone is named Europe/Zurich but it has
|
||||||
|
an alias as Switzerland. Zurich was 34 minutes and 8
|
||||||
|
seconds west of GMT until 1848-09-12 at 00:00, when the
|
||||||
|
offset changed to 29 minutes and 44 seconds. After
|
||||||
|
1894-06-01 at 00:00 Swiss daylight saving rules (defined
|
||||||
|
with lines beginning with "Rule Swiss") apply, and the GMT
|
||||||
|
offset became one hour. From 1981 to the present, EU
|
||||||
|
daylight saving rules have applied, and the UTC offset has
|
||||||
|
remained at one hour.
|
||||||
|
|
||||||
|
In 1940, daylight saving time applied from November 2 at
|
||||||
|
00:00 to December 31 at 00:00. In 1941 and 1942, daylight
|
||||||
|
saving time applied from the first Sunday in May at 02:00 to
|
||||||
|
the first Sunday in October at 00:00. The pre-1981 EU
|
||||||
|
daylight-saving rules have no effect here, but are included
|
||||||
|
for completeness. Since 1981, daylight saving has begun on
|
||||||
|
the last Sunday in March at 01:00 UTC. Until 1995 it ended
|
||||||
|
the last Sunday in September at 01:00 UTC, but this changed
|
||||||
|
to the last Sunday in October starting in 1996.
|
||||||
|
|
||||||
|
For purposes of display, "LMT" and "BMT" were initially
|
||||||
|
used, respectively. Since Swiss rules and later EU rules
|
||||||
|
were applied, the display name for the timezone has been CET
|
||||||
|
for standard time and CEST for daylight saving time.
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
For areas with more than two types of local time, you may
|
||||||
|
need to use local standard time in the AT field of the
|
||||||
|
earliest transition time's rule to ensure that the earliest
|
||||||
|
transition time recorded in the compiled file is correct.
|
||||||
|
|
||||||
|
If, for a particular zone, a clock advance caused by the
|
||||||
|
start of daylight saving coincides with and is equal to a
|
||||||
|
clock retreat caused by a change in UTC offset, zic produces
|
||||||
|
a single transition to daylight saving at the new UTC offset
|
||||||
|
(without any change in wall clock time). To get separate
|
||||||
|
transitions use multiple zone continuation lines specifying
|
||||||
|
transition instants using universal time.
|
||||||
|
|
||||||
|
FILE
|
||||||
|
/usr/local/etc/zoneinfo standard directory used for
|
||||||
|
created files
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
newctime(3), tzfile(5), zdump(8)
|
||||||
46
src/ezic.erl
46
src/ezic.erl
@@ -4,8 +4,8 @@
|
|||||||
-export([
|
-export([
|
||||||
localtime/1
|
localtime/1
|
||||||
, utc_to_local/2
|
, utc_to_local/2
|
||||||
, utc_from_local/2
|
% , local_to_utc/2
|
||||||
, zone_convert/3
|
% , zone_convert/3
|
||||||
, next_timechange/1
|
, next_timechange/1
|
||||||
, next_timechange/2
|
, next_timechange/2
|
||||||
]).
|
]).
|
||||||
@@ -28,28 +28,28 @@ localtime(TzName) ->
|
|||||||
utc_to_local(erlang:universaltime(), TzName).
|
utc_to_local(erlang:universaltime(), TzName).
|
||||||
|
|
||||||
|
|
||||||
utc_to_local(Datetime, TzName) ->
|
utc_to_local(UTCDatetime, TzName) ->
|
||||||
{ok, Zone, Rule}= current(Datetime, TzName),
|
{ok, Zone, Rule}= current_as_of_utc(UTCDatetime, TzName),
|
||||||
SecDiff= offset(Zone, Rule),
|
SecDiff= time_offset(Zone, Rule),
|
||||||
date_add(Datetime, SecDiff).
|
date_add(UTCDatetime, SecDiff).
|
||||||
|
|
||||||
|
|
||||||
utc_from_local(Datetime, TzName) ->
|
%% local_to_utc(Datetime, TzName) ->
|
||||||
{ok, Zone, Rule}= current(Datetime, TzName),
|
%% {ok, Zone, Rule}= current_as_of_local(Datetime, TzName),
|
||||||
SecDiff= offset(Zone, Rule),
|
%% SecDiff= time_offset(Zone, Rule),
|
||||||
date_subtract(Datetime, SecDiff).
|
%% date_subtract(Datetime, SecDiff).
|
||||||
|
|
||||||
|
|
||||||
zone_convert(Datetime, FromTimeZone, ToTimeZone) ->
|
%% zone_convert(Datetime, FromTimeZone, ToTimeZone) ->
|
||||||
UTC= utc_from_local(Datetime, FromTimeZone),
|
%% UTC= local_to_utc(Datetime, FromTimeZone),
|
||||||
utc_to_local(UTC, ToTimeZone).
|
%% utc_to_local(UTC, ToTimeZone).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
next_timechange(TzName) ->
|
next_timechange(TzName) ->
|
||||||
next_timechange(localtime(TzName), TzName).
|
next_timechange(erlang:universaltime(), TzName).
|
||||||
|
|
||||||
next_timechange(Datetime, TzName) ->
|
next_timechange(UtcDatetime, TzName) ->
|
||||||
not_done.
|
not_done.
|
||||||
|
|
||||||
|
|
||||||
@@ -84,23 +84,19 @@ test() ->
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
|
||||||
current(Datetime, TzName) ->
|
current_as_of_utc(UTCDatetime, TzName) ->
|
||||||
Zones= ezic_db:zones(TzName),
|
CZone= ezic_zone:current_as_of_utc(UTCDatetime, TzName),
|
||||||
% ?debug("All Zones: ~p", [Zones]),
|
|
||||||
CZone= ezic_zone:current(Datetime, Zones),
|
|
||||||
% ?debug("Current Zone: ~p", [CZone]),
|
|
||||||
|
|
||||||
RuleName= CZone#zone.rule,
|
RuleName= CZone#zone.rule,
|
||||||
Rules= ezic_db:rules(RuleName),
|
Rules= ezic_db:rules(RuleName),
|
||||||
% ?debug("All Rules: ~p", [Rules]),
|
CRule= ezic_rule:current_as_of_utc(UTCDatetime, Rules),
|
||||||
CRule= ezic_rule:current(Datetime, Rules),
|
|
||||||
% ?debug("Current Rule: ~p", [CRule]),
|
|
||||||
{ok, CZone, CRule}.
|
{ok, CZone, CRule}.
|
||||||
|
|
||||||
|
|
||||||
|
%%current_as_of_local(Datetime, TzName) ->
|
||||||
|
%% not_done.
|
||||||
|
|
||||||
|
|
||||||
offset(Zone, Rule) ->
|
time_offset(Zone, Rule) ->
|
||||||
OffsetSec= ezic_zone:offset_sec(Zone),
|
OffsetSec= ezic_zone:offset_sec(Zone),
|
||||||
DSTSec= ezic_rule:dst_sec(Rule),
|
DSTSec= ezic_rule:dst_sec(Rule),
|
||||||
OffsetSec + DSTSec.
|
OffsetSec + DSTSec.
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
% returns the concrete date tuple for a given rule and year, irrespective of zone
|
% returns the date tuple for a given rule and year
|
||||||
|
% same for all timezones.
|
||||||
% e.g. -> {Y,M,D}
|
% e.g. -> {Y,M,D}
|
||||||
for(#rule{in=M, on={last, D}}, Y) ->
|
for(#rule{in=M, on={last, D}}, Y) ->
|
||||||
last_day_of(D, Y,M);
|
last_day_of(D, Y,M);
|
||||||
@@ -24,13 +25,15 @@ for(#rule{in=M, on=#tzon{day=Day, filter=Filter}}, Y) ->
|
|||||||
first_day_limited(Day, Filter, Y,M).
|
first_day_limited(Day, Filter, Y,M).
|
||||||
|
|
||||||
|
|
||||||
|
% returns the date on which the last Day (sun,mon,tue,etc.) occurs in a given month/year
|
||||||
|
% same for all timezones
|
||||||
last_day_of(Day, Y,M) ->
|
last_day_of(Day, Y,M) ->
|
||||||
LastDay= calendar:last_day_of_the_month(Y,M),
|
LastDay= calendar:last_day_of_the_month(Y,M),
|
||||||
previous_day(Day, {Y,M,LastDay}).
|
previous_day(Day, {Y,M,LastDay}).
|
||||||
|
|
||||||
|
|
||||||
% returns a date tuple {Y,M,D} representing the first available date, given the filter
|
% returns a date tuple {Y,M,D} representing the first available date, given the filter
|
||||||
|
% same for all timezones
|
||||||
first_day_limited(Day, {geq, N}, Y,M) ->
|
first_day_limited(Day, {geq, N}, Y,M) ->
|
||||||
next_day(Day, {Y,M,N});
|
next_day(Day, {Y,M,N});
|
||||||
first_day_limited(Day, {leq, N}, Y,M) ->
|
first_day_limited(Day, {leq, N}, Y,M) ->
|
||||||
@@ -38,7 +41,8 @@ first_day_limited(Day, {leq, N}, Y,M) ->
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% Returns the soonest date on which Day (sun/mon/tue/etc.) occurs BEFORE the given date.
|
||||||
|
% same for all timezones
|
||||||
previous_day(Day, Date={Y,M,D}) ->
|
previous_day(Day, Date={Y,M,D}) ->
|
||||||
Daynum= day_to_num(Day),
|
Daynum= day_to_num(Day),
|
||||||
LeqDoW= calendar:day_of_the_week(Date),
|
LeqDoW= calendar:day_of_the_week(Date),
|
||||||
@@ -56,6 +60,8 @@ previous_day(Day, Date={Y,M,D}) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
% Returns the soonest date on which Day (sun/mon/tue/etc.) occurs AFTER the given date.
|
||||||
|
% same for all timezones
|
||||||
next_day(Day, Date={Y,M,D}) ->
|
next_day(Day, Date={Y,M,D}) ->
|
||||||
Daynum= day_to_num(Day),
|
Daynum= day_to_num(Day),
|
||||||
LeqDoW= calendar:day_of_the_week(Date),
|
LeqDoW= calendar:day_of_the_week(Date),
|
||||||
@@ -115,6 +121,8 @@ compare_datetimes_normal(current, current) -> true;
|
|||||||
compare_datetimes_normal(current, _) -> false;
|
compare_datetimes_normal(current, _) -> false;
|
||||||
compare_datetimes_normal(_,current) -> true;
|
compare_datetimes_normal(_,current) -> true;
|
||||||
|
|
||||||
|
% @bug this is not true if the times are from different time zones
|
||||||
|
% @todo check types for D1 and D2
|
||||||
compare_datetimes_normal({D1, _}, {D2, _}) when D1 < D2 ->
|
compare_datetimes_normal({D1, _}, {D2, _}) when D1 < D2 ->
|
||||||
true;
|
true;
|
||||||
compare_datetimes_normal({D1, _}, {D2, _}) when D1 > D2 ->
|
compare_datetimes_normal({D1, _}, {D2, _}) when D1 > D2 ->
|
||||||
@@ -144,6 +152,7 @@ normalize(only) -> only;
|
|||||||
normalize(maximum) -> maximum;
|
normalize(maximum) -> maximum;
|
||||||
normalize(minimum) -> minimum;
|
normalize(minimum) -> minimum;
|
||||||
|
|
||||||
|
|
||||||
normalize(Y) when is_integer(Y) ->
|
normalize(Y) when is_integer(Y) ->
|
||||||
{{Y,1,1}, #tztime{}};
|
{{Y,1,1}, #tztime{}};
|
||||||
normalize(Date={_,_,D}) when is_integer(D) ->
|
normalize(Date={_,_,D}) when is_integer(D) ->
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-include("include/ezic.hrl").
|
-include("include/ezic.hrl").
|
||||||
|
|
||||||
|
|
||||||
-export([current/2, relevant/2, sort/2]).
|
-export([current/2, current_set/2, sort/2]).
|
||||||
-export([from_time/1, dst_sec/1]).
|
-export([from_time/1, dst_sec/1]).
|
||||||
|
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@
|
|||||||
current(_, []) ->
|
current(_, []) ->
|
||||||
none;
|
none;
|
||||||
current(Now, Rules) ->
|
current(Now, Rules) ->
|
||||||
RelevantRules= lists:filter(fun(R)-> relevant(Now, R) end, Rules),
|
Current_SetRules= lists:filter(fun(R)-> current_set(Now, R) end, Rules),
|
||||||
% ?debug("RelevantRules: ~p", [RelevantRules]),
|
% ?debug("Current_SetRules: ~p", [Current_SetRules]),
|
||||||
SRules= lists:sort(fun sort/2, RelevantRules),
|
SRules= lists:sort(fun sort/2, Current_SetRules),
|
||||||
CRule= choose_rule(SRules),
|
CRule= choose_rule(SRules),
|
||||||
CRule.
|
CRule.
|
||||||
|
|
||||||
@@ -27,15 +27,15 @@ sort(R1, R2) ->
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
relevant(Now, Rule=#rule{from=F, to=T}) ->
|
current_set(Now, Rule=#rule{from=F, to=T}) ->
|
||||||
{{Y,_,_},_} = Now,
|
{{Y,_,_},_} = Now,
|
||||||
case ezic_date:date_between(Y, {F,T}) of
|
case ezic_date:date_between(Y, {F,T}) of
|
||||||
false -> false;
|
false -> false;
|
||||||
true -> relevant2(Now, Rule)
|
true -> current_set2(Now, Rule)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
relevant2(Now, #rule{in=Month, on=Day, at=Time}) ->
|
current_set2(Now, #rule{in=Month, on=Day, at=Time}) ->
|
||||||
{{Y,_,_},_} = Now,
|
{{Y,_,_},_} = Now,
|
||||||
RTime= {{Y, Month, Day}, Time},
|
RTime= {{Y, Month, Day}, Time},
|
||||||
ezic_date:compare_datetimes(RTime, Now).
|
ezic_date:compare_datetimes(RTime, Now).
|
||||||
|
|||||||
@@ -1,16 +1,45 @@
|
|||||||
-module(ezic_zone).
|
-module(ezic_zone).
|
||||||
-include("include/ezic.hrl").
|
-include("include/ezic.hrl").
|
||||||
|
|
||||||
-export([current/2, sort/2, revsort/2, offset_sec/1]).
|
-export([
|
||||||
|
current/1
|
||||||
|
, current_as_of_utc/2
|
||||||
|
, sort/2
|
||||||
|
, revsort/2
|
||||||
|
, offset_sec/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
|
||||||
current(_, []) ->
|
|
||||||
|
|
||||||
|
current(TzName) ->
|
||||||
|
current_as_of_utc(erlang:universaltime(), TzName).
|
||||||
|
|
||||||
|
current_as_of_utc(UTCDatetime, TzName) ->
|
||||||
|
Zones= ezic_db:zones(TzName),
|
||||||
|
get_zone_utc(UTCDatetime, Zones).
|
||||||
|
|
||||||
|
%%current_as_of_local(_Datetime, _TzName) ->
|
||||||
|
%% not_done.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
get_zone_utc(_, []) ->
|
||||||
erlang:error(no_current);
|
erlang:error(no_current);
|
||||||
current(Now, Zones) ->
|
get_zone_utc(UTCDatetime, Zones) ->
|
||||||
SortedZones= lists:sort(fun revsort/2, Zones),
|
SortedZones= lists:sort(fun revsort/2, Zones),
|
||||||
% ?debug("SortedZones: ~p", [SortedZones]),
|
%% [CZone|_]= lists:dropwhile(fun(SZ)-> older_zone_utc(SZ, UTCDatetime) end, SortedZones),
|
||||||
[CZone|_]= lists:dropwhile(fun(SZ)-> older_zone(SZ, Now) end, SortedZones),
|
%% CZone.
|
||||||
CZone.
|
|
||||||
|
|
||||||
|
%% begin with GMT offset.
|
||||||
|
%% foreach rule (oldest first)
|
||||||
|
%% see whether until=(standard|wall|utc) time
|
||||||
|
%%
|
||||||
|
|
||||||
|
not_done.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -20,8 +49,11 @@ sort(#zone{until=U1}, #zone{until=U2}) ->
|
|||||||
revsort(Z1=#zone{}, Z2=#zone{}) ->
|
revsort(Z1=#zone{}, Z2=#zone{}) ->
|
||||||
not sort(Z1, Z2).
|
not sort(Z1, Z2).
|
||||||
|
|
||||||
older_zone(#zone{until=Until}, Now) ->
|
|
||||||
ezic_date:compare_datetimes(Until, Now).
|
|
||||||
|
% @bug doesn't normalize times. Times are self-relative (to standard time), and Now may come from any timezone unless we're careful about that.
|
||||||
|
older_zone(#zone{until=Until}, UTCDatetime) ->
|
||||||
|
ezic_date:compare_datetimes(Until, UTCDatetime).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user