Dust settled on Argentina?

Paul Eggert eggert at CS.UCLA.EDU
Tue Jul 13 20:55:02 UTC 2004


"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:

> I haven't seen another patch. Should I go ahead and promulgate the
> 2004-06-15 patch, or should I wait?

Sorry, I dropped the ball on that one.  Here's a revised version:
it incorporates the discussion to earlier revisions of
all the patches I've posted.  I also updated the tz-link.htm web page
to reflect foreign web site changes.

* Patches relevant to current time stamps.

  - Major reorganizaton for Argentina.

    . New zones America/Argentina/Tucuman, America/Argentina/La_Rioja,
      America/Argentina/San_Juan, America/Argentina/ComodRivadavia,
      America/Argentina/Rio_Gallegos, America/Argentina/Ushuaia reflecting
      this year's changes.  (Thanks to Mariano Absatz and Gwillim Law for
      their dogged reporting of this info.)

    . America/Buenos_Aires renamed to America/Argentina/Buenos_Aires,
      and similarly for America/Cordoba, America/Jujuy,
      America/Catamarca, America/Mendoza.  Backward-compatible links
      retained so that old TZ settings still work.

  - Georgia switched to Moscow time on June 27.  (Thanks to Joseph S. Myers
    for this info.)

* Patch relevant to historical time stamps.

  - Where new zones were created, I recorded Shanks's opinions of what
    happened to Argentina in 1991.

* Patch due to ISO 3166 change.

  - The ISO added a country code AX for the Aaland Islands, so I added
    a new link Europe/Mariehamn for AX.

* Documentation patches.

  - Add URL for PyTZ.
  - Update URL for Tru64.
  - Remove dangling URL to <http://www.hilink.com.au/times/>.
  - Mention that Java has only a subset of the Olson database.
  - Move ICU localization reference to "Time Notation" section.
  - Update names for Astrodienst, worldtimezone.com, INMS.

* Some proposed source-code patches for 64-bit hosts.

  - Use 'long' when computing years, not 'int', to avoid spurious
    overflow on 64-bit hosts.

  - Use our (more generous) asctime implementation than relying on the
    system asctime, so that we can print years before 1900 and after
    9999 (even on HP-UX).

  - When localtime/gmtime fails, print the raw time_t value as an
    integer than the less-informative "NULL".

  - Don't attempt to print out all the 64-bit time_t space, as that will
    take nearly forever; instead, default to printing a few decades
    into the future, and don't bother to print times before about 1833.
    This suffices for all the current tz data and is good enough for
    POSIX tz values until the year 2106.  We can revisit this when we
    add true 64-bit support for tz data.

diff -pru tz-2004aa/asia tz/asia
--- tz-2004aa/asia	2004-05-27 09:00:30.000000000 -0700
+++ tz/asia	2004-07-13 13:16:45.000000000 -0700
@@ -335,6 +335,16 @@ Link	Asia/Nicosia	Europe/Nicosia
 # Instead of putting back clocks at the end of October, Georgia
 # will stay on daylight savings time this winter to save energy,
 # President Eduard Shevardnadze decreed Wednesday.
+#
+# From the BBC via Joseph S. Myers (2004-06-27):
+#  	
+# Georgia moved closer to Western Europe on Sunday...  The former Soviet
+# republic has changed its time zone back to that of Moscow.  As a result it
+# is now just four hours ahead of Greenwich Mean Time, rather than five hours
+# ahead.  The switch was decreed by the pro-Western president of Georgia,
+# Mikhail Saakashvili, who said the change was partly prompted by the process
+# of integration into Europe.
+
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Asia/Tbilisi	2:59:16 -	LMT	1880
 			2:59:16	-	TBMT	1924 May  2 # Tbilisi Mean Time
@@ -345,7 +355,8 @@ Zone	Asia/Tbilisi	2:59:16 -	LMT	1880
 			3:00 E-EurAsia	GE%sT	1994 Sep lastSun
 			4:00 E-EurAsia	GE%sT	1996 Oct lastSun
 			4:00	1:00	GEST	1997 Mar lastSun
-			4:00 E-EurAsia	GE%sT
+			4:00 E-EurAsia	GE%sT	2004 Jun 27
+			3:00 RussiaAsia	GE%sT
 
 # East Timor
 
diff -pru tz-2004aa/backward tz/backward
--- tz-2004aa/backward	2004-05-27 09:00:30.000000000 -0700
+++ tz/backward	2004-06-14 12:30:06.845824000 -0700
@@ -4,9 +4,14 @@
 # and their old names.  Many names changed in late 1993.
 
 Link	America/Adak		America/Atka
+Link	America/Argentina/Buenos_Aires	America/Buenos_Aires
+Link	America/Argentina/Catamarca	America/Catamarca
+Link	America/Argentina/Cordoba	America/Cordoba
 Link	America/Tijuana		America/Ensenada
 Link	America/Indianapolis	America/Fort_Wayne
+Link	America/Argentina/Jujuy	America/Jujuy
 Link	America/Indiana/Knox	America/Knox_IN
+Link	America/Argentina/Mendoza	America/Mendoza
 Link	America/Rio_Branco	America/Porto_Acre
 Link	America/Cordoba		America/Rosario
 Link	America/St_Thomas	America/Virgin
diff -pru tz-2004aa/europe tz/europe
--- tz-2004aa/europe	2004-05-27 09:00:30.000000000 -0700
+++ tz/europe	2004-06-12 18:37:32.547326000 -0700
@@ -921,6 +921,10 @@ Zone	Europe/Helsinki	1:39:52 -	LMT	1878 
 			2:00	Finland	EE%sT	1981 Mar 29 2:00
 			2:00	EU	EE%sT
 
+# Aaland Is
+Link	Europe/Helsinki	Europe/Mariehamn
+
+
 # France
 
 # From Ciro Discepolo (2000-12-20):
diff -pru tz-2004aa/iso3166.tab tz/iso3166.tab
--- tz-2004aa/iso3166.tab	2003-12-15 06:36:37.000000000 -0800
+++ tz/iso3166.tab	2004-06-13 23:50:45.657245000 -0700
@@ -2,11 +2,11 @@
 #
 # @(#)iso3166.tab	1.14
 #
-# From Paul Eggert <eggert at twinsun.com> (2003-02-04):
+# From Paul Eggert <eggert at twinsun.com> (2004-06-14):
 #
 # This file contains a table with the following columns:
 # 1.  ISO 3166-1 alpha-2 country code, current as of
-#     ISO 3166-1 Newsletter No. V-8 (2003-07-23).  See:
+#     ISO 3166-1 Newsletter No. V-10 (2004-04-26).  See:
 #     <a href="http://www.iso.org/iso/en/prods-services/iso3166ma/index.html">
 #     ISO 3166 Maintenance agency (ISO 3166/MA)
 #     </a>.
@@ -36,6 +36,7 @@ AS	Samoa (American)
 AT	Austria
 AU	Australia
 AW	Aruba
+AX	Aaland Islands
 AZ	Azerbaijan
 BA	Bosnia & Herzegovina
 BB	Barbados
diff -pru tz-2004aa/southamerica tz/southamerica
--- tz-2004aa/southamerica	2004-05-27 09:00:30.000000000 -0700
+++ tz/southamerica	2004-06-22 21:39:13.762385000 -0700
@@ -155,17 +155,47 @@ Rule	Arg	2000	only	-	Mar	Sun>=1	0:00	0	-
 # </a> says that standard time in Argentina from 1894-10-31
 # to 1920-05-01 was -4:16:48.25.  Go with this more-precise value
 # over Shanks.
+#
+# From Mariano Absatz (2004-06-05):
+# These media articles from a major newspaper mostly cover the current state:
+# http://www.lanacion.com.ar/04/05/27/de_604825.asp
+# http://www.lanacion.com.ar/04/05/28/de_605203.asp
+#
+# The following eight (8) provinces pulled clocks back to UTC-04:00 at
+# midnight Monday May 31st. (that is, the night between 05/31 and 06/01).
+# Apparently, all nine provinces would go back to UTC-03:00 at the same
+# time in October 17th.
+#
+# Catamarca, Chubut, La Rioja, San Juan, San Luis, Santa Cruz,
+# Tierra del Fuego, Tucuman.
+#
+# From Mariano Absatz (2004-06-14):
+# ... this weekend, the Province of Tucuman decided it'd go back to UTC-03:00
+# yesterday midnight (that is, at 24:00 Saturday 12th), since the people's
+# annoyance with the change is much higher than the power savings obtained....
+#
+# From Gwillim Law (2004-06-14):
+# http://www.lanacion.com.ar/04/06/10/de_609078.asp ...
+#     "The time change in Tierra del Fuego was a conflicted decision from
+#   the start.  The government had decreed that the measure would take
+#   effect on June 1, but a normative error forced the new time to begin
+#   three days earlier, from a Saturday to a Sunday....
+# Our understanding was that the change was originally scheduled to take place
+# on June 1 at 00:00 in Chubut, Santa Cruz, Tierra del Fuego (and some other
+# provinces).  Sunday was May 30, only two days earlier.  So the article
+# contains a contradiction.  I would give more credence to the Saturday/Sunday
+# date than the "three days earlier" phrase, and conclude that Tierra del
+# Fuego set its clocks back at 2004-05-30 00:00.
 
 # Unless otherwise specified, data are from Shanks through 1992, from
 # the IATA otherwise.  As noted below, Shanks says that
-# America/Cordoba split into 7 subregions during 1991/1992, but we
+# America/Cordoba split into 6 subregions during 1991/1992, but we
 # haven't verified this yet so for now we'll keep it a single region.
 #
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 #
-# Buenos Aires (BA), Capital Federal (CF), Santa Cruz (SC),
-# Tierra del Fuego, Antartida e Islas del Atlantico Sur (TF)
-Zone America/Buenos_Aires -3:53:48 -	LMT	1894 Oct 31
+# Buenos Aires (BA), Capital Federal (CF),
+Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31
 			-4:16:48 -	CMT	1920 May # Cordoba Mean Time
 			-4:00	-	ART	1930 Dec
 			-4:00	Arg	AR%sT	1969 Oct  5
@@ -174,24 +204,19 @@ Zone America/Buenos_Aires -3:53:48 -	LMT
 			-3:00	-	ART
 #
 # Santa Fe (SF), Entre Rios (ER), Corrientes (CN), Misiones (MN), Chaco (CC),
-# Formosa (FM), Salta (SA), Tucuman (TM), Santiago del Estero (SE),
-# Cordoba (CB), La Rioja (LR), San Juan (SJ), San Luis (SL), La Pampa (LP),
-# Neuquen (NQ), Rio Negro (RN), Chubut (CH)
+# Formosa (FM), Salta (SA), Santiago del Estero (SE), Cordoba (CB),
+# San Luis (SL), La Pampa (LP), Neuquen (NQ), Rio Negro (RN)
 #
 # Shanks also makes the following claims, which we haven't verified:
 # - Formosa switched to -3:00 on 1991-01-07.
-# - La Rioja and San Juan switched to -4:00 on 1991-03-01
-#   and then to -3:00 on 1991-05-07.
 # - Misiones switched to -3:00 on 1990-12-29.
 # - Chaco switched to -3:00 on 1991-01-04.
 # - San Luis switched to -4:00 on 1990-03-14, then to -3:00 on 1990-10-15,
 #   then to -4:00 on 1991-03-01, then to -3:00 on 1991-06-01.
 # - Santiago del Estero switched to -4:00 on 1991-04-01,
 #   then to -3:00 on 1991-04-26.
-# If we need to add Zones for these areas, we may need to have a subdirectory
-# for Argentina, as e.g. "America/San_Luis" is too ambiguious.
 #
-Zone America/Cordoba	-4:16:48 -	LMT	1894 Oct 31
+Zone America/Argentina/Cordoba -4:16:48 - LMT	1894 Oct 31
 			-4:16:48 -	CMT	1920 May
 			-4:00	-	ART	1930 Dec
 			-4:00	Arg	AR%sT	1969 Oct  5
@@ -201,8 +226,47 @@ Zone America/Cordoba	-4:16:48 -	LMT	1894
 			-4:00	Arg	AR%sT	2000 Mar  3
 			-3:00	-	ART
 #
+# Tucuman (TM)
+Zone America/Argentina/Tucuman -4:20:52 - LMT	1894 Oct 31
+			-4:16:48 -	CMT	1920 May
+			-4:00	-	ART	1930 Dec
+			-4:00	Arg	AR%sT	1969 Oct  5
+			-3:00	Arg	AR%sT	1991 Mar  3
+			-4:00	-	WART	1991 Oct 20
+			-3:00	Arg	AR%sT	1999 Oct  3
+			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 Jun  1
+			-4:00	-	WART	2004 Jun 13
+			-3:00	-	ART
+#
+# La Rioja (LR)
+Zone America/Argentina/La_Rioja -4:27:24 - LMT	1894 Oct 31
+			-4:16:48 -	CMT	1920 May
+			-4:00	-	ART	1930 Dec
+			-4:00	Arg	AR%sT	1969 Oct  5
+			-3:00	Arg	AR%sT	1991 Mar  1
+			-4:00	-	WART	1991 May  7
+			-3:00	Arg	AR%sT	1999 Oct  3
+			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 Jun  1
+			-4:00	-	WART	2004 Jun 20
+			-3:00	-	ART
+#
+# San Juan (SJ)
+Zone America/Argentina/San_Juan -4:34:04 - LMT	1894 Oct 31
+			-4:16:48 -	CMT	1920 May
+			-4:00	-	ART	1930 Dec
+			-4:00	Arg	AR%sT	1969 Oct  5
+			-3:00	Arg	AR%sT	1991 Mar  1
+			-4:00	-	WART	1991 May  7
+			-3:00	Arg	AR%sT	1999 Oct  3
+			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 Jun  1
+			-4:00	-	WART	2004 Oct 17
+			-3:00	-	ART
+#
 # Jujuy (JY)
-Zone America/Jujuy	-4:21:12 -	LMT	1894 Oct 31
+Zone America/Argentina/Jujuy -4:21:12 -	LMT	1894 Oct 31
 			-4:16:48 -	CMT	1920 May
 			-4:00	-	ART	1930 Dec
 			-4:00	Arg	AR%sT	1969 Oct  5
@@ -216,7 +280,7 @@ Zone America/Jujuy	-4:21:12 -	LMT	1894 O
 			-3:00	-	ART
 #
 # Catamarca (CT)
-Zone America/Catamarca	-4:23:08 -	LMT	1894 Oct 31
+Zone America/Argentina/Catamarca -4:23:08 - LMT	1894 Oct 31
 			-4:16:48 -	CMT	1920 May
 			-4:00	-	ART	1930 Dec
 			-4:00	Arg	AR%sT	1969 Oct  5
@@ -224,10 +288,12 @@ Zone America/Catamarca	-4:23:08 -	LMT	18
 			-4:00	-	WART	1991 Oct 20
 			-3:00	Arg	AR%sT	1999 Oct  3
 			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 Jun  1
+			-4:00	-	WART	2004 Jun 20
 			-3:00	-	ART
 #
 # Mendoza (MZ)
-Zone America/Mendoza	-4:35:16 -	LMT	1894 Oct 31
+Zone America/Argentina/Mendoza -4:35:16 - LMT	1894 Oct 31
 			-4:16:48 -	CMT	1920 May
 			-4:00	-	ART	1930 Dec
 			-4:00	Arg	AR%sT	1969 Oct  5
@@ -242,6 +308,42 @@ Zone America/Mendoza	-4:35:16 -	LMT	1894
 			-3:00	-	ART	2004 May 23
 			-4:00	-	WART	2004 Oct 17
 			-3:00	-	ART
+#
+# Chubut (CH)
+# The name "Comodoro Rivadavia" exceeds the 14-byte POSIX limit.
+Zone America/Argentina/ComodRivadavia -4:30:00 - LMT	1894 Oct 31
+			-4:16:48 -	CMT	1920 May
+			-4:00	-	ART	1930 Dec
+			-4:00	Arg	AR%sT	1969 Oct  5
+			-3:00	Arg	AR%sT	1991 Mar  3
+			-4:00	-	WART	1991 Oct 20
+			-3:00	Arg	AR%sT	1999 Oct  3
+			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 Jun  1
+			-4:00	-	WART	2004 Jun 20
+			-3:00	-	ART
+#
+# Santa Cruz (SC)
+Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31
+			-4:16:48 -	CMT	1920 May # Cordoba Mean Time
+			-4:00	-	ART	1930 Dec
+			-4:00	Arg	AR%sT	1969 Oct  5
+			-3:00	Arg	AR%sT	1999 Oct  3
+			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 Jun  1
+			-4:00	-	WART	2004 Jun 20
+			-3:00	-	ART
+#
+# Tierra del Fuego, Antartida e Islas del Atlantico Sur (TF)
+Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31
+			-4:16:48 -	CMT	1920 May # Cordoba Mean Time
+			-4:00	-	ART	1930 Dec
+			-4:00	Arg	AR%sT	1969 Oct  5
+			-3:00	Arg	AR%sT	1999 Oct  3
+			-4:00	Arg	AR%sT	2000 Mar  3
+			-3:00	-	ART	2004 May 30
+			-4:00	-	WART	2004 Jun 20
+			-3:00	-	ART
 
 # Aruba
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
diff -pru tz-2004aa/tz-link.htm tz/tz-link.htm
--- tz-2004aa/tz-link.htm	2004-05-27 09:00:30.000000000 -0700
+++ tz/tz-link.htm	2004-07-13 13:21:17.651768000 -0700
@@ -9,7 +9,7 @@
 <meta http-equiv="Content-type" content='text/html; charset="US-ASCII"' />
 <meta name="DC.Creator" content="Eggert, Paul" />
 <meta name="DC.Contributor" content="Olson, Arthur David" />
-<meta name="DC.Date" content="2004-05-24" />
+<meta name="DC.Date" content="2004-07-13" />
 <meta name="DC.Description"
  content="Sources of information about time zones and daylight saving time" />
 <meta name="DC.Identifier" content="http://www.twinsun.com/tz/tz-link.htm" />
@@ -47,7 +47,7 @@ including
 <a href="http://www.apple.com/macosx/">Mac OS X</a>,
 <a href="http://h71000.www7.hp.com/">OpenVMS</a>,
 <a href="http://wwws.sun.com/software/solaris/">Solaris</a>,
-<a href="http://www.tru64unix.compaq.com/">Tru64</a>, and
+<a href="http://h30097.www3.hp.com/">Tru64</a>, and
 <a href="http://www.sco.com/products/unixware/">UnixWare</a>.</p>
 <p>
 Each location in the database represents a national region where all
@@ -104,14 +104,9 @@ is a text-based point-and-click interfac
 throughout the world.</li>
 <li>Fancier web interfaces, roughly in ascending order of complexity, include:
 <ul>
-<li><a href="http://www.hilink.com.au/times/">Local Times Around the
-World</a></li>
 <li><a href="http://www.convertit.com/Go/ConvertIt/World_Time/Current_Time.ASP">Current Time in 1000 Places</a></li>
 <li><a href="http://timezoneconverter.com/">Time Zone Converter</a></li>
 </ul></li>
-<li><a href="http://www.holidayfestival.com/">The Worldwide Holiday
-&amp; Festival Site</a> lists DST-related clock changes along with
-holidays.</li>
 <li><a href="http://www.timeanddate.com/worldclock/">The World Clock -
 Time Zones</a>
 is a web interface to a time zone database derived from
@@ -152,9 +147,8 @@ contains a script <code>parse_olson</cod
 <code>tz</code> source into <a href="http://www.perl.org/">Perl</a>
 modules. It is part of the Perl <a
 href="http://datetime.perl.org/">DateTime Project</a>, which is freely
-available under both the GPL and the Perl <a
-href="http://www.perl.com/language/misc/Artistic.html">Artistic
-License</a>. DateTime::TimeZone also contains a script
+available under both the GPL and the Perl Artistic
+License. DateTime::TimeZone also contains a script
 <code>tests_from_zdump</code> that generates test cases for each clock
 transition in the <code>tz</code> database.</li>
 <li><a href="http://oss.software.ibm.com/icu/">International Components for
@@ -166,6 +160,10 @@ and time API</a> contains a class
 <code>org.joda.time.tz.ZoneInfoCompiler</code> that compiles
 <code>tz</code> source into a Joda-specific binary format. Joda Time
 is freely available under a BSD-style license.</li>
+<li><a href="http://sourceforge.net/projects/pytz/">PyTZ - Python Time
+Zone Library</a> compiles <code>tz</code> source into
+<a href="http://www.python.org/">Python</a>.
+It is freely available under a BSD-style license.</li>
 </ul>
 <h2>Other <code>tz</code> binary file readers</h2>
 <ul>
@@ -180,15 +178,14 @@ and is widely used in GNU/Linux systems.
 is a <code>tz</code> binary file reader written in Java.
 It is freely available under the GNU LGPL.</li>
 <li><a href="http://s.keim.free.fr/tz/doc.html">Python time zones</a>
-is a <code>tz</code> binary file reader written in <a
-href="http://www.python.org/">Python</a>. It is freely available
-under a BSD-style license.</li>
+is a <code>tz</code> binary file reader written in Python.
+It is freely available under a BSD-style license.</li>
 </ul>
 <h2>Other <code>tz</code>-based time zone conversion software</h2>
 <ul>
 <li><a href="http://java.sun.com/">Sun Java</a> releases since 1.4
-contain a copy of a recent <samp>tz</samp> database in a Java-specific
-format.</li>
+contain a copy of a subset of a recent <samp>tz</samp> database in a
+Java-specific format.</li>
 <li><a
 href="http://www1.tip.nl/~t876506/AboutTimeZonesHC.html">HyperCard
 time zones calculator</a> is a HyperCard stack.</li>
@@ -198,8 +195,8 @@ Microsoft Windows program.</li>
 </ul>
 <h2>Other time zone databases</h2>
 <ul>
-<li><a href="http://www.astro.com/cgi-bin/atlw3/aq.cgi?lang=e">Atlas Query
-- Astrodienst</a> is Astrodienst's Web version of Shanks's
+<li><a href="http://www.astro.com/cgi/aq.cgi?lang=e">Atlas Query</a>
+is Astrodienst's Web version of Shanks's
 excellent time zone history atlases published in both <a
 href="http://astrocom.com/software/pcatlas.php">computer</a> and <a
 href="http://astrocom.com/books/xrefa.php#SHANKS">book</a> form by <a
@@ -217,7 +214,7 @@ for the <code>usno*</code> files in the 
 <li><a href="http://www.airportcitycodes.com/aaa/">Airlines, Airplanes
 and Airports</a> lists current standard times for thousands of
 airports around the world. This seems to be derived from
-the <a href="http://www.iata.org/sked/publications/">Standard
+the <a href="http://www.iata.org/ps/publications/9179.htm">Standard
 Schedules Information Manual (SSIM)</a> of the
 the <a href="http://www.iata.org/">International Air Transport
 Association</a>,
@@ -238,8 +235,8 @@ recent editions.
 The pictorial quality is good,
 but the maps do not indicate summer time,
 and parts of the data are a few years out of date.</li>
-<li><a href="http://worldtimezone.com/">World timezones map with
-current time</a>
+<li><a href="http://worldtimezone.com/">Current time around the world
+and standard time zones map of the world</a>
 has several fancy time zone maps; it covers Russia particularly well.
 The maps' pictorial quality is not quite as good as the CIA's
 but the maps are more up to date.</li>
@@ -252,9 +249,7 @@ boundaries between time zones within cou
 <li>Manifold.net's <a
 href="http://www.manifold.net/download/freemaps.html">Free Maps and
 GIS Data</a> includes a Manifold-format map of world time zone
-boundaries distributed under the GPL. The GeoCommunity's <a
-href="http://software.geocomm.com/data/intl_timezones.html">International
-Time Zones</a> publishes the same data in other formats.</li>
+boundaries distributed under the GPL.</li>
 <li>The US Geological Survey's National Atlas of the United States
 publishes the <a href="http://www.nationalatlas.gov/timeznm.html">Time
 Zones of the United States</a> in the public domain.</li>
@@ -304,7 +299,7 @@ Portuguese)</a>.</dd>
 <dd>The Institute for National Measurement Standards publishes current
 and some older information about <a
 href="http://inms-ienm.nrc-cnrc.gc.ca/time_services/daylight_savings_e.html">Time
-Zones and Daylight Saving Time</a>.</dd>
+Zones &amp; Daylight Saving Time</a>.</dd>
 <dt>Chile</dt>
 <dd>WebExhibits publishes a <a
 href="http://webexhibits.org/daylightsaving/chile.html"
@@ -420,6 +415,12 @@ protocols.</li>
 <a href="http://www.exit109.com/~ghealton/y2k/yrexamples.html">The
 Best of Dates, the Worst of Dates</a> covers many problems encountered
 by software developers when handling dates and time stamps.</li>
+<li><a
+href="http://oss.software.ibm.com/cvs/icu/~checkout~/icuhtml/design/formatting/time_zone_localization.html">Time
+Zone Localization</a> is a proposed mechanism for localizing time zone
+labels and abbreviations; for example, one might use it to specify
+Russian translations for "Eastern European Summer Time", "EEST", and
+<code>Europe/Bucharest</code>.</li>
 <li>
 Alphabetic time zone abbreviations should not be used as unique
 identifiers for UTC offsets as they are ambiguous in practice. For
diff -pru tz-2004aa/zone.tab tz/zone.tab
--- tz-2004aa/zone.tab	2003-10-06 06:32:21.000000000 -0700
+++ tz/zone.tab	2004-06-22 21:35:35.301478000 -0700
@@ -41,11 +41,17 @@ AQ	-6617+11031	Antarctica/Casey	Casey St
 AQ	-7824+10654	Antarctica/Vostok	Vostok Station, S Magnetic Pole
 AQ	-6640+14001	Antarctica/DumontDUrville	Dumont-d'Urville Base, Terre Adelie
 AQ	-690022+0393524	Antarctica/Syowa	Syowa Station, E Ongul I
-AR	-3436-05827	America/Buenos_Aires	E Argentina (BA, DF, SC, TF)
-AR	-3124-06411	America/Cordoba	most locations (CB,CC,CH,CN,ER,FM,LP,LR,MN,NQ,RN,SA,SE,SF,SJ,SL,TM)
-AR	-2411-06518	America/Jujuy	Jujuy (JY)
-AR	-2828-06547	America/Catamarca	Catamarca (CT)
-AR	-3253-06849	America/Mendoza	Mendoza (MZ)
+AR	-3436-05827	America/Argentina/Buenos_Aires	Buenos Aires (BA, CF)
+AR	-3124-06411	America/Argentina/Cordoba	most locations (CB, CC, CN, ER, FM, LP, MN, NQ, RN, SA, SE, SF, SL)
+AR	-2411-06518	America/Argentina/Jujuy	Jujuy (JY)
+AR	-3124-06411	America/Argentina/Tucuman	Tucuman (TM)
+AR	-2828-06547	America/Argentina/Catamarca	Catamarca (CT)
+AR	-2926-06651	America/Argentina/La_Rioja	La Rioja (LR)
+AR	-3132-06831	America/Argentina/San_Juan	San Juan (SJ)
+AR	-3253-06849	America/Argentina/Mendoza	Mendoza (MZ)
+AR	-4552-06730	America/Argentina/ComodRivadavia	Chubut (CH)
+AR	-5138-06913	America/Argentina/Rio_Gallegos	Santa Cruz (SC)
+AR	-5448-06818	America/Argentina/Ushuaia	Tierra del Fuego (TF)
 AS	-1416-17042	Pacific/Pago_Pago
 AT	+4813+01620	Europe/Vienna
 AU	-3133+15905	Australia/Lord_Howe	Lord Howe Island
@@ -59,6 +65,7 @@ AU	-3455+13835	Australia/Adelaide	South 
 AU	-1228+13050	Australia/Darwin	Northern Territory
 AU	-3157+11551	Australia/Perth	Western Australia
 AW	+1230-06858	America/Aruba
+AX	+6006+01957	Europe/Mariehamn
 AZ	+4023+04951	Asia/Baku
 BA	+4352+01825	Europe/Sarajevo
 BB	+1306-05937	America/Barbados
diff -pru tz-2004aa/asctime.c tz/asctime.c
--- tz-2004aa/asctime.c	1998-05-28 06:56:06.000000000 -0700
+++ tz/asctime.c	2004-06-14 21:23:43.000000000 -0700
@@ -41,14 +41,14 @@ char *				buf;
 	else	mn = mon_name[timeptr->tm_mon];
 	/*
 	** The X3J11-suggested format is
-	**	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
-	** Since the .2 in 02.2d is ignored, we drop it.
+	**	"%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
+	** Use "%02d", as it is a bit more portable than "%.2d".
 	*/
-	(void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+	(void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %ld\n",
 		wn, mn,
 		timeptr->tm_mday, timeptr->tm_hour,
 		timeptr->tm_min, timeptr->tm_sec,
-		TM_YEAR_BASE + timeptr->tm_year);
+		timeptr->tm_year + (long) TM_YEAR_BASE);
 	return buf;
 }
 
diff -pru tz-2004aa/date.c tz/date.c
--- tz-2004aa/date.c	2001-10-09 10:31:31.000000000 -0700
+++ tz/date.c	2004-06-14 22:44:08.703969000 -0700
@@ -630,8 +630,12 @@ const time_t			t;
 	time_t		outt;
 
 	tm = *localtime(&t);
-	cent = (tm.tm_year + TM_YEAR_BASE) / 100;
-	year_in_cent = (tm.tm_year + TM_YEAR_BASE) - cent * 100;
+	cent = tm.tm_year / 100 + TM_YEAR_BASE / 100;
+	year_in_cent = tm.tm_year % 100;
+	if (year_int_cent < 0) {
+		cent--;
+		year_in_cent += 100;
+	}
 	month = tm.tm_mon + 1;
 	day = tm.tm_mday;
 	hour = tm.tm_hour;
diff -pru tz-2004aa/localtime.c tz/localtime.c
--- tz-2004aa/localtime.c	2003-12-15 06:36:35.000000000 -0800
+++ tz/localtime.c	2004-06-14 23:11:00.519250000 -0700
@@ -134,8 +134,11 @@ static void		gmtsub P((const time_t * ti
 static void		localsub P((const time_t * timep, long offset,
 				struct tm * tmp));
 static int		increment_overflow P((int * number, int delta));
+static int		long_increment_overflow P((long * number, int delta));
 static int		normalize_overflow P((int * tensptr, int * unitsptr,
 				int base));
+static int		long_normalize_overflow P((long * tensptr,
+				int * unitsptr, int base));
 static void		settzname P((void));
 static time_t		time1 P((struct tm * tmp,
 				void(*funcp) P((const time_t *,
@@ -1149,7 +1152,7 @@ register struct tm * const		tmp;
 	register const struct lsinfo *	lp;
 	register long			days;
 	register long			rem;
-	register int			y;
+	register long			y;
 	register int			yleap;
 	register const int *		ip;
 	register long			corr;
@@ -1218,7 +1221,7 @@ register struct tm * const		tmp;
 	y = EPOCH_YEAR;
 #define LEAPS_THRU_END_OF(y)	((y) / 4 - (y) / 100 + (y) / 400)
 	while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
-		register int	newy;
+		register long	newy;
 
 		newy = y + days / DAYSPERNYEAR;
 		if (days < 0)
@@ -1294,6 +1297,18 @@ int	delta;
 }
 
 static int
+long_increment_overflow(number, delta)
+long *	number;
+int	delta;
+{
+	long	number0;
+
+	number0 = *number;
+	*number += delta;
+	return (*number < number0) != (delta < 0);
+}
+
+static int
 normalize_overflow(tensptr, unitsptr, base)
 int * const	tensptr;
 int * const	unitsptr;
@@ -1309,6 +1324,21 @@ const int	base;
 }
 
 static int
+long_normalize_overflow(tensptr, unitsptr, base)
+long * const	tensptr;
+int * const	unitsptr;
+const int	base;
+{
+	register int	tensdelta;
+
+	tensdelta = (*unitsptr >= 0) ?
+		(*unitsptr / base) :
+		(-1 - (-1 - *unitsptr) / base);
+	*unitsptr -= tensdelta * base;
+	return long_increment_overflow(tensptr, tensdelta);
+}
+
+static int
 tmcomp(atmp, btmp)
 register const struct tm * const atmp;
 register const struct tm * const btmp;
@@ -1336,6 +1366,8 @@ const int		do_norm_secs;
 	register int			dir;
 	register int			bits;
 	register int			i, j ;
+	register long			li;
+	long				y;
 	register int			saved_seconds;
 	time_t				newt;
 	time_t				t;
@@ -1352,42 +1384,46 @@ const int		do_norm_secs;
 		return WRONG;
 	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
 		return WRONG;
-	if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
+	y = yourtm.tm_year;
+	if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
 		return WRONG;
 	/*
-	** Turn yourtm.tm_year into an actual year number for now.
+	** Turn y into an actual year number for now.
 	** It is converted back to an offset from TM_YEAR_BASE later.
 	*/
-	if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
+	if (long_increment_overflow(&y, TM_YEAR_BASE))
 		return WRONG;
 	while (yourtm.tm_mday <= 0) {
-		if (increment_overflow(&yourtm.tm_year, -1))
+		if (long_increment_overflow(&y, -1))
 			return WRONG;
-		i = yourtm.tm_year + (1 < yourtm.tm_mon);
-		yourtm.tm_mday += year_lengths[isleap(i)];
+		li = y + (1 < yourtm.tm_mon);
+		yourtm.tm_mday += year_lengths[isleap(li)];
 	}
 	while (yourtm.tm_mday > DAYSPERLYEAR) {
-		i = yourtm.tm_year + (1 < yourtm.tm_mon);
-		yourtm.tm_mday -= year_lengths[isleap(i)];
-		if (increment_overflow(&yourtm.tm_year, 1))
+		li = y + (1 < yourtm.tm_mon);
+		yourtm.tm_mday -= year_lengths[isleap(li)];
+		if (long_increment_overflow(&y, 1))
 			return WRONG;
 	}
 	for ( ; ; ) {
-		i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
+		i = mon_lengths[isleap(y)][yourtm.tm_mon];
 		if (yourtm.tm_mday <= i)
 			break;
 		yourtm.tm_mday -= i;
 		if (++yourtm.tm_mon >= MONSPERYEAR) {
 			yourtm.tm_mon = 0;
-			if (increment_overflow(&yourtm.tm_year, 1))
+			if (long_increment_overflow(&y, 1))
 				return WRONG;
 		}
 	}
-	if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
+	if (long_increment_overflow(&y, -TM_YEAR_BASE))
+		return WRONG;
+	yourtm.tm_year = y;
+	if (yourtm.tm_year != y)
 		return WRONG;
 	if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
 		saved_seconds = 0;
-	else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+	else if (y < EPOCH_YEAR - TM_YEAR_BASE) {
 		/*
 		** We can't set tm_sec to 0, because that might push the
 		** time below the minimum representable time.
diff -pru tz-2004aa/strftime.c tz/strftime.c
--- tz-2004aa/strftime.c	2001-10-04 14:01:17.000000000 -0700
+++ tz/strftime.c	2004-06-14 22:37:47.213984000 -0700
@@ -108,6 +108,7 @@ static const struct lc_time_T	C_time_loc
 
 static char *	_add P((const char *, char *, const char *));
 static char *	_conv P((int, const char *, char *, const char *));
+static char *	_lconv P((long, const char *, char *, const char *));
 static char *	_fmt P((const char *, const struct tm *, char *, const char *, int *));
 
 size_t strftime P((char *, size_t, const char *, const struct tm *));
@@ -210,8 +211,12 @@ label:
 				** something completely different.
 				** (ado, 1993-05-24)
 				*/
-				pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
-					"%02d", pt, ptlim);
+				{
+				int c = (TM_YEAR_BASE / 100
+					 + t->tm_year / 100
+					 - (t->tm_year % 100 < 0));
+				pt = _conv(c, "%02d", pt, ptlim);
+				}
 				continue;
 			case 'c':
 				{
@@ -379,12 +384,13 @@ label:
 ** (ado, 1996-01-02)
 */
 				{
-					int	year;
+					long	year;
 					int	yday;
 					int	wday;
 					int	w;
 
-					year = t->tm_year + TM_YEAR_BASE;
+					year = t->tm_year;
+					year += TM_YEAR_BASE;
 					yday = t->tm_yday;
 					wday = t->tm_wday;
 					for ( ; ; ) {
@@ -436,10 +442,13 @@ label:
 						pt = _conv(w, "%02d",
 							pt, ptlim);
 					else if (*format == 'g') {
+						int g = year % 100;
+						if (g < 0)
+							g += 100;
 						*warnp = IN_ALL;
-						pt = _conv(year % 100, "%02d",
+						pt = _conv(g, "%02d",
 							pt, ptlim);
-					} else	pt = _conv(year, "%04d",
+					} else	pt = _lconv(year, "%04ld",
 							pt, ptlim);
 				}
 				continue;
@@ -476,13 +485,17 @@ label:
 				}
 				continue;
 			case 'y':
+				{
+				int y = t->tm_year % 100;
+				if (y < 0)
+					  y += 100;
 				*warnp = IN_ALL;
-				pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
-					"%02d", pt, ptlim);
+				pt = _conv(y, "%02d", pt, ptlim);
+				}
 				continue;
 			case 'Y':
-				pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
-					pt, ptlim);
+				pt = _lconv(t->tm_year + (long) TM_YEAR_BASE,
+					"%04ld", pt, ptlim);
 				continue;
 			case 'Z':
 #ifdef TM_ZONE
@@ -586,6 +599,19 @@ const char * const	ptlim;
 }
 
 static char *
+_lconv(n, format, pt, ptlim)
+const long		n;
+const char * const	format;
+char * const		pt;
+const char * const	ptlim;
+{
+	char	buf[INT_STRLEN_MAXIMUM(long) + 1];
+
+	(void) sprintf(buf, format, n);
+	return _add(buf, pt, ptlim);
+}
+
+static char *
 _add(str, pt, ptlim)
 const char *		str;
 char *			pt;
diff -pru tz-2004aa/zdump.8 tz/zdump.8
--- tz-2004aa/zdump.8	2003-09-16 04:12:41.000000000 -0700
+++ tz/zdump.8	2004-06-17 13:33:33.238073000 -0700
@@ -40,6 +40,12 @@ otherwise.
 .TP
 .BI "\-c " cutoffyear
 Cut off the verbose output near the start of the given year.
+By default, verbose output is cut off a few decades after the
+current date and time.
 .SH "SEE ALSO"
 newctime(3), tzfile(5), zic(8)
+.SH "BUGS"
+Time discontinuities are detected via a heuristic that suffices for
+all historically valid discontinuities, but one can construct
+artificial time zones for which the heuristic fails.
 .\" @(#)zdump.8	7.4
diff -pru tz-2004aa/zdump.c tz/zdump.c
--- tz-2004aa/zdump.c	2003-09-16 04:12:41.000000000 -0700
+++ tz/zdump.c	2004-06-17 14:12:10.425374000 -0700
@@ -6,6 +6,7 @@ static char	elsieid[] = "@(#)zdump.c	7.3
 ** You can use this code to help in verifying other implementations.
 */
 
+#include "limits.h"	/* for CHAR_BIT */
 #include "stdio.h"	/* for stdout, stderr, perror */
 #include "string.h"	/* for strcpy */
 #include "sys/types.h"	/* for time_t */
@@ -24,6 +25,25 @@ static char	elsieid[] = "@(#)zdump.c	7.3
 #define FALSE		0
 #endif /* !defined FALSE */
 
+#ifndef TYPE_BIT
+#define TYPE_BIT(type)	(sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+    ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
 #ifndef EXIT_SUCCESS
 #define EXIT_SUCCESS	0
 #endif /* !defined EXIT_SUCCESS */
@@ -56,10 +76,18 @@ static char	elsieid[] = "@(#)zdump.c	7.3
 #define TM_YEAR_BASE	1900
 #endif /* !defined TM_YEAR_BASE */
 
+#ifndef DAYSPERWEEK
+#define DAYSPERWEEK	7
+#endif /* !defined DAYSPERWEEK */
+
 #ifndef DAYSPERNYEAR
 #define DAYSPERNYEAR	365
 #endif /* !defined DAYSPERNYEAR */
 
+#ifndef MONSPERYEAR
+#define MONSPERYEAR	12
+#endif /* !defined MONSPERYEAR */
+
 #ifndef isleap
 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
 #endif /* !defined isleap */
@@ -128,6 +156,9 @@ static long	delta P((struct tm * newp, s
 static time_t	hunt P((char * name, time_t lot, time_t	hit));
 static size_t	longest;
 static char *	progname;
+static char *	my_asctime_r P((const struct tm *, char *));
+static char *	my_asctime P((const struct tm *));
+static void	showtime P((time_t, const struct tm *));
 static void	show P((char * zone, time_t t, int v));
 
 int
@@ -139,17 +170,19 @@ char *	argv[];
 	register int		c;
 	register int		vflag;
 	register char *		cutoff;
-	register int		cutyear;
-	register long		cuttime;
+	register long		cutyear;
+	long			y;
+	register time_t		lasttime;
 	char **			fakeenv;
 	time_t			now;
 	time_t			t;
 	time_t			newt;
 	time_t			hibit;
+	time_t			time_t_min;
+	time_t			time_t_max;
 	struct tm		tm;
-	struct tm		newtm;
+	struct tm *		newtm;
 
-	INITIALIZE(cuttime);
 #if HAVE_GETTEXT - 0
 	(void) setlocale(LC_MESSAGES, "");
 #ifdef TZ_DOMAINDIR
@@ -176,22 +209,44 @@ _("%s: usage is %s [ --version ] [ -v ] 
 				argv[0], argv[0]);
 			(void) exit(EXIT_FAILURE);
 	}
+	for (hibit = 1; (hibit << 1) != 0; hibit <<= 1)
+		continue;
+	time_t_max = ~((time_t) 0);
+	if (TYPE_SIGNED(time_t))
+		time_t_max &= ~hibit;
+	time_t_min = TYPE_SIGNED(time_t) ? hibit : 0;
+	(void) time(&now);
 	if (cutoff != NULL) {
-		int	y;
-
-		cutyear = atoi(cutoff);
-		cuttime = 0;
-		for (y = EPOCH_YEAR; y < cutyear; ++y)
-			cuttime += DAYSPERNYEAR + isleap(y);
-		cuttime *= SECSPERHOUR * HOURSPERDAY;
+		cutyear = atol(cutoff);
+	} else {
+		/*
+		** By default, the cutoff is the maximum time value,
+		** or a few decades from now, whichever comes first.
+		** This prevents us from generating too much output if
+		** time_t is wider than 32 bits.
+		*/
+		newtm = gmtime (&now);
+		if (newtm == NULL)
+			cutyear = now < 0 ? LONG_MIN : LONG_MAX;
+		else {
+			cutyear = newtm->tm_year + (long) TM_YEAR_BASE;
+			cutyear = 2050 + 100 * ((cutyear - 1930) / 100);
+		}
+	}
+	lasttime = 0;
+	for (y = EPOCH_YEAR; y < cutyear; ++y) {
+		newt = lasttime - !lasttime;
+		newt += (DAYSPERNYEAR + isleap(y)) * SECSPERHOUR * HOURSPERDAY;
+		if (newt < lasttime) {
+			lasttime = time_t_max;
+			break;
+		}
+		lasttime = newt;
 	}
-	(void) time(&now);
 	longest = 0;
 	for (i = optind; i < argc; ++i)
 		if (strlen(argv[i]) > longest)
 			longest = strlen(argv[i]);
-	for (hibit = 1; (hibit << 1) != 0; hibit <<= 1)
-		continue;
 	{
 		register int	from;
 		register int	to;
@@ -224,40 +279,47 @@ _("%s: usage is %s [ --version ] [ -v ] 
 		/*
 		** Get lowest value of t.
 		*/
-		t = hibit;
-		if (t > 0)		/* time_t is unsigned */
-			t = 0;
+		t = time_t_min;
 		show(argv[i], t, TRUE);
 		t += SECSPERHOUR * HOURSPERDAY;
 		show(argv[i], t, TRUE);
+		/*
+		** But don't go below - 2**32 in the following loop,
+		** as that predates standard time, would cause us to
+		** generate too much output, and could dump core if
+		** localtime returns NULL.  The earliest known use of
+		** standard time is 1837 (Iceland) and - 2**32 is a
+		** bit before that.
+		*/
+		if ((t + 1) / 2 < -2147483647) {
+			t = -1 - 2147483647;
+			t *= 2;
+		}
 		tm = *localtime(&t);
 		(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
 		for ( ; ; ) {
-			if (cutoff != NULL && t >= cuttime)
+			if (lasttime < t)
 				break;
 			newt = t + SECSPERHOUR * 12;
-			if (cutoff != NULL && newt >= cuttime)
+			if (lasttime < newt)
 				break;
 			if (newt <= t)
 				break;
-			newtm = *localtime(&newt);
-			if (delta(&newtm, &tm) != (newt - t) ||
-				newtm.tm_isdst != tm.tm_isdst ||
-				strcmp(abbr(&newtm), buf) != 0) {
+			newtm = localtime(&newt);
+			if (newtm == NULL)
+				break;
+			if (delta(newtm, &tm) != (newt - t) ||
+				newtm->tm_isdst != tm.tm_isdst ||
+				strcmp(abbr(newtm), buf) != 0) {
 					newt = hunt(argv[i], t, newt);
-					newtm = *localtime(&newt);
-					(void) strncpy(buf, abbr(&newtm),
+					newtm = localtime(&newt);
+					(void) strncpy(buf, abbr(newtm),
 						(sizeof buf) - 1);
 			}
 			t = newt;
-			tm = newtm;
+			tm = *newtm;
 		}
-		/*
-		** Get highest value of t.
-		*/
-		t = ~((time_t) 0);
-		if (t < 0)		/* time_t is signed */
-			t &= ~hibit;
+		t = time_t_max;
 		t -= SECSPERHOUR * HOURSPERDAY;
 		show(argv[i], t, TRUE);
 		t += SECSPERHOUR * HOURSPERDAY;
@@ -323,7 +385,7 @@ struct tm *	oldp;
 		return -delta(oldp, newp);
 	result = 0;
 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
-		result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE);
+		result += DAYSPERNYEAR + isleap(tmy + (long) TM_YEAR_BASE);
 	result += newp->tm_yday - oldp->tm_yday;
 	result *= HOURSPERDAY;
 	result += newp->tm_hour - oldp->tm_hour;
@@ -334,6 +396,80 @@ struct tm *	oldp;
 	return result;
 }
 
+/*
+** my_asctime_r and my_asctime are stolen from asctime.c.  Unlike
+** Standard C asctime, my_asctime has well-defined behavior even if
+** the year is less than -999 or greater than 9999.
+*/
+
+static char *
+my_asctime_r(timeptr, buf)
+register const struct tm *	timeptr;
+char *				buf;
+{
+	static const char	wday_name[][3] = {
+		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+	};
+	static const char	mon_name[][3] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+	register const char *	wn;
+	register const char *	mn;
+
+	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
+		wn = "???";
+	else	wn = wday_name[timeptr->tm_wday];
+	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
+		mn = "???";
+	else	mn = mon_name[timeptr->tm_mon];
+	/*
+	** The X3J11-suggested format is
+	**	"%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
+	** Use "%02d", as it is a bit more portable than "%.2d".
+	*/
+	(void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %ld\n",
+		wn, mn,
+		timeptr->tm_mday, timeptr->tm_hour,
+		timeptr->tm_min, timeptr->tm_sec,
+		timeptr->tm_year + (long) TM_YEAR_BASE);
+	return buf;
+}
+
+static char *
+my_asctime(timeptr)
+register const struct tm *	timeptr;
+{
+	/*
+	** Big enough for something such as
+	** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
+	** (two three-character abbreviations, five strings denoting integers,
+	** three explicit spaces, two explicit colons, a newline,
+	** and a trailing ASCII nul).
+	*/
+	static char		result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
+					3 + 2 + 1 + 1];
+
+	return my_asctime_r(timeptr, result);
+}
+
+static void
+showtime(t, timeptr)
+time_t			t;
+const struct tm *	timeptr;
+{
+	if (timeptr != NULL) {
+		const char *p;
+		for (p = my_asctime (timeptr); *p != '\n'; p++)
+			putchar (*p);
+	} else {
+		if (TYPE_SIGNED(time_t))
+			printf ("%ld\n", (long) t);
+		else
+			printf ("%lu\n", (unsigned long) t);
+	}
+}
+
 static void
 show(zone, t, v)
 char *	zone;
@@ -343,13 +479,15 @@ int	v;
 	struct tm *	tmp;
 
 	(void) printf("%-*s  ", (int) longest, zone);
-	if (v)
-		(void) printf("%.24s UTC = ", asctime(gmtime(&t)));
+	if (v) {
+		showtime(t, gmtime(&t));
+		(void) printf(" UTC = ");
+	}
 	tmp = localtime(&t);
-	(void) printf("%.24s", asctime(tmp));
+	showtime(t, tmp);
 	if (*abbr(tmp) != '\0')
 		(void) printf(" %s", abbr(tmp));
-	if (v) {
+	if (v && tmp != NULL) {
 		(void) printf(" isdst=%d", tmp->tm_isdst);
 #ifdef TM_GMTOFF
 		(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
@@ -365,7 +503,7 @@ struct tm *	tmp;
 	register char *	result;
 	static char	nada;
 
-	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
+	if (!tmp || (tmp->tm_isdst != 0 && tmp->tm_isdst != 1))
 		return &nada;
 	result = tzname[tmp->tm_isdst];
 	return (result == NULL) ? &nada : result;



More information about the tz mailing list