ICU-4716 initial checkin of zic utilities

X-SVN-Rev: 19969
This commit is contained in:
Steven R. Loomis 2006-08-03 23:40:18 +00:00
parent 411a74f3bf
commit f946c487b7
11 changed files with 6208 additions and 537 deletions

View File

@ -0,0 +1,51 @@
# Some Portions Copyright (c) 2006 IBM and others. All Rights Reserved.
srcdir = @srcdir@
top_srcdir = @top_srcdir@
top_builddir = ../..
include ../../icudefs.mk
ZOBJS= zic.o localtime.o asctime.o scheck.o ialloc.o
CFLAGS+=-D_POSIX_C_SOURCE
PRIMARY_YDATA= africa antarctica asia australasia \
europe northamerica southamerica
YDATA= $(PRIMARY_YDATA) pacificnew etcetera factory backward
NDATA= systemv
SDATA= solar87 solar88 solar89
TDATA= $(YDATA) $(NDATA) $(SDATA)
all: tz2icu zic
@echo use $(MAKE) icu_data to build icu data
zic: $(TZCOBJS) yearistype tz2icu.h
$(CC) $(CFLAGS) $(LFLAGS) $(TZCOBJS) $(LDLIBS) -o $@
tz2icu: tz2icu.cpp tz2icu.h
$(CPP) -W -Wall -pedantic tz2icu.cpp -o $@
yearistype: yearistype.sh
cp yearistype.sh yearistype
chmod +x yearistype
posix_only: zic $(TDATA)
$(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L /dev/null $(TDATA)
icu_data: tz2icu posix_only
./tz2icu zoneinfo zone.tab `ls tzdata*.tar.gz | sed -e "s/^tzdata//;s/\.tar\.gz$$//"`
clean:
rm -f core *.o *.out tzselect zdump zic yearistype date
@echo ICU specific cleanup:
-rm -f ZoneMetaData.java icu_zone.txt tz2icu zoneinfo.txt
-rm -rf zoneinfo
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status

View File

@ -0,0 +1,138 @@
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** Avoid the temptation to punt entirely to strftime;
** the output of strftime is supposed to be locale specific
** whereas the output of asctime is supposed to be constant.
*/
#ifndef lint
#ifndef NOID
static char elsieid[] = "@(#)asctime.c 8.2";
#endif /* !defined NOID */
#endif /* !defined lint */
/*LINTLIBRARY*/
#include "private.h"
#include "tzfile.h"
/*
** Some systems only handle "%.2d"; others only handle "%02d";
** "%02.2d" makes (most) everybody happy.
** At least some versions of gcc warn about the %02.2d;
** we conditionalize below to avoid the warning.
*/
/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
** and may assume that the newline always lands in the same place.
** For years that are less than four digits, we pad the output with
** leading zeroes to get the newline in the traditional place.
** The -4 ensures that we get four characters of output even if
** we call a strftime variant that produces fewer characters for some years.
** The ISO C 1999 and POSIX 1003.1-2004 standards prohibit padding the year,
** but many implementations pad anyway; most likely the standards are buggy.
*/
#ifdef __GNUC__
#define ASCTIME_FMT "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %-4s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n"
#endif /* !defined __GNUC__ */
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
#ifdef __GNUC__
#define ASCTIME_FMT_B "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %s\n"
#endif /* !defined __GNUC__ */
#define STD_ASCTIME_BUF_SIZE 26
/*
** Big enough for something such as
** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
** (two three-character abbreviations, five strings denoting integers,
** seven explicit spaces, two explicit colons, a newline,
** and a trailing ASCII nul).
** The values above are for systems where an int is 32 bits and are provided
** as an example; the define below calculates the maximum for the system at
** hand.
*/
#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
char *
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;
char year[INT_STRLEN_MAXIMUM(int) + 2];
char result[MAX_ASCTIME_BUF_SIZE];
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];
/*
** Use strftime's %Y to generate the year, to avoid overflow problems
** when computing timeptr->tm_year + TM_YEAR_BASE.
** Assume that strftime is unaffected by other out-of-range members
** (e.g., timeptr->tm_mday) when processing "%Y".
*/
(void) strftime(year, sizeof year, "%Y", timeptr);
/*
** We avoid using snprintf since it's not available on all systems.
*/
(void) sprintf(result,
((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
year);
if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
(void) strcpy(buf, result);
return buf;
} else {
#ifdef EOVERFLOW
errno = EOVERFLOW;
#else /* !defined EOVERFLOW */
errno = EINVAL;
#endif /* !defined EOVERFLOW */
return NULL;
}
}
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
char *
asctime(timeptr)
register const struct tm * timeptr;
{
return asctime_r(timeptr, buf_asctime);
}

View File

@ -0,0 +1,86 @@
/*
** This file is in the public domain, so clarified as of
** 2006-07-17 by Arthur David Olson.
*/
#ifndef lint
#ifndef NOID
static char elsieid[] = "@(#)ialloc.c 8.30";
#endif /* !defined NOID */
#endif /* !defined lint */
/*LINTLIBRARY*/
#include "private.h"
#define nonzero(n) (((n) == 0) ? 1 : (n))
char *
imalloc(n)
const int n;
{
return malloc((size_t) nonzero(n));
}
char *
icalloc(nelem, elsize)
int nelem;
int elsize;
{
if (nelem == 0 || elsize == 0)
nelem = elsize = 1;
return calloc((size_t) nelem, (size_t) elsize);
}
void *
irealloc(pointer, size)
void * const pointer;
const int size;
{
if (pointer == NULL)
return imalloc(size);
return realloc((void *) pointer, (size_t) nonzero(size));
}
char *
icatalloc(old, new)
char * const old;
const char * const new;
{
register char * result;
register int oldsize, newsize;
newsize = (new == NULL) ? 0 : strlen(new);
if (old == NULL)
oldsize = 0;
else if (newsize == 0)
return old;
else oldsize = strlen(old);
if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
if (new != NULL)
(void) strcpy(result + oldsize, new);
return result;
}
char *
icpyalloc(string)
const char * const string;
{
return icatalloc((char *) NULL, string);
}
void
ifree(p)
char * const p;
{
if (p != NULL)
(void) free(p);
}
void
icfree(p)
char * const p;
{
if (p != NULL)
(void) free(p);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,478 +0,0 @@
diff -ur tzcode.orig/Makefile tzcode/Makefile
--- tzcode.orig/Makefile 2006-03-14 06:42:05.000000000 -0800
+++ tzcode/Makefile 2006-04-03 10:24:01.000000000 -0700
@@ -40,7 +40,7 @@
# (and subdirectories).
# Use an absolute path name for TZDIR unless you're just testing the software.
-TZDIR= $(TOPDIR)/etc/zoneinfo
+TZDIR= zoneinfo
# The "tzselect", "zic", and "zdump" commands get installed in. . .
@@ -241,9 +241,11 @@
###############################################################################
-cc= cc
+cc= gcc
CC= $(cc) -DTZDIR=\"$(TZDIR)\"
+CPP= g++
+
TZCSRCS= zic.c localtime.c asctime.c scheck.c ialloc.c
TZCOBJS= zic.o localtime.o asctime.o scheck.o ialloc.o
TZDSRCS= zdump.c localtime.c ialloc.c
@@ -277,7 +279,10 @@
SHELL= /bin/sh
-all: tzselect zic zdump $(LIBOBJS)
+LS= /bin/ls
+SED= /bin/sed
+
+all: tzselect zic zdump $(LIBOBJS) tz2icu
ALL: all date
@@ -310,9 +315,12 @@
zdump: $(TZDOBJS)
$(CC) $(CFLAGS) $(LFLAGS) $(TZDOBJS) $(LDLIBS) -o $@
-zic: $(TZCOBJS) yearistype
+zic: $(TZCOBJS) yearistype tz2icu.h
$(CC) $(CFLAGS) $(LFLAGS) $(TZCOBJS) $(LDLIBS) -o $@
+tz2icu: tz2icu.cpp tz2icu.h
+ $(CPP) -W -Wall -pedantic tz2icu.cpp -o $@
+
yearistype: yearistype.sh
cp yearistype.sh yearistype
chmod +x yearistype
@@ -323,6 +331,9 @@
right_only: zic leapseconds $(TDATA)
$(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L leapseconds $(TDATA)
+icu_data: tz2icu posix_only
+ ./tz2icu zoneinfo zone.tab `$(LS) tzdata*.tar.gz | $(SED) -e "s/^tzdata//;s/\.tar\.gz$$//"`
+
# In earlier versions of this makefile, the other two directories were
# subdirectories of $(TZDIR). However, this led to configuration errors.
# For example, with posix_right under the earlier scheme,
diff -ur tzcode.orig/zic.c tzcode/zic.c
--- tzcode.orig/zic.c 2006-04-03 07:09:14.000000000 -0700
+++ tzcode/zic.c 2006-04-03 11:31:13.000000000 -0700
@@ -21,6 +21,20 @@
#define MKDIR_UMASK 0755
#endif
+/* Enable extensions and modifications for ICU. */
+#define ICU
+
+/* Continue executing after link failure. Even if ICU is undefined
+ * (for vanilla zic behavior), ICU_LINKS should be defined, since zic
+ * appears to fail on the 2003 data the first time through during the
+ * linking phase. Running zic twice, with ICU_LINKS defined, causes
+ * links to be handled correctly. */
+#define ICU_LINKS
+
+#ifdef ICU
+#include "tz2icu.h"
+#endif
+
/*
** On some ancient hosts, predicates like `isspace(C)' are defined
** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
@@ -101,8 +115,14 @@
extern int optind;
static void addtt P((zic_t starttime, int type));
+#ifdef ICU
+static int addtype P((long gmtoff, long rawoff, long dstoff,
+ const char * abbr, int isdst,
+ int ttisstd, int ttisgmt));
+#else
static int addtype P((long gmtoff, const char * abbr, int isdst,
int ttisstd, int ttisgmt));
+#endif
static void leapadd P((zic_t t, int positive, int rolling, int count));
static void adjleap P((void));
static void associate P((void));
@@ -282,6 +302,18 @@
const int l_value;
};
+#ifdef ICU
+
+/* Indices into rules[] for final rules. They will occur in pairs,
+ * with finalRules[i] occurring before finalRules[i+1] in the year.
+ * Each zone need only store a start year, a standard offset, and an
+ * index into finalRules[]. FinalRules[] are aliases into rules[]. */
+
+static const struct rule ** finalRules;
+static int finalRulesCount;
+
+#endif
+
static struct lookup const * byword P((const char * string,
const struct lookup * lp));
@@ -364,6 +396,11 @@
unsigned char type;
} attypes[TZ_MAX_TIMES];
static long gmtoffs[TZ_MAX_TYPES];
+#ifdef ICU
+/* gmtoffs[i] = rawoffs[i] + dstoffs[i] */
+static long rawoffs[TZ_MAX_TYPES];
+static long dstoffs[TZ_MAX_TYPES];
+#endif
static char isdsts[TZ_MAX_TYPES];
static unsigned char abbrinds[TZ_MAX_TYPES];
static char ttisstds[TZ_MAX_TYPES];
@@ -475,6 +512,62 @@
exit(EXIT_FAILURE);
}
+#ifdef ICU
+
+/* File into which we will write supplemental ICU data. */
+static FILE * icuFile;
+
+void emit_icu_zone(FILE* f, const char* zoneName, int zoneOffset,
+ const struct rule* rule,
+ int ruleIndex, int startYear) {
+ /* machine-readable section */
+ fprintf(f, "zone %s %d %d %s", zoneName, zoneOffset, startYear, rule->r_name);
+
+ /* human-readable section */
+ fprintf(f, " # zone %s, offset %d, year >= %d, rule %s (%d)\n",
+ zoneName, zoneOffset, startYear,
+ rule->r_name, ruleIndex);
+}
+
+void emit_icu_link(FILE* f, const char* from, const char* to) {
+ /* machine-readable section */
+ fprintf(f, "link %s %s\n", from, to);
+}
+
+static const char* DYCODE[] = {"DOM", "DOWGEQ", "DOWLEQ"};
+
+void emit_icu_rule(FILE* f, const struct rule* r, int ruleIndex) {
+ if (r->r_yrtype != NULL) {
+ warning("year types not supported by ICU");
+ fprintf(stderr, "rule %s, file %s, line %d\n",
+ r->r_name, r->r_filename, r->r_linenum);
+ }
+
+ /* machine-readable section */
+ fprintf(f, "rule %s %s %d %d %d %d %d %d %d",
+ r->r_name, DYCODE[r->r_dycode],
+ r->r_month, r->r_dayofmonth,
+ (r->r_dycode == DC_DOM ? -1 : r->r_wday),
+ r->r_tod, r->r_todisstd, r->r_todisgmt, r->r_stdoff
+ );
+
+ /* human-readable section */
+ fprintf(f, " # %d: %s, file %s, line %d",
+ ruleIndex, r->r_name, r->r_filename, r->r_linenum);
+ fprintf(f, ", mode %s", DYCODE[r->r_dycode]);
+ fprintf(f, ", %s, dom %d", mon_names[r->r_month].l_word, r->r_dayofmonth);
+ if (r->r_dycode != DC_DOM) {
+ fprintf(f, ", %s", wday_names[r->r_wday].l_word);
+ }
+ fprintf(f, ", time %d", r->r_tod);
+ fprintf(f, ", isstd %d", r->r_todisstd);
+ fprintf(f, ", isgmt %d", r->r_todisgmt);
+ fprintf(f, ", offset %ld", r->r_stdoff);
+ fprintf(f, "\n");
+}
+
+#endif
+
static const char * psxrules;
static const char * lcltime;
static const char * directory;
@@ -586,6 +679,14 @@
adjleap();
}
+#ifdef ICU
+ if ((icuFile = fopen(ICU_ZONE_FILE, "w")) == NULL) {
+ const char *e = strerror(errno);
+ (void) fprintf(stderr, _("%s: Can't open %s: %s\n"),
+ progname, ICU_ZONE_FILE, e);
+ (void) exit(EXIT_FAILURE);
+ }
+#endif
for (i = optind; i < argc; ++i)
infile(argv[i]);
if (errors)
@@ -605,6 +706,9 @@
for (i = 0; i < nlinks; ++i) {
eat(links[i].l_filename, links[i].l_linenum);
dolink(links[i].l_from, links[i].l_to);
+#ifdef ICU
+ emit_icu_link(icuFile, links[i].l_from, links[i].l_to);
+#endif
if (noise)
for (j = 0; j < nlinks; ++j)
if (strcmp(links[i].l_to,
@@ -619,6 +723,11 @@
eat("command line", 1);
dolink(psxrules, TZDEFRULES);
}
+#ifdef ICU
+ for (i=0; i<finalRulesCount; ++i) {
+ emit_icu_rule(icuFile, finalRules[i], i);
+ }
+#endif /*ICU*/
return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
@@ -684,7 +793,9 @@
(void) fprintf(stderr,
_("%s: Can't link from %s to %s: %s\n"),
progname, fromname, toname, e);
+#ifndef ICU_LINKS
exit(EXIT_FAILURE);
+#endif
}
}
ifree(fromname);
@@ -1650,7 +1761,12 @@
#define DO(field) (void) fwrite((void *) tzh.field, \
(size_t) sizeof tzh.field, (size_t) 1, fp)
tzh = tzh0;
+#ifdef ICU
+ * (ICUZoneinfoVersion*) &tzh.tzh_reserved = TZ_ICU_VERSION;
+ (void) strncpy(tzh.tzh_magic, TZ_ICU_MAGIC, sizeof tzh.tzh_magic);
+#else
(void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+#endif
tzh.tzh_version[0] = ZIC_VERSION;
convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
@@ -1683,7 +1799,12 @@
}
for (i = 0; i < typecnt; ++i)
if (writetype[i]) {
- puttzcode(gmtoffs[i], fp);
+#ifdef ICU
+ puttzcode((long) rawoffs[i], fp);
+ puttzcode((long) dstoffs[i], fp);
+#else
+ puttzcode((long) gmtoffs[i], fp);
+#endif
(void) putc(isdsts[i], fp);
(void) putc((unsigned char) indmap[abbrinds[i]], fp);
}
@@ -1952,6 +2073,24 @@
}
}
+#ifdef ICU
+
+int add_icu_final_rules(const struct rule* r1, const struct rule* r2) {
+ int i;
+
+ for (i=0; i<finalRulesCount; ++i) { /* i+=2 should work too */
+ if (r1==finalRules[i]) return i; /* [sic] pointer comparison */
+ }
+
+ finalRules = (const struct rule**) (void*) erealloc((char *) finalRules,
+ (finalRulesCount + 2) * sizeof(*finalRules));
+ finalRules[finalRulesCount++] = r1;
+ finalRules[finalRulesCount++] = r2;
+ return finalRulesCount - 2;
+}
+
+#endif /*ICU*/
+
static void
outzone(zpfirst, zonecount)
const struct zone * const zpfirst;
@@ -1974,6 +2113,11 @@
register char * envvar;
register int max_abbr_len;
register int max_envvar_len;
+#ifdef ICU
+ int finalRuleYear, finalRuleIndex;
+ const struct rule* finalRule1;
+ const struct rule* finalRule2;
+#endif
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2050,11 +2194,56 @@
eat(zp->z_filename, zp->z_linenum);
*startbuf = '\0';
startoff = zp->z_gmtoff;
+#ifdef ICU
+ finalRuleYear = finalRuleIndex = -1;
+ finalRule1 = finalRule2 = NULL;
+ if (i == (zonecount - 1)) { /* !useuntil */
+ /* Look for exactly 2 rules that end at 'max' and
+ * note them. Determine max(r_loyear) for the 2 of
+ * them. */
+ for (j=0; j<zp->z_nrules; ++j) {
+ rp = &zp->z_rules[j];
+ if (rp->r_hiyear == INT_MAX) {
+ if (finalRule1 == NULL) {
+ finalRule1 = rp;
+ finalRuleYear = rp->r_loyear;
+ } else if (finalRule2 == NULL) {
+ finalRule2 = rp;
+ if (rp->r_loyear > finalRuleYear) {
+ finalRuleYear = rp->r_loyear;
+ }
+ } else {
+ error("more than two max rules found (ICU)");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ if (finalRule1 != NULL && finalRule2 == NULL) {
+ error("only one max rule found (ICU)");
+ exit(EXIT_FAILURE);
+ }
+ if (finalRule1 != NULL) {
+ /* Swap if necessary so finalRule1 occurs before
+ * finalRule2 */
+ if (finalRule1->r_month > finalRule2->r_month) {
+ const struct rule* t = finalRule1;
+ finalRule1 = finalRule2;
+ finalRule2 = t;
+ }
+ /* Add final rule to our list */
+ finalRuleIndex = add_icu_final_rules(finalRule1, finalRule2);
+ }
+ }
+#endif
+
if (zp->z_nrules == 0) {
stdoff = zp->z_stdoff;
doabbr(startbuf, zp->z_format,
(char *) NULL, stdoff != 0, FALSE);
type = addtype(oadd(zp->z_gmtoff, stdoff),
+#ifdef ICU
+ zp->z_gmtoff, stdoff,
+#endif
startbuf, stdoff != 0, startttisstd,
startttisgmt);
if (usestart) {
@@ -2127,6 +2316,15 @@
break; /* go on to next year */
rp = &zp->z_rules[k];
rp->r_todo = FALSE;
+#ifdef ICU
+ if (year >= finalRuleYear && rp == finalRule1) {
+ emit_icu_zone(icuFile,
+ zpfirst->z_name, zp->z_gmtoff,
+ rp, finalRuleIndex, year);
+ /* only emit this for the first year */
+ finalRule1 = NULL;
+ }
+#endif
if (useuntil && ktime >= untiltime)
break;
stdoff = rp->r_stdoff;
@@ -2158,8 +2356,14 @@
doabbr(ab, zp->z_format, rp->r_abbrvar,
rp->r_stdoff != 0, FALSE);
offset = oadd(zp->z_gmtoff, rp->r_stdoff);
+#ifdef ICU
+ type = addtype(offset, zp->z_gmtoff, rp->r_stdoff,
+ ab, rp->r_stdoff != 0,
+ rp->r_todisstd, rp->r_todisgmt);
+#else
type = addtype(offset, ab, rp->r_stdoff != 0,
rp->r_todisstd, rp->r_todisgmt);
+#endif
addtt(ktime, type);
}
}
@@ -2173,10 +2377,19 @@
if (*startbuf == '\0')
error(_("can't determine time zone abbreviation to use just after until time"));
else addtt(starttime,
+#ifdef ICU
+ addtype(startoff,
+ zp->z_gmtoff, startoff - zp->z_gmtoff,
+ startbuf,
+ startoff != zp->z_gmtoff,
+ startttisstd,
+ startttisgmt));
+#else
addtype(startoff, startbuf,
startoff != zp->z_gmtoff,
startttisstd,
startttisgmt));
+#endif
}
/*
** Now we may get to set starttime for the next zone line.
@@ -2205,6 +2418,10 @@
if (starttime <= min_time ||
(timecnt == 1 && attypes[0].at < min_time)) {
gmtoffs[0] = gmtoffs[type];
+#ifdef ICU
+ rawoffs[0] = rawoffs[type];
+ dstoffs[0] = dstoffs[type];
+#endif
isdsts[0] = isdsts[type];
ttisstds[0] = ttisstds[type];
ttisgmts[0] = ttisgmts[type];
@@ -2226,8 +2443,15 @@
}
static int
+#ifdef ICU
+addtype(gmtoff, rawoff, dstoff, abbr, isdst, ttisstd, ttisgmt)
+const long gmtoff;
+const long rawoff;
+const long dstoff;
+#else
addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
const long gmtoff;
+#endif
const char * const abbr;
const int isdst;
const int ttisstd;
@@ -2247,12 +2471,25 @@
error(_("internal error - addtype called with bad ttisgmt"));
exit(EXIT_FAILURE);
}
+#ifdef ICU
+ if (isdst != (dstoff != 0)) {
+ error(_("internal error - addtype called with bad isdst/dstoff"));
+ (void) exit(EXIT_FAILURE);
+ }
+ if (gmtoff != (rawoff + dstoff)) {
+ error(_("internal error - addtype called with bad gmt/raw/dstoff"));
+ (void) exit(EXIT_FAILURE);
+ }
+#endif
/*
** See if there's already an entry for this zone type.
** If so, just return its index.
*/
for (i = 0; i < typecnt; ++i) {
if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
+#ifdef ICU
+ rawoff == rawoffs[i] && dstoff == dstoffs[i] &&
+#endif
strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
ttisstd == ttisstds[i] &&
ttisgmt == ttisgmts[i])
@@ -2267,6 +2504,10 @@
exit(EXIT_FAILURE);
}
gmtoffs[i] = gmtoff;
+#ifdef ICU
+ rawoffs[i] = rawoff;
+ dstoffs[i] = dstoff;
+#endif
isdsts[i] = isdst;
ttisstds[i] = ttisstd;
ttisgmts[i] = ttisgmt;

View File

@ -0,0 +1,368 @@
#ifndef PRIVATE_H
#define PRIVATE_H
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** ID
*/
#ifndef lint
#ifndef NOID
static char privatehid[] = "@(#)private.h 8.2";
#endif /* !defined NOID */
#endif /* !defined lint */
#define GRANDPARENTED "Local time zone must be set--see zic manual page"
/*
** Defaults for preprocessor symbols.
** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
*/
#ifndef HAVE_ADJTIME
#define HAVE_ADJTIME 1
#endif /* !defined HAVE_ADJTIME */
#ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */
#ifndef HAVE_INCOMPATIBLE_CTIME_R
#define HAVE_INCOMPATIBLE_CTIME_R 0
#endif /* !defined INCOMPATIBLE_CTIME_R */
#ifndef HAVE_SETTIMEOFDAY
#define HAVE_SETTIMEOFDAY 3
#endif /* !defined HAVE_SETTIMEOFDAY */
#ifndef HAVE_STRERROR
#define HAVE_STRERROR 1
#endif /* !defined HAVE_STRERROR */
#ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
#ifndef HAVE_SYS_STAT_H
#define HAVE_SYS_STAT_H 1
#endif /* !defined HAVE_SYS_STAT_H */
#ifndef HAVE_SYS_WAIT_H
#define HAVE_SYS_WAIT_H 1
#endif /* !defined HAVE_SYS_WAIT_H */
#ifndef HAVE_UNISTD_H
#define HAVE_UNISTD_H 1
#endif /* !defined HAVE_UNISTD_H */
#ifndef HAVE_UTMPX_H
#define HAVE_UTMPX_H 0
#endif /* !defined HAVE_UTMPX_H */
#ifndef LOCALE_HOME
#define LOCALE_HOME "/usr/lib/locale"
#endif /* !defined LOCALE_HOME */
#if HAVE_INCOMPATIBLE_CTIME_R
#define asctime_r _incompatible_asctime_r
#define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/*
** Nested includes
*/
#include "sys/types.h" /* for time_t */
#include "stdio.h"
#include "errno.h"
#include "string.h"
#include "limits.h" /* for CHAR_BIT et al. */
#include "time.h"
#include "stdlib.h"
#if HAVE_GETTEXT
#include "libintl.h"
#endif /* HAVE_GETTEXT */
#if HAVE_SYS_WAIT_H
#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
#endif /* HAVE_SYS_WAIT_H */
#ifndef WIFEXITED
#define WIFEXITED(status) (((status) & 0xff) == 0)
#endif /* !defined WIFEXITED */
#ifndef WEXITSTATUS
#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
#endif /* !defined WEXITSTATUS */
#if HAVE_UNISTD_H
#include "unistd.h" /* for F_OK and R_OK */
#endif /* HAVE_UNISTD_H */
#if !HAVE_UNISTD_H
#ifndef F_OK
#define F_OK 0
#endif /* !defined F_OK */
#ifndef R_OK
#define R_OK 4
#endif /* !defined R_OK */
#endif /* !HAVE_UNISTD_H */
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
/*
** Define HAVE_STDINT_H's default value here, rather than at the
** start, since __GLIBC__'s value depends on previously-included
** files.
** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
*/
#ifndef HAVE_STDINT_H
#define HAVE_STDINT_H \
(199901 <= __STDC_VERSION__ || \
2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
#endif /* !defined HAVE_STDINT_H */
#if HAVE_STDINT_H
#include "stdint.h"
#endif /* !HAVE_STDINT_H */
#ifndef INT_FAST64_MAX
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
#if defined LLONG_MAX || defined __LONG_LONG_MAX__
typedef long long int_fast64_t;
#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#if (LONG_MAX >> 31) < 0xffffffff
Please use a compiler that supports a 64-bit integer type (or wider);
you may need to compile with "-DHAVE_STDINT_H".
#endif /* (LONG_MAX >> 31) < 0xffffffff */
typedef long int_fast64_t;
#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#endif /* !defined INT_FAST64_MAX */
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif /* !defined INT32_MAX */
#ifndef INT32_MIN
#define INT32_MIN (-1 - INT32_MAX)
#endif /* !defined INT32_MIN */
/*
** Workarounds for compilers/systems.
*/
/*
** If your compiler lacks prototypes, "#define P(x) ()".
*/
#ifndef P
#define P(x) x
#endif /* !defined P */
/*
** SunOS 4.1.1 headers lack EXIT_SUCCESS.
*/
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif /* !defined EXIT_SUCCESS */
/*
** SunOS 4.1.1 headers lack EXIT_FAILURE.
*/
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif /* !defined EXIT_FAILURE */
/*
** SunOS 4.1.1 headers lack FILENAME_MAX.
*/
#ifndef FILENAME_MAX
#ifndef MAXPATHLEN
#ifdef unix
#include "sys/param.h"
#endif /* defined unix */
#endif /* !defined MAXPATHLEN */
#ifdef MAXPATHLEN
#define FILENAME_MAX MAXPATHLEN
#endif /* defined MAXPATHLEN */
#ifndef MAXPATHLEN
#define FILENAME_MAX 1024 /* Pure guesswork */
#endif /* !defined MAXPATHLEN */
#endif /* !defined FILENAME_MAX */
/*
** SunOS 4.1.1 libraries lack remove.
*/
#ifndef remove
extern int unlink P((const char * filename));
#define remove unlink
#endif /* !defined remove */
/*
** Some ancient errno.h implementations don't declare errno.
** But some newer errno.h implementations define it as a macro.
** Fix the former without affecting the latter.
*/
#ifndef errno
extern int errno;
#endif /* !defined errno */
/*
** Some time.h implementations don't declare asctime_r.
** Others might define it as a macro.
** Fix the former without affecting the latter.
*/
#ifndef asctime_r
extern char * asctime_r();
#endif
/*
** Private function declarations.
*/
char * icalloc P((int nelem, int elsize));
char * icatalloc P((char * old, const char * new));
char * icpyalloc P((const char * string));
char * imalloc P((int n));
void * irealloc P((void * pointer, int size));
void icfree P((char * pointer));
void ifree P((char * pointer));
const char * scheck P((const char * string, const char * format));
/*
** Finally, some convenience items.
*/
#ifndef TRUE
#define TRUE 1
#endif /* !defined TRUE */
#ifndef FALSE
#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 */
/*
** Since the definition of TYPE_INTEGRAL contains floating point numbers,
** it cannot be used in preprocessor directives.
*/
#ifndef TYPE_INTEGRAL
#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
#endif /* !defined TYPE_INTEGRAL */
#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 */
/*
** INITIALIZE(x)
*/
#ifndef GNUC_or_lint
#ifdef lint
#define GNUC_or_lint
#endif /* defined lint */
#ifndef lint
#ifdef __GNUC__
#define GNUC_or_lint
#endif /* defined __GNUC__ */
#endif /* !defined lint */
#endif /* !defined GNUC_or_lint */
#ifndef INITIALIZE
#ifdef GNUC_or_lint
#define INITIALIZE(x) ((x) = 0)
#endif /* defined GNUC_or_lint */
#ifndef GNUC_or_lint
#define INITIALIZE(x)
#endif /* !defined GNUC_or_lint */
#endif /* !defined INITIALIZE */
/*
** For the benefit of GNU folk...
** `_(MSGID)' uses the current locale's message library string for MSGID.
** The default is to use gettext if available, and use MSGID otherwise.
*/
#ifndef _
#if HAVE_GETTEXT
#define _(msgid) gettext(msgid)
#else /* !HAVE_GETTEXT */
#define _(msgid) msgid
#endif /* !HAVE_GETTEXT */
#endif /* !defined _ */
#ifndef TZ_DOMAIN
#define TZ_DOMAIN "tz"
#endif /* !defined TZ_DOMAIN */
#if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r
#undef ctime_r
char *asctime_r P((struct tm const *, char *));
char *ctime_r P((time_t const *, char *));
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
#ifndef YEARSPERREPEAT
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#endif /* !defined YEARSPERREPEAT */
/*
** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
*/
#ifndef AVGSECSPERYEAR
#define AVGSECSPERYEAR 31556952L
#endif /* !defined AVGSECSPERYEAR */
#ifndef SECSPERREPEAT
#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
#endif /* !defined SECSPERREPEAT */
#ifndef SECSPERREPEAT_BITS
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined SECSPERREPEAT_BITS */
/*
** UNIX was a registered trademark of The Open Group in 2003.
*/
#endif /* !defined PRIVATE_H */

View File

@ -7,6 +7,9 @@
* Since: ICU 2.8
**********************************************************************
Note: this directory currently contains tzcode as of tzcode2006h.tar.gz
----------------------------------------------------------------------
OVERVIEW
@ -62,16 +65,18 @@ where YYYY is the year and V is the version letter ('a'...'z').
----------------------------------------------------------------------
HOWTO
1. Obtain the current versions of tzcodeYYYYV.tar.gz (aka `tzcode')
and tzdataYYYYV.tar.gz (aka `tzdata') from the FTP site given
0. Generate Makefile
( cd ../.. ; CONFIG_FILES=tools/tzcode/Makefile CONFIG_HEADERS= sh config.status )
1. Obtain the current versions of tzdataYYYYV.tar.gz (aka `tzdata') from the FTP site given
above. Either manually download or use wget:
$ cd {path_to}/icu/source/tools/tzcode
$ wget "ftp://elsie.nci.nih.gov/pub/tz*.tar.gz"
2. Unpack tzcode and tzdata directly into the directory tzcode:
2. Unpack tzdata directly into the directory tzcode:
$ tar xzvf tzcode*.tar.gz
$ tar xzvf tzdata*.tar.gz
*** Make sure you only have ONE FILE named tzdata*.tar.gz in the
@ -81,71 +86,20 @@ HOWTO
The Makefile looks in the current directory to determine the
version of Olson data it is building by looking for tzdata*.tar.gz.
3. Apply the ICU patch to zic.c:
$ patch < patch-icu-tzcode
If patch complains at this point, there is a mismatch that must be
manually addressed. See the CVS log of `patch-icu-tzcode' for
version details.
4. Build:
3. Build:
$ make icu_data
5. Copy the data files to the correct location in the ICU4C/ICU4J
4. Copy the data files to the correct location in the ICU4C/ICU4J
source trees:
$ cp zoneinfo.txt ../../data/misc/
$ cp ZoneMetaData.java {path_to}/icu4j/src/com/ibm/icu/impl
6. Rebuild ICU:
5. Rebuild ICU:
$ cd ../..
$ {*make}
7. Don't forget to check in the new zoneinfo.txt (from its location at
6. Don't forget to check in the new zoneinfo.txt (from its location at
{path_to}/icu/source/data/misc/zoneinfo.txt) into CVS.
----------------------------------------------------------------------
HOWTO platform issues
00. On macosx, I had to do "sudo ln -s /usr/bin/sed /bin/" first
00. On macosx, I had to build with "make CPPFLAGS=-DSTD_INSPIRED icu_data"
----------------------------------------------------------------------
HOWTO regenerate patch-icu-tzcode
If you need to edit any of the tzcode* files, you will need to
regenerate the patch file as follows.
1. Follow the above instructions to extract and patch the tzcode*
files in {path_to}/icu/source/tools/tzcode. Modify any of the
tzcode files.
2. Extract a clean set of the tzcode* files into a new directory,
../tzcode.orig/:
$ mkdir ../tzcode.orig
$ cd ../tzcode.orig
$ tar xzf ../tzcode/tzcode*.tar.gz
$ cd ../tzcode
3. Compute diffs, ignoring files that are in only one directory:
$ diff -ur ../tzcode.orig . | grep -vE -e "^Only in " > patch-icu-tzcode
4. Test the patch-icu-tzcode file by regenerating and diffing the
files again in another directory. The expected output from the
final diff command is *nothing*.
$ mkdir ../tzcode.new
$ cd ../tzcode.new
$ tar xzf ../tzcode/tzcode*.tar.gz
$ patch < ../tzcode/patch-icu-tzcode
$ cd ../tzcode
$ diff -ur ../tzcode.new . | grep -vE -e "^Only in "
5. Check in the new patch-icu-tzcode file.
----------------------------------------------------------------------
eof

View File

@ -0,0 +1,63 @@
/*
** This file is in the public domain, so clarified as of
** 2006-07-17 by Arthur David Olson.
*/
#ifndef lint
#ifndef NOID
static char elsieid[] = "@(#)scheck.c 8.19";
#endif /* !defined lint */
#endif /* !defined NOID */
/*LINTLIBRARY*/
#include "private.h"
const char *
scheck(string, format)
const char * const string;
const char * const format;
{
register char * fbuf;
register const char * fp;
register char * tp;
register int c;
register const char * result;
char dummy;
result = "";
if (string == NULL || format == NULL)
return result;
fbuf = imalloc((int) (2 * strlen(format) + 4));
if (fbuf == NULL)
return result;
fp = format;
tp = fbuf;
while ((*tp++ = c = *fp++) != '\0') {
if (c != '%')
continue;
if (*fp == '%') {
*tp++ = *fp++;
continue;
}
*tp++ = '*';
if (*fp == '*')
++fp;
while (is_digit(*fp))
*tp++ = *fp++;
if (*fp == 'l' || *fp == 'h')
*tp++ = *fp++;
else if (*fp == '[')
do *tp++ = *fp++;
while (*fp != '\0' && *fp != ']');
if ((*tp++ = *fp++) == '\0')
break;
}
*(tp - 1) = '%';
*tp++ = 'c';
*tp = '\0';
if (sscanf(string, fbuf, &dummy) != 1)
result = (char *) format;
ifree(fbuf);
return result;
}

View File

@ -0,0 +1,180 @@
#ifndef TZFILE_H
#define TZFILE_H
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** ID
*/
#ifndef lint
#ifndef NOID
static char tzfilehid[] = "@(#)tzfile.h 8.1";
#endif /* !defined NOID */
#endif /* !defined lint */
/*
** Information about time zone files.
*/
#ifndef TZDIR
#define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
#define TZDEFAULT "localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
#define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
/*
** Each file begins with. . .
*/
#define TZ_MAGIC "TZif"
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' as of 2005 */
char tzh_reserved[15]; /* reserved--must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
char tzh_timecnt[4]; /* coded number of transition times */
char tzh_typecnt[4]; /* coded number of local time types */
char tzh_charcnt[4]; /* coded number of abbr. chars */
};
/*
** . . .followed by. . .
**
** tzh_timecnt (char [4])s coded transition times a la time(2)
** tzh_timecnt (unsigned char)s types of local time starting at above
** tzh_typecnt repetitions of
** one (char [4]) coded UTC offset in seconds
** one (unsigned char) used to set tm_isdst
** one (unsigned char) that's an abbreviation list index
** tzh_charcnt (char)s '\0'-terminated zone abbreviations
** tzh_leapcnt repetitions of
** one (char [4]) coded leap second transition times
** one (char [4]) total correction after above
** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
** time is standard time, if FALSE,
** transition time is wall clock time
** if absent, transition times are
** assumed to be wall clock time
** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
** time is UTC, if FALSE,
** transition time is local time
** if absent, transition times are
** assumed to be local time
*/
/*
** If tzh_version is '2' or greater, the above is followed by a second instance
** of tzhead and a second instance of the data in which each coded transition
** time uses 8 rather than 4 chars,
** then a POSIX-TZ-environment-variable-style string for use in handling
** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for
** such instants).
*/
/*
** In the current implementation, "tzset()" refuses to deal with files that
** exceed any of the limits below.
*/
#ifndef TZ_MAX_TIMES
#define TZ_MAX_TIMES 1200
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
#ifndef NOSOLAR
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined NOSOLAR */
#ifdef NOSOLAR
/*
** Must be at least 14 for Europe/Riga as of Jan 12 1995,
** as noted by Earl Chew.
*/
#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
#endif /* !defined NOSOLAR */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_JANUARY 0
#define TM_FEBRUARY 1
#define TM_MARCH 2
#define TM_APRIL 3
#define TM_MAY 4
#define TM_JUNE 5
#define TM_JULY 6
#define TM_AUGUST 7
#define TM_SEPTEMBER 8
#define TM_OCTOBER 9
#define TM_NOVEMBER 10
#define TM_DECEMBER 11
#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
** Since everything in isleap is modulo 400 (or a factor of 400), we know that
** isleap(y) == isleap(y % 400)
** and so
** isleap(a + b) == isleap((a + b) % 400)
** or
** isleap(a + b) == isleap(a % 400 + b % 400)
** This is true even if % means modulo rather than Fortran remainder
** (which is allowed by C89 but not C99).
** We use this to avoid addition overflow problems.
*/
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined TZFILE_H */

View File

@ -0,0 +1,308 @@
#! /bin/ksh
# '@(#)tzselect.ksh 8.1'
# Ask the user about the time zone, and output the resulting TZ value to stdout.
# Interact with the user via stderr and stdin.
# Contributed by Paul Eggert.
# Porting notes:
#
# This script requires several features of the Korn shell.
# If your host lacks the Korn shell,
# you can use either of the following free programs instead:
#
# <a href=ftp://ftp.gnu.org/pub/gnu/>
# Bourne-Again shell (bash)
# </a>
#
# <a href=ftp://ftp.cs.mun.ca/pub/pdksh/pdksh.tar.gz>
# Public domain ksh
# </a>
#
# This script also uses several features of modern awk programs.
# If your host lacks awk, or has an old awk that does not conform to Posix.2,
# you can use either of the following free programs instead:
#
# <a href=ftp://ftp.gnu.org/pub/gnu/>
# GNU awk (gawk)
# </a>
#
# <a href=ftp://ftp.whidbey.net/pub/brennan/>
# mawk
# </a>
# Specify default values for environment variables if they are unset.
: ${AWK=awk}
: ${TZDIR=$(pwd)}
# Check for awk Posix compliance.
($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
[ $? = 123 ] || {
echo >&2 "$0: Sorry, your \`$AWK' program is not Posix compatible."
exit 1
}
# Make sure the tables are readable.
TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
TZ_ZONE_TABLE=$TZDIR/zone.tab
for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
do
<$f || {
echo >&2 "$0: time zone files are not set up correctly"
exit 1
}
done
newline='
'
IFS=$newline
# Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
case $(echo 1 | (select x in x; do break; done) 2>/dev/null) in
?*) PS3=
esac
# Begin the main loop. We come back here if the user wants to retry.
while
echo >&2 'Please identify a location' \
'so that time zone rules can be set correctly.'
continent=
country=
region=
# Ask the user for continent or ocean.
echo >&2 'Please select a continent or ocean.'
select continent in \
Africa \
Americas \
Antarctica \
'Arctic Ocean' \
Asia \
'Atlantic Ocean' \
Australia \
Europe \
'Indian Ocean' \
'Pacific Ocean' \
'none - I want to specify the time zone using the Posix TZ format.'
do
case $continent in
'')
echo >&2 'Please enter a number in range.';;
?*)
case $continent in
Americas) continent=America;;
*' '*) continent=$(expr "$continent" : '\([^ ]*\)')
esac
break
esac
done
case $continent in
'')
exit 1;;
none)
# Ask the user for a Posix TZ string. Check that it conforms.
while
echo >&2 'Please enter the desired value' \
'of the TZ environment variable.'
echo >&2 'For example, GST-10 is a zone named GST' \
'that is 10 hours ahead (east) of UTC.'
read TZ
$AWK -v TZ="$TZ" 'BEGIN {
tzname = "[^-+,0-9][^-+,0-9][^-+,0-9]+"
time = "[0-2]?[0-9](:[0-5][0-9](:[0-5][0-9])?)?"
offset = "[-+]?" time
date = "(J?[0-9]+|M[0-9]+\.[0-9]+\.[0-9]+)"
datetime = "," date "(/" time ")?"
tzpattern = "^(:.*|" tzname offset "(" tzname \
"(" offset ")?(" datetime datetime ")?)?)$"
if (TZ ~ tzpattern) exit 1
exit 0
}'
do
echo >&2 "\`$TZ' is not a conforming" \
'Posix time zone string.'
done
TZ_for_date=$TZ;;
*)
# Get list of names of countries in the continent or ocean.
countries=$($AWK -F'\t' \
-v continent="$continent" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
/^#/ { next }
$3 ~ ("^" continent "/") {
if (!cc_seen[$1]++) cc_list[++ccs] = $1
}
END {
while (getline <TZ_COUNTRY_TABLE) {
if ($0 !~ /^#/) cc_name[$1] = $2
}
for (i = 1; i <= ccs; i++) {
country = cc_list[i]
if (cc_name[country]) {
country = cc_name[country]
}
print country
}
}
' <$TZ_ZONE_TABLE | sort -f)
# If there's more than one country, ask the user which one.
case $countries in
*"$newline"*)
echo >&2 'Please select a country.'
select country in $countries
do
case $country in
'') echo >&2 'Please enter a number in range.';;
?*) break
esac
done
case $country in
'') exit 1
esac;;
*)
country=$countries
esac
# Get list of names of time zone rule regions in the country.
regions=$($AWK -F'\t' \
-v country="$country" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
BEGIN {
cc = country
while (getline <TZ_COUNTRY_TABLE) {
if ($0 !~ /^#/ && country == $2) {
cc = $1
break
}
}
}
$1 == cc { print $4 }
' <$TZ_ZONE_TABLE)
# If there's more than one region, ask the user which one.
case $regions in
*"$newline"*)
echo >&2 'Please select one of the following' \
'time zone regions.'
select region in $regions
do
case $region in
'') echo >&2 'Please enter a number in range.';;
?*) break
esac
done
case $region in
'') exit 1
esac;;
*)
region=$regions
esac
# Determine TZ from country and region.
TZ=$($AWK -F'\t' \
-v country="$country" \
-v region="$region" \
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
'
BEGIN {
cc = country
while (getline <TZ_COUNTRY_TABLE) {
if ($0 !~ /^#/ && country == $2) {
cc = $1
break
}
}
}
$1 == cc && $4 == region { print $3 }
' <$TZ_ZONE_TABLE)
# Make sure the corresponding zoneinfo file exists.
TZ_for_date=$TZDIR/$TZ
<$TZ_for_date || {
echo >&2 "$0: time zone files are not set up correctly"
exit 1
}
esac
# Use the proposed TZ to output the current date relative to UTC.
# Loop until they agree in seconds.
# Give up after 8 unsuccessful tries.
extra_info=
for i in 1 2 3 4 5 6 7 8
do
TZdate=$(LANG=C TZ="$TZ_for_date" date)
UTdate=$(LANG=C TZ=UTC0 date)
TZsec=$(expr "$TZdate" : '.*:\([0-5][0-9]\)')
UTsec=$(expr "$UTdate" : '.*:\([0-5][0-9]\)')
case $TZsec in
$UTsec)
extra_info="
Local time is now: $TZdate.
Universal Time is now: $UTdate."
break
esac
done
# Output TZ info and ask the user to confirm.
echo >&2 ""
echo >&2 "The following information has been given:"
echo >&2 ""
case $country+$region in
?*+?*) echo >&2 " $country$newline $region";;
?*+) echo >&2 " $country";;
+) echo >&2 " TZ='$TZ'"
esac
echo >&2 ""
echo >&2 "Therefore TZ='$TZ' will be used.$extra_info"
echo >&2 "Is the above information OK?"
ok=
select ok in Yes No
do
case $ok in
'') echo >&2 'Please enter 1 for Yes, or 2 for No.';;
?*) break
esac
done
case $ok in
'') exit 1;;
Yes) break
esac
do :
done
case $SHELL in
*csh) file=.login line="setenv TZ '$TZ'";;
*) file=.profile line="TZ='$TZ'; export TZ"
esac
echo >&2 "
You can make this change permanent for yourself by appending the line
$line
to the file '$file' in your home directory; then log out and log in again.
Here is that TZ value again, this time on standard output so that you
can use the $0 command in shell scripts:"
echo "$TZ"

File diff suppressed because it is too large Load Diff