17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
506e1a714Sraf * Common Development and Distribution License (the "License").
606e1a714Sraf * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
2106e1a714Sraf
227c478bd9Sstevel@tonic-gate /*
23*6b3dcabaSAlexander Stetsenko * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
246eaad1d3SGarrett D'Amore */
256eaad1d3SGarrett D'Amore
266eaad1d3SGarrett D'Amore /*
27b9175c69SKenjiro Tsuji * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
287c478bd9Sstevel@tonic-gate * Use is subject to license terms.
297c478bd9Sstevel@tonic-gate */
307c478bd9Sstevel@tonic-gate
317c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
327c478bd9Sstevel@tonic-gate /* All Rights Reserved */
337c478bd9Sstevel@tonic-gate
347c478bd9Sstevel@tonic-gate /*
357c478bd9Sstevel@tonic-gate * A part of this file comes from public domain source, so
367c478bd9Sstevel@tonic-gate * clarified as of June 5, 1996 by Arthur David Olson
377c478bd9Sstevel@tonic-gate * (arthur_david_olson@nih.gov).
387c478bd9Sstevel@tonic-gate */
397c478bd9Sstevel@tonic-gate
407c478bd9Sstevel@tonic-gate /*
417c478bd9Sstevel@tonic-gate * localtime.c
427c478bd9Sstevel@tonic-gate *
437c478bd9Sstevel@tonic-gate * This file contains routines to convert struct tm to time_t and
447c478bd9Sstevel@tonic-gate * back as well as adjust time values based on their timezone, which
457c478bd9Sstevel@tonic-gate * is a local offset from GMT (Greenwich Mean Time).
467c478bd9Sstevel@tonic-gate *
477c478bd9Sstevel@tonic-gate * Many timezones actually consist of more than one offset from GMT.
487c478bd9Sstevel@tonic-gate * The GMT offset that is considered the normal offset is referred
497c478bd9Sstevel@tonic-gate * to as standard time. The other offset is referred to as alternate
507c478bd9Sstevel@tonic-gate * time, but is better known as daylight savings time or summer time.
517c478bd9Sstevel@tonic-gate *
527c478bd9Sstevel@tonic-gate * The current timezone for an application is derived from the TZ
537c478bd9Sstevel@tonic-gate * environment variable either as defined in the environment or in
547c478bd9Sstevel@tonic-gate * /etc/default/init. As defined by IEEE 1003.1-1990 (POSIX), the
557c478bd9Sstevel@tonic-gate * TZ variable can either be:
567c478bd9Sstevel@tonic-gate * :<characters>
577c478bd9Sstevel@tonic-gate * or
587c478bd9Sstevel@tonic-gate * <std><offset1>[<dst>[<offset2>]][,<start>[/<time>],<end>[/<time>]
597c478bd9Sstevel@tonic-gate *
607c478bd9Sstevel@tonic-gate * <characters> is an implementation-defined string that somehow describes
617c478bd9Sstevel@tonic-gate * a timezone. The implementation-defined description of a timezone used
627c478bd9Sstevel@tonic-gate * in Solaris is based on the public domain zoneinfo code available from
637c478bd9Sstevel@tonic-gate * elsie.nci.nih.gov and a timezone that is specified in this way is
647c478bd9Sstevel@tonic-gate * referred to as a zoneinfo timezone. An example of this is ":US/Pacific".
657c478bd9Sstevel@tonic-gate *
667c478bd9Sstevel@tonic-gate * The precise definition of the second format can be found in POSIX,
677c478bd9Sstevel@tonic-gate * but, basically, <std> is the abbreviation for the timezone in standard
687c478bd9Sstevel@tonic-gate * (not daylight savings time), <offset1> is the standard offset from GMT,
697c478bd9Sstevel@tonic-gate * <dst> is the abbreviation for the timezone in daylight savings time and
707c478bd9Sstevel@tonic-gate * <offset2> is the daylight savings time offset from GMT. The remainder
717c478bd9Sstevel@tonic-gate * specifies when daylight savings time begins and ends. A timezone
727c478bd9Sstevel@tonic-gate * specified in this way is referred to as a POSIX timezone. An example
737c478bd9Sstevel@tonic-gate * of this is "PST7PDT".
747c478bd9Sstevel@tonic-gate *
757c478bd9Sstevel@tonic-gate * In Solaris, there is an extension to this. If the timezone is not
767c478bd9Sstevel@tonic-gate * preceded by a ":" and it does not parse as a POSIX timezone, then it
777c478bd9Sstevel@tonic-gate * will be treated as a zoneinfo timezone. Much usage of zoneinfo
787c478bd9Sstevel@tonic-gate * timezones in Solaris is done without the leading ":".
797c478bd9Sstevel@tonic-gate *
807c478bd9Sstevel@tonic-gate * A zoneinfo timezone is a reference to a file that contains a set of
817c478bd9Sstevel@tonic-gate * rules that describe the timezone. In Solaris, the file is in
827c478bd9Sstevel@tonic-gate * /usr/share/lib/zoneinfo. The file is generated by zic(1M), based
837c478bd9Sstevel@tonic-gate * on zoneinfo rules "source" files. This is all described on the zic(1M)
847c478bd9Sstevel@tonic-gate * man page.
857c478bd9Sstevel@tonic-gate */
867c478bd9Sstevel@tonic-gate
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate * Functions that are common to ctime(3C) and cftime(3C)
897c478bd9Sstevel@tonic-gate */
907c478bd9Sstevel@tonic-gate
917257d1b4Sraf #pragma weak _tzset = tzset
927c478bd9Sstevel@tonic-gate
937257d1b4Sraf #include "lint.h"
947c478bd9Sstevel@tonic-gate #include "libc.h"
957c478bd9Sstevel@tonic-gate #include "tsd.h"
967c478bd9Sstevel@tonic-gate #include <stdarg.h>
977c478bd9Sstevel@tonic-gate #include <mtlib.h>
987c478bd9Sstevel@tonic-gate #include <sys/types.h>
997c478bd9Sstevel@tonic-gate #include <ctype.h>
1007c478bd9Sstevel@tonic-gate #include <stdio.h>
1017c478bd9Sstevel@tonic-gate #include <limits.h>
1027c478bd9Sstevel@tonic-gate #include <sys/param.h>
1037c478bd9Sstevel@tonic-gate #include <time.h>
1047c478bd9Sstevel@tonic-gate #include <unistd.h>
1057c478bd9Sstevel@tonic-gate #include <stdlib.h>
1067c478bd9Sstevel@tonic-gate #include <string.h>
1077c478bd9Sstevel@tonic-gate #include <tzfile.h>
1087c478bd9Sstevel@tonic-gate #include <thread.h>
1097c478bd9Sstevel@tonic-gate #include <synch.h>
1107c478bd9Sstevel@tonic-gate #include <fcntl.h>
1117c478bd9Sstevel@tonic-gate #include <errno.h>
11206e1a714Sraf #include <deflt.h>
1137c478bd9Sstevel@tonic-gate #include <sys/stat.h>
114d1419d5aSNobutomo Nakano #include <sys/mman.h>
1157c478bd9Sstevel@tonic-gate
1166065583aSrobbin /* JAN_01_1902 cast to (int) - negative number of seconds from 1970 */
1176065583aSrobbin #define JAN_01_1902 (int)0x8017E880
1187c478bd9Sstevel@tonic-gate #define LEN_TZDIR (sizeof (TZDIR) - 1)
1197c478bd9Sstevel@tonic-gate #define TIMEZONE "/etc/default/init"
1207c478bd9Sstevel@tonic-gate #define TZSTRING "TZ="
121d1419d5aSNobutomo Nakano #define HASHTABLE 31
1227c478bd9Sstevel@tonic-gate
1237c478bd9Sstevel@tonic-gate #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate /* Days since 1/1/70 to 12/31/(1900 + Y - 1) */
1267c478bd9Sstevel@tonic-gate #define DAYS_SINCE_70(Y) (YR((Y)-1L) - YR(70-1))
1277c478bd9Sstevel@tonic-gate #define YR(X) /* Calc # days since 0 A.D. X = curr. yr - 1900 */ \
1287c478bd9Sstevel@tonic-gate ((1900L + (X)) * 365L + (1900L + (X)) / 4L - \
1297c478bd9Sstevel@tonic-gate (1900L + (X)) / 100L + ((1900L + (X)) - 1600L) / 400L)
1307c478bd9Sstevel@tonic-gate
1317c478bd9Sstevel@tonic-gate
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate * The following macros are replacements for detzcode(), which has
1347c478bd9Sstevel@tonic-gate * been in the public domain versions of the localtime.c code for
1357c478bd9Sstevel@tonic-gate * a long time. The primatives supporting the CVTZCODE macro are
1367c478bd9Sstevel@tonic-gate * implemented differently for different endianness (ie. little
1377c478bd9Sstevel@tonic-gate * vs. big endian) out of necessity, to account for the different
1387c478bd9Sstevel@tonic-gate * byte ordering of the quantities being fetched. Both versions
1397c478bd9Sstevel@tonic-gate * are substantially faster than the detzcode() macro. The big
1407c478bd9Sstevel@tonic-gate * endian version is approx. 6.8x faster than detzcode(), the
1417c478bd9Sstevel@tonic-gate * little endian version is approximately 3x faster, due to the
1427c478bd9Sstevel@tonic-gate * extra shifting requiring to change byte order. The micro
1437c478bd9Sstevel@tonic-gate * benchmarks used to compare were based on the SUNWSpro SC6.1
1447c478bd9Sstevel@tonic-gate * (and later) compilers.
1457c478bd9Sstevel@tonic-gate */
1467c478bd9Sstevel@tonic-gate
1477c478bd9Sstevel@tonic-gate #if defined(__sparc) || defined(__sparcv9) /* big endian */
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gate #define GET_LONG(p) \
1507c478bd9Sstevel@tonic-gate *(uint_t *)(p)
1517c478bd9Sstevel@tonic-gate
1527c478bd9Sstevel@tonic-gate #define GET_SHORTS(p) \
1537c478bd9Sstevel@tonic-gate *(ushort_t *)(p) << 16 |\
1547c478bd9Sstevel@tonic-gate *(ushort_t *)((p) + 2)
1557c478bd9Sstevel@tonic-gate
1567c478bd9Sstevel@tonic-gate #define GET_CHARS(p) \
1577c478bd9Sstevel@tonic-gate *(uchar_t *)(p) << 24 |\
1587c478bd9Sstevel@tonic-gate *(uchar_t *)((p) + 1) << 16 |\
1597c478bd9Sstevel@tonic-gate *(uchar_t *)((p) + 2) << 8 |\
1607c478bd9Sstevel@tonic-gate *(uchar_t *)((p) + 3)
1617c478bd9Sstevel@tonic-gate
1627c478bd9Sstevel@tonic-gate #else /* little endian */
1637c478bd9Sstevel@tonic-gate
1647c478bd9Sstevel@tonic-gate #define GET_BYTE(x) \
1657c478bd9Sstevel@tonic-gate ((x) & 0xff)
1667c478bd9Sstevel@tonic-gate
1677c478bd9Sstevel@tonic-gate #define SWAP_BYTES(x) ((\
1687c478bd9Sstevel@tonic-gate GET_BYTE(x) << 8) |\
1697c478bd9Sstevel@tonic-gate GET_BYTE((x) >> 8))
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate #define SWAP_WORDS(x) ((\
1727c478bd9Sstevel@tonic-gate SWAP_BYTES(x) << 16) |\
1737c478bd9Sstevel@tonic-gate SWAP_BYTES((x) >> 16))
1747c478bd9Sstevel@tonic-gate
1757c478bd9Sstevel@tonic-gate #define GET_LONG(p) \
1767c478bd9Sstevel@tonic-gate SWAP_WORDS(*(uint_t *)(p))
1777c478bd9Sstevel@tonic-gate
1787c478bd9Sstevel@tonic-gate #define GET_SHORTS(p) \
1797c478bd9Sstevel@tonic-gate SWAP_BYTES(*(ushort_t *)(p)) << 16 |\
1807c478bd9Sstevel@tonic-gate SWAP_BYTES(*(ushort_t *)((p) + 2))
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate #define GET_CHARS(p) \
1837c478bd9Sstevel@tonic-gate GET_BYTE(*(uchar_t *)(p)) << 24 |\
1847c478bd9Sstevel@tonic-gate GET_BYTE(*(uchar_t *)((p) + 1)) << 16 |\
1857c478bd9Sstevel@tonic-gate GET_BYTE(*(uchar_t *)((p) + 2)) << 8 |\
1867c478bd9Sstevel@tonic-gate GET_BYTE(*(uchar_t *)((p) + 3))
1877c478bd9Sstevel@tonic-gate
1887c478bd9Sstevel@tonic-gate #endif
1897c478bd9Sstevel@tonic-gate
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate #define IF_ALIGNED(ptr, byte_alignment) \
1927c478bd9Sstevel@tonic-gate !((uintptr_t)(ptr) & (byte_alignment - 1))
1937c478bd9Sstevel@tonic-gate
1947c478bd9Sstevel@tonic-gate #define CVTZCODE(p) (int)(\
1957c478bd9Sstevel@tonic-gate IF_ALIGNED(p, 4) ? GET_LONG(p) :\
1967c478bd9Sstevel@tonic-gate IF_ALIGNED(p, 2) ? GET_SHORTS(p) : GET_CHARS(p));\
1977c478bd9Sstevel@tonic-gate p += 4;
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate #ifndef FALSE
2007c478bd9Sstevel@tonic-gate #define FALSE (0)
2017c478bd9Sstevel@tonic-gate #endif
2027c478bd9Sstevel@tonic-gate
2037c478bd9Sstevel@tonic-gate #ifndef TRUE
2047c478bd9Sstevel@tonic-gate #define TRUE (1)
2057c478bd9Sstevel@tonic-gate #endif
2067c478bd9Sstevel@tonic-gate
2077c478bd9Sstevel@tonic-gate extern mutex_t _time_lock;
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate extern const int __lyday_to_month[];
2107c478bd9Sstevel@tonic-gate extern const int __yday_to_month[];
2117c478bd9Sstevel@tonic-gate extern const int __mon_lengths[2][MONS_PER_YEAR];
2127c478bd9Sstevel@tonic-gate extern const int __year_lengths[2];
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate const char _tz_gmt[4] = "GMT"; /* "GMT" */
2157c478bd9Sstevel@tonic-gate const char _tz_spaces[4] = " "; /* " " */
2167c478bd9Sstevel@tonic-gate static const char _posix_gmt0[5] = "GMT0"; /* "GMT0" */
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate typedef struct ttinfo { /* Time type information */
2197c478bd9Sstevel@tonic-gate long tt_gmtoff; /* GMT offset in seconds */
2207c478bd9Sstevel@tonic-gate int tt_isdst; /* used to set tm_isdst */
2217c478bd9Sstevel@tonic-gate int tt_abbrind; /* abbreviation list index */
2227c478bd9Sstevel@tonic-gate int tt_ttisstd; /* TRUE if trans is std time */
2237c478bd9Sstevel@tonic-gate int tt_ttisgmt; /* TRUE if transition is GMT */
2247c478bd9Sstevel@tonic-gate } ttinfo_t;
2257c478bd9Sstevel@tonic-gate
2267c478bd9Sstevel@tonic-gate typedef struct lsinfo { /* Leap second information */
2277c478bd9Sstevel@tonic-gate time_t ls_trans; /* transition time */
2287c478bd9Sstevel@tonic-gate long ls_corr; /* correction to apply */
2297c478bd9Sstevel@tonic-gate } lsinfo_t;
2307c478bd9Sstevel@tonic-gate
2317c478bd9Sstevel@tonic-gate typedef struct previnfo { /* Info about *prev* trans */
2327c478bd9Sstevel@tonic-gate ttinfo_t *std; /* Most recent std type */
2337c478bd9Sstevel@tonic-gate ttinfo_t *alt; /* Most recent alt type */
2347c478bd9Sstevel@tonic-gate } prev_t;
2357c478bd9Sstevel@tonic-gate
2367c478bd9Sstevel@tonic-gate typedef enum {
2377c478bd9Sstevel@tonic-gate MON_WEEK_DOW, /* Mm.n.d - month, week, day of week */
2387c478bd9Sstevel@tonic-gate JULIAN_DAY, /* Jn - Julian day */
2397c478bd9Sstevel@tonic-gate DAY_OF_YEAR /* n - day of year */
2407c478bd9Sstevel@tonic-gate } posrule_type_t;
2417c478bd9Sstevel@tonic-gate
2427c478bd9Sstevel@tonic-gate typedef struct {
2437c478bd9Sstevel@tonic-gate posrule_type_t r_type; /* type of rule */
2447c478bd9Sstevel@tonic-gate int r_day; /* day number of rule */
2457c478bd9Sstevel@tonic-gate int r_week; /* week number of rule */
2467c478bd9Sstevel@tonic-gate int r_mon; /* month number of rule */
2477c478bd9Sstevel@tonic-gate long r_time; /* transition time of rule */
2487c478bd9Sstevel@tonic-gate } rule_t;
2497c478bd9Sstevel@tonic-gate
2507c478bd9Sstevel@tonic-gate typedef struct {
2517c478bd9Sstevel@tonic-gate rule_t *rules[2];
2527c478bd9Sstevel@tonic-gate long offset[2];
2537c478bd9Sstevel@tonic-gate long long rtime[2];
2547c478bd9Sstevel@tonic-gate } posix_daylight_t;
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate /*
2577c478bd9Sstevel@tonic-gate * Note: ZONERULES_INVALID used for global curr_zonerules variable, but not
2587c478bd9Sstevel@tonic-gate * for zonerules field of state_t.
2597c478bd9Sstevel@tonic-gate */
2607c478bd9Sstevel@tonic-gate typedef enum {
2617c478bd9Sstevel@tonic-gate ZONERULES_INVALID, POSIX, POSIX_USA, ZONEINFO
2627c478bd9Sstevel@tonic-gate } zone_rules_t;
2637c478bd9Sstevel@tonic-gate
2647c478bd9Sstevel@tonic-gate /*
2657c478bd9Sstevel@tonic-gate * The following members are allocated from the libc-internal malloc:
2667c478bd9Sstevel@tonic-gate *
2677c478bd9Sstevel@tonic-gate * zonename
2687c478bd9Sstevel@tonic-gate * chars
2697c478bd9Sstevel@tonic-gate */
2707c478bd9Sstevel@tonic-gate typedef struct state {
2717c478bd9Sstevel@tonic-gate const char *zonename; /* Timezone */
2727c478bd9Sstevel@tonic-gate struct state *next; /* next state */
2737c478bd9Sstevel@tonic-gate zone_rules_t zonerules; /* Type of zone */
2747c478bd9Sstevel@tonic-gate int daylight; /* daylight global */
2757c478bd9Sstevel@tonic-gate long default_timezone; /* Def. timezone val */
2767c478bd9Sstevel@tonic-gate long default_altzone; /* Def. altzone val */
2777c478bd9Sstevel@tonic-gate const char *default_tzname0; /* Def tz..[0] val */
2787c478bd9Sstevel@tonic-gate const char *default_tzname1; /* Def tz..[1] val */
2797c478bd9Sstevel@tonic-gate int leapcnt; /* # leap sec trans */
2807c478bd9Sstevel@tonic-gate int timecnt; /* # transitions */
2817c478bd9Sstevel@tonic-gate int typecnt; /* # zone types */
2827c478bd9Sstevel@tonic-gate int charcnt; /* # zone abbv. chars */
2837c478bd9Sstevel@tonic-gate char *chars; /* Zone abbv. chars */
2847c478bd9Sstevel@tonic-gate size_t charsbuf_size; /* malloc'ed buflen */
2857c478bd9Sstevel@tonic-gate prev_t prev[TZ_MAX_TIMES]; /* Pv. trans info */
2867c478bd9Sstevel@tonic-gate time_t ats[TZ_MAX_TIMES]; /* Trans. times */
2877c478bd9Sstevel@tonic-gate uchar_t types[TZ_MAX_TIMES]; /* Type indices */
2887c478bd9Sstevel@tonic-gate ttinfo_t ttis[TZ_MAX_TYPES]; /* Zone types */
2897c478bd9Sstevel@tonic-gate lsinfo_t lsis[TZ_MAX_LEAPS]; /* Leap sec trans */
290d1419d5aSNobutomo Nakano int last_ats_idx; /* last ats index */
2917c478bd9Sstevel@tonic-gate rule_t start_rule; /* For POSIX w/rules */
2927c478bd9Sstevel@tonic-gate rule_t end_rule; /* For POSIX w/rules */
2937c478bd9Sstevel@tonic-gate } state_t;
2947c478bd9Sstevel@tonic-gate
295d1419d5aSNobutomo Nakano typedef struct tznmlist {
296d1419d5aSNobutomo Nakano struct tznmlist *link;
297d1419d5aSNobutomo Nakano char name[1];
298d1419d5aSNobutomo Nakano } tznmlist_t;
299d1419d5aSNobutomo Nakano
300d1419d5aSNobutomo Nakano static const char *systemTZ;
301d1419d5aSNobutomo Nakano static tznmlist_t *systemTZrec;
3027c478bd9Sstevel@tonic-gate
3037c478bd9Sstevel@tonic-gate static const char *namecache;
3047c478bd9Sstevel@tonic-gate
3057c478bd9Sstevel@tonic-gate static state_t *tzcache[HASHTABLE];
3067c478bd9Sstevel@tonic-gate
307d1419d5aSNobutomo Nakano #define TZNMC_SZ 43
308d1419d5aSNobutomo Nakano static tznmlist_t *tznmhash[TZNMC_SZ];
309d1419d5aSNobutomo Nakano static const char *last_tzname[2];
310d1419d5aSNobutomo Nakano
3117c478bd9Sstevel@tonic-gate static state_t *lclzonep;
3127c478bd9Sstevel@tonic-gate
3137c478bd9Sstevel@tonic-gate static struct tm tm; /* For non-reentrant use */
3147c478bd9Sstevel@tonic-gate static int is_in_dst; /* Set if t is in DST */
3157c478bd9Sstevel@tonic-gate static zone_rules_t curr_zonerules = ZONERULES_INVALID;
3167c478bd9Sstevel@tonic-gate static int cached_year; /* mktime() perf. enhancement */
3177c478bd9Sstevel@tonic-gate static long long cached_secs_since_1970; /* mktime() perf. */
3187c478bd9Sstevel@tonic-gate static int year_is_cached = FALSE; /* mktime() perf. */
3197c478bd9Sstevel@tonic-gate
320d1419d5aSNobutomo Nakano #define TZSYNC_FILE "/var/run/tzsync"
321d1419d5aSNobutomo Nakano static uint32_t zoneinfo_seqno;
322d1419d5aSNobutomo Nakano static uint32_t zoneinfo_seqno_init = 1;
323d1419d5aSNobutomo Nakano static uint32_t *zoneinfo_seqadr = &zoneinfo_seqno_init;
324d1419d5aSNobutomo Nakano #define RELOAD_INFO() (zoneinfo_seqno != *zoneinfo_seqadr)
3257c478bd9Sstevel@tonic-gate
3267c478bd9Sstevel@tonic-gate #define _2AM (2 * SECS_PER_HOUR)
3277c478bd9Sstevel@tonic-gate #define FIRSTWEEK 1
3287c478bd9Sstevel@tonic-gate #define LASTWEEK 5
3297c478bd9Sstevel@tonic-gate
3307c478bd9Sstevel@tonic-gate enum wks {
3317c478bd9Sstevel@tonic-gate _1st_week = 1,
3327c478bd9Sstevel@tonic-gate _2nd_week,
3337c478bd9Sstevel@tonic-gate _3rd_week,
3347c478bd9Sstevel@tonic-gate _4th_week,
3357c478bd9Sstevel@tonic-gate _Last_week
3367c478bd9Sstevel@tonic-gate };
3377c478bd9Sstevel@tonic-gate
3387c478bd9Sstevel@tonic-gate enum dwk {
3397c478bd9Sstevel@tonic-gate Sun,
3407c478bd9Sstevel@tonic-gate Mon,
3417c478bd9Sstevel@tonic-gate Tue,
3427c478bd9Sstevel@tonic-gate Wed,
3437c478bd9Sstevel@tonic-gate Thu,
3447c478bd9Sstevel@tonic-gate Fri,
3457c478bd9Sstevel@tonic-gate Sat
3467c478bd9Sstevel@tonic-gate };
3477c478bd9Sstevel@tonic-gate
3487c478bd9Sstevel@tonic-gate enum mth {
3497c478bd9Sstevel@tonic-gate Jan = 1,
3507c478bd9Sstevel@tonic-gate Feb,
3517c478bd9Sstevel@tonic-gate Mar,
3527c478bd9Sstevel@tonic-gate Apr,
3537c478bd9Sstevel@tonic-gate May,
3547c478bd9Sstevel@tonic-gate Jun,
3557c478bd9Sstevel@tonic-gate Jul,
3567c478bd9Sstevel@tonic-gate Aug,
3577c478bd9Sstevel@tonic-gate Sep,
3587c478bd9Sstevel@tonic-gate Oct,
3597c478bd9Sstevel@tonic-gate Nov,
3607c478bd9Sstevel@tonic-gate Dec
3617c478bd9Sstevel@tonic-gate };
3627c478bd9Sstevel@tonic-gate
3637c478bd9Sstevel@tonic-gate /*
3647c478bd9Sstevel@tonic-gate * The following table defines standard USA DST transitions
3657c478bd9Sstevel@tonic-gate * as they have been declared throughout history, disregarding
3667c478bd9Sstevel@tonic-gate * the legally sanctioned local variants.
3677c478bd9Sstevel@tonic-gate *
3687c478bd9Sstevel@tonic-gate * Note: At some point, this table may be supplanted by
3697c478bd9Sstevel@tonic-gate * more popular 'posixrules' logic.
3707c478bd9Sstevel@tonic-gate */
3717c478bd9Sstevel@tonic-gate typedef struct {
3727c478bd9Sstevel@tonic-gate int s_year;
3737c478bd9Sstevel@tonic-gate int e_year;
3747c478bd9Sstevel@tonic-gate rule_t start;
3757c478bd9Sstevel@tonic-gate rule_t end;
3767c478bd9Sstevel@tonic-gate } __usa_rules_t;
3777c478bd9Sstevel@tonic-gate
3787c478bd9Sstevel@tonic-gate static const __usa_rules_t __usa_rules[] = {
3797c478bd9Sstevel@tonic-gate {
38080868c53Srobbin 2007, 2037,
38180868c53Srobbin { MON_WEEK_DOW, Sun, _2nd_week, Mar, _2AM },
38280868c53Srobbin { MON_WEEK_DOW, Sun, _1st_week, Nov, _2AM },
38380868c53Srobbin },
38480868c53Srobbin {
38580868c53Srobbin 1987, 2006,
3867c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _1st_week, Apr, _2AM },
3877c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
3887c478bd9Sstevel@tonic-gate },
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 1976, 1986,
3917c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
3927c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
3937c478bd9Sstevel@tonic-gate },
3947c478bd9Sstevel@tonic-gate {
3957c478bd9Sstevel@tonic-gate 1975, 1975,
3967c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Feb, _2AM },
3977c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
3987c478bd9Sstevel@tonic-gate },
3997c478bd9Sstevel@tonic-gate
4007c478bd9Sstevel@tonic-gate {
4017c478bd9Sstevel@tonic-gate 1974, 1974,
4027c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _1st_week, Jan, _2AM },
4037c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Nov, _2AM },
4047c478bd9Sstevel@tonic-gate },
4057c478bd9Sstevel@tonic-gate /*
4067c478bd9Sstevel@tonic-gate * The entry below combines two previously separate entries for
4077c478bd9Sstevel@tonic-gate * 1969-1973 and 1902-1968
4087c478bd9Sstevel@tonic-gate */
4097c478bd9Sstevel@tonic-gate {
4107c478bd9Sstevel@tonic-gate 1902, 1973,
4117c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Apr, _2AM },
4127c478bd9Sstevel@tonic-gate { MON_WEEK_DOW, Sun, _Last_week, Oct, _2AM },
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate };
4157c478bd9Sstevel@tonic-gate #define MAX_RULE_TABLE (sizeof (__usa_rules) / sizeof (__usa_rules_t) - 1)
4167c478bd9Sstevel@tonic-gate
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate * Prototypes for static functions.
4197c478bd9Sstevel@tonic-gate */
420d1419d5aSNobutomo Nakano static const char *getsystemTZ(void);
4217c478bd9Sstevel@tonic-gate static const char *getzname(const char *, int);
4227c478bd9Sstevel@tonic-gate static const char *getnum(const char *, int *, int, int);
4237c478bd9Sstevel@tonic-gate static const char *getsecs(const char *, long *);
4247c478bd9Sstevel@tonic-gate static const char *getoffset(const char *, long *);
4257c478bd9Sstevel@tonic-gate static const char *getrule(const char *, rule_t *, int);
4267c478bd9Sstevel@tonic-gate static int load_posixinfo(const char *, state_t *);
4277c478bd9Sstevel@tonic-gate static int load_zoneinfo(const char *, state_t *);
428d1419d5aSNobutomo Nakano static void load_posix_transitions(state_t *, long, long, zone_rules_t);
429d1419d5aSNobutomo Nakano static void adjust_posix_default(state_t *, long, long);
430d1419d5aSNobutomo Nakano static void *ltzset_u(time_t);
4317c478bd9Sstevel@tonic-gate static struct tm *offtime_u(time_t, long, struct tm *);
4327c478bd9Sstevel@tonic-gate static int posix_check_dst(long long, state_t *);
4337c478bd9Sstevel@tonic-gate static int posix_daylight(long long *, int, posix_daylight_t *);
4347c478bd9Sstevel@tonic-gate static void set_zone_context(time_t);
435d1419d5aSNobutomo Nakano static void reload_counter(void);
436d1419d5aSNobutomo Nakano static void purge_zone_cache(void);
437d1419d5aSNobutomo Nakano static void set_tzname(const char **);
4387c478bd9Sstevel@tonic-gate
4397c478bd9Sstevel@tonic-gate /*
4407c478bd9Sstevel@tonic-gate * definition of difftime
4417c478bd9Sstevel@tonic-gate *
4427c478bd9Sstevel@tonic-gate * This code assumes time_t is type long. Note the difference of two
4437c478bd9Sstevel@tonic-gate * longs in absolute value is representable as an unsigned long. So,
4447c478bd9Sstevel@tonic-gate * compute the absolute value of the difference, cast the result to
4457c478bd9Sstevel@tonic-gate * double and attach the sign back on.
4467c478bd9Sstevel@tonic-gate *
4477c478bd9Sstevel@tonic-gate * Note this code assumes 2's complement arithmetic. The subtraction
4487c478bd9Sstevel@tonic-gate * operation may overflow when using signed operands, but when the
4497c478bd9Sstevel@tonic-gate * result is cast to unsigned long, it yields the desired value
4507c478bd9Sstevel@tonic-gate * (ie, the absolute value of the difference). The cast to unsigned
4517c478bd9Sstevel@tonic-gate * long is done using pointers to avoid undefined behavior if casting
4527c478bd9Sstevel@tonic-gate * a negative value to unsigned.
4537c478bd9Sstevel@tonic-gate */
4547c478bd9Sstevel@tonic-gate double
difftime(time_t time1,time_t time0)4557c478bd9Sstevel@tonic-gate difftime(time_t time1, time_t time0)
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate if (time1 < time0) {
4587c478bd9Sstevel@tonic-gate time0 -= time1;
4597c478bd9Sstevel@tonic-gate return (-(double)*(unsigned long *) &time0);
4607c478bd9Sstevel@tonic-gate } else {
4617c478bd9Sstevel@tonic-gate time1 -= time0;
4627c478bd9Sstevel@tonic-gate return ((double)*(unsigned long *) &time1);
4637c478bd9Sstevel@tonic-gate }
4647c478bd9Sstevel@tonic-gate }
4657c478bd9Sstevel@tonic-gate
4667c478bd9Sstevel@tonic-gate /*
4677c478bd9Sstevel@tonic-gate * Accepts a time_t, returns a tm struct based on it, with
4687c478bd9Sstevel@tonic-gate * no local timezone adjustment.
4697c478bd9Sstevel@tonic-gate *
4707c478bd9Sstevel@tonic-gate * This routine is the thread-safe variant of gmtime(), and
4717c478bd9Sstevel@tonic-gate * requires that the call provide the address of their own tm
4727c478bd9Sstevel@tonic-gate * struct.
4737c478bd9Sstevel@tonic-gate *
4747c478bd9Sstevel@tonic-gate * Locking is not done here because set_zone_context()
4757c478bd9Sstevel@tonic-gate * is not called, thus timezone, altzone, and tzname[] are not
4767c478bd9Sstevel@tonic-gate * accessed, no memory is allocated, and no common dynamic
4777c478bd9Sstevel@tonic-gate * data is accessed.
4787c478bd9Sstevel@tonic-gate *
4797c478bd9Sstevel@tonic-gate * See ctime(3C)
4807c478bd9Sstevel@tonic-gate */
4817c478bd9Sstevel@tonic-gate struct tm *
gmtime_r(const time_t * timep,struct tm * p_tm)4827257d1b4Sraf gmtime_r(const time_t *timep, struct tm *p_tm)
4837c478bd9Sstevel@tonic-gate {
4847c478bd9Sstevel@tonic-gate return (offtime_u((time_t)*timep, 0L, p_tm));
4857c478bd9Sstevel@tonic-gate }
4867c478bd9Sstevel@tonic-gate
4877c478bd9Sstevel@tonic-gate /*
4887c478bd9Sstevel@tonic-gate * Accepts a time_t, returns a tm struct based on it, with
4897c478bd9Sstevel@tonic-gate * no local timezone adjustment.
4907c478bd9Sstevel@tonic-gate *
4917c478bd9Sstevel@tonic-gate * This function is explicitly NOT THREAD-SAFE. The standards
4927c478bd9Sstevel@tonic-gate * indicate it should provide its results in its own statically
4937c478bd9Sstevel@tonic-gate * allocated tm struct that gets overwritten. The thread-safe
4947c478bd9Sstevel@tonic-gate * variant is gmtime_r(). We make it mostly thread-safe by
4957c478bd9Sstevel@tonic-gate * allocating its buffer in thread-specific data.
4967c478bd9Sstevel@tonic-gate *
4977c478bd9Sstevel@tonic-gate * See ctime(3C)
4987c478bd9Sstevel@tonic-gate */
4997c478bd9Sstevel@tonic-gate struct tm *
gmtime(const time_t * timep)5007c478bd9Sstevel@tonic-gate gmtime(const time_t *timep)
5017c478bd9Sstevel@tonic-gate {
5027c478bd9Sstevel@tonic-gate struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
5037c478bd9Sstevel@tonic-gate
5047c478bd9Sstevel@tonic-gate if (p_tm == NULL) /* memory allocation failure */
5057c478bd9Sstevel@tonic-gate p_tm = &tm; /* use static buffer and hope for the best */
5067257d1b4Sraf return (gmtime_r(timep, p_tm));
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate
5097c478bd9Sstevel@tonic-gate /*
5107c478bd9Sstevel@tonic-gate * This is the hashing function, based on the input timezone name.
5117c478bd9Sstevel@tonic-gate */
5127c478bd9Sstevel@tonic-gate static int
get_hashid(const char * id)5137c478bd9Sstevel@tonic-gate get_hashid(const char *id)
5147c478bd9Sstevel@tonic-gate {
5157c478bd9Sstevel@tonic-gate unsigned char c;
5167c478bd9Sstevel@tonic-gate unsigned int h;
5177c478bd9Sstevel@tonic-gate
518d1419d5aSNobutomo Nakano h = *id++;
519d1419d5aSNobutomo Nakano while ((c = *id++) != '\0')
520d1419d5aSNobutomo Nakano h += c;
5217c478bd9Sstevel@tonic-gate return ((int)(h % HASHTABLE));
5227c478bd9Sstevel@tonic-gate }
5237c478bd9Sstevel@tonic-gate
5247c478bd9Sstevel@tonic-gate /*
5257c478bd9Sstevel@tonic-gate * find_zone() gets the hashid for zonename, then uses the hashid
5267c478bd9Sstevel@tonic-gate * to search the hash table for the appropriate timezone entry. If
5277c478bd9Sstevel@tonic-gate * the entry for zonename is found in the hash table, return a pointer
528d1419d5aSNobutomo Nakano * to the entry.
5297c478bd9Sstevel@tonic-gate */
5307c478bd9Sstevel@tonic-gate static state_t *
find_zone(const char * zonename)531d1419d5aSNobutomo Nakano find_zone(const char *zonename)
5327c478bd9Sstevel@tonic-gate {
5337c478bd9Sstevel@tonic-gate int hashid;
534d1419d5aSNobutomo Nakano state_t *cur;
5357c478bd9Sstevel@tonic-gate
5367c478bd9Sstevel@tonic-gate hashid = get_hashid(zonename);
5377c478bd9Sstevel@tonic-gate cur = tzcache[hashid];
5387c478bd9Sstevel@tonic-gate while (cur) {
5397c478bd9Sstevel@tonic-gate int res;
5407c478bd9Sstevel@tonic-gate res = strcmp(cur->zonename, zonename);
5417c478bd9Sstevel@tonic-gate if (res == 0) {
5427c478bd9Sstevel@tonic-gate return (cur);
5437c478bd9Sstevel@tonic-gate } else if (res > 0) {
5447c478bd9Sstevel@tonic-gate break;
5457c478bd9Sstevel@tonic-gate }
5467c478bd9Sstevel@tonic-gate cur = cur->next;
5477c478bd9Sstevel@tonic-gate }
5487c478bd9Sstevel@tonic-gate return (NULL);
5497c478bd9Sstevel@tonic-gate }
5507c478bd9Sstevel@tonic-gate
551d1419d5aSNobutomo Nakano /*
552d1419d5aSNobutomo Nakano * Register new state in the cache.
553d1419d5aSNobutomo Nakano */
554d1419d5aSNobutomo Nakano static void
reg_zone(state_t * new)555d1419d5aSNobutomo Nakano reg_zone(state_t *new)
556d1419d5aSNobutomo Nakano {
557d1419d5aSNobutomo Nakano int hashid, res;
558d1419d5aSNobutomo Nakano state_t *cur, *prv;
559d1419d5aSNobutomo Nakano
560d1419d5aSNobutomo Nakano hashid = get_hashid(new->zonename);
561d1419d5aSNobutomo Nakano cur = tzcache[hashid];
562d1419d5aSNobutomo Nakano prv = NULL;
563d1419d5aSNobutomo Nakano while (cur != NULL) {
564d1419d5aSNobutomo Nakano res = strcmp(cur->zonename, new->zonename);
565d1419d5aSNobutomo Nakano if (res == 0) {
566d1419d5aSNobutomo Nakano /* impossible, but just in case */
567d1419d5aSNobutomo Nakano return;
568d1419d5aSNobutomo Nakano } else if (res > 0) {
569d1419d5aSNobutomo Nakano break;
570d1419d5aSNobutomo Nakano }
571d1419d5aSNobutomo Nakano prv = cur;
572d1419d5aSNobutomo Nakano cur = cur->next;
573d1419d5aSNobutomo Nakano }
574d1419d5aSNobutomo Nakano if (prv != NULL) {
575d1419d5aSNobutomo Nakano new->next = prv->next;
576d1419d5aSNobutomo Nakano prv->next = new;
577d1419d5aSNobutomo Nakano } else {
578d1419d5aSNobutomo Nakano new->next = tzcache[hashid];
579d1419d5aSNobutomo Nakano tzcache[hashid] = new;
580d1419d5aSNobutomo Nakano }
581d1419d5aSNobutomo Nakano }
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate * Returns tm struct based on input time_t argument, correcting
5857c478bd9Sstevel@tonic-gate * for the local timezone, producing documented side-effects
5867c478bd9Sstevel@tonic-gate * to extern global state, timezone, altzone, daylight and tzname[].
5877c478bd9Sstevel@tonic-gate *
5887c478bd9Sstevel@tonic-gate * localtime_r() is the thread-safe variant of localtime().
5897c478bd9Sstevel@tonic-gate *
5907c478bd9Sstevel@tonic-gate * IMPLEMENTATION NOTE:
5917c478bd9Sstevel@tonic-gate *
5927c478bd9Sstevel@tonic-gate * Locking slows multithreaded access and is probably ultimately
5937c478bd9Sstevel@tonic-gate * unnecessary here. The POSIX specification is a bit vague
5947c478bd9Sstevel@tonic-gate * as to whether the extern variables set by tzset() need to
5957c478bd9Sstevel@tonic-gate * set as a result of a call to localtime_r()
5967c478bd9Sstevel@tonic-gate *
5977c478bd9Sstevel@tonic-gate * Currently, the spec only mentions that tzname[] doesn't
5987c478bd9Sstevel@tonic-gate * need to be set. As soon as it becomes unequivocal
5997c478bd9Sstevel@tonic-gate * that the external zone state doesn't need to be asserted
6007c478bd9Sstevel@tonic-gate * for this call, and it really doesn't make much sense
6017c478bd9Sstevel@tonic-gate * to set common state from multi-threaded calls made to this
6027c478bd9Sstevel@tonic-gate * function, locking can be dispensed with here.
6037c478bd9Sstevel@tonic-gate *
6047c478bd9Sstevel@tonic-gate * local zone state would still need to be aquired for the
6057c478bd9Sstevel@tonic-gate * time in question in order for calculations elicited here
6067c478bd9Sstevel@tonic-gate * to be correct, but that state wouldn't need to be shared,
6077c478bd9Sstevel@tonic-gate * thus no multi-threaded synchronization would be required.
6087c478bd9Sstevel@tonic-gate *
6097c478bd9Sstevel@tonic-gate * It would be nice if POSIX would approve an ltzset_r()
6107c478bd9Sstevel@tonic-gate * function, but if not, it wouldn't stop us from making one
6117c478bd9Sstevel@tonic-gate * privately.
6127c478bd9Sstevel@tonic-gate *
6137c478bd9Sstevel@tonic-gate * localtime_r() can now return NULL if overflow is detected.
6147c478bd9Sstevel@tonic-gate * offtime_u() is the function that detects overflow, and sets
6157c478bd9Sstevel@tonic-gate * errno appropriately. We unlock before the call to offtime_u(),
6167c478bd9Sstevel@tonic-gate * so that lmutex_unlock() does not reassign errno. The function
6177c478bd9Sstevel@tonic-gate * offtime_u() is MT-safe and does not have to be locked. Use
6187c478bd9Sstevel@tonic-gate * my_is_in_dst to reference local copy of is_in_dst outside locks.
6197c478bd9Sstevel@tonic-gate *
6207c478bd9Sstevel@tonic-gate * See ctime(3C)
6217c478bd9Sstevel@tonic-gate */
6227c478bd9Sstevel@tonic-gate struct tm *
localtime_r(const time_t * timep,struct tm * p_tm)6237257d1b4Sraf localtime_r(const time_t *timep, struct tm *p_tm)
6247c478bd9Sstevel@tonic-gate {
6257c478bd9Sstevel@tonic-gate long offset;
6267c478bd9Sstevel@tonic-gate struct tm *rt;
627d1419d5aSNobutomo Nakano void *unused;
6287c478bd9Sstevel@tonic-gate int my_is_in_dst;
6297c478bd9Sstevel@tonic-gate
6307c478bd9Sstevel@tonic-gate lmutex_lock(&_time_lock);
631d1419d5aSNobutomo Nakano unused = ltzset_u(*timep);
6327c478bd9Sstevel@tonic-gate if (lclzonep == NULL) {
6337c478bd9Sstevel@tonic-gate lmutex_unlock(&_time_lock);
634d1419d5aSNobutomo Nakano if (unused != NULL)
635d1419d5aSNobutomo Nakano free(unused);
6367c478bd9Sstevel@tonic-gate return (offtime_u(*timep, 0L, p_tm));
6377c478bd9Sstevel@tonic-gate }
6387c478bd9Sstevel@tonic-gate my_is_in_dst = is_in_dst;
6397c478bd9Sstevel@tonic-gate offset = (my_is_in_dst) ? -altzone : -timezone;
6407c478bd9Sstevel@tonic-gate lmutex_unlock(&_time_lock);
641d1419d5aSNobutomo Nakano if (unused != NULL)
642d1419d5aSNobutomo Nakano free(unused);
6437c478bd9Sstevel@tonic-gate rt = offtime_u(*timep, offset, p_tm);
6447c478bd9Sstevel@tonic-gate p_tm->tm_isdst = my_is_in_dst;
6457c478bd9Sstevel@tonic-gate return (rt);
6467c478bd9Sstevel@tonic-gate }
6477c478bd9Sstevel@tonic-gate
6487c478bd9Sstevel@tonic-gate /*
6497c478bd9Sstevel@tonic-gate * Accepts a time_t, returns a tm struct based on it, correcting
6507c478bd9Sstevel@tonic-gate * for the local timezone. Produces documented side-effects to
6517c478bd9Sstevel@tonic-gate * extern global timezone state data.
6527c478bd9Sstevel@tonic-gate *
6537c478bd9Sstevel@tonic-gate * This function is explicitly NOT THREAD-SAFE. The standards
6547c478bd9Sstevel@tonic-gate * indicate it should provide its results in its own statically
6557c478bd9Sstevel@tonic-gate * allocated tm struct that gets overwritten. The thread-safe
6567c478bd9Sstevel@tonic-gate * variant is localtime_r(). We make it mostly thread-safe by
6577c478bd9Sstevel@tonic-gate * allocating its buffer in thread-specific data.
6587c478bd9Sstevel@tonic-gate *
6597c478bd9Sstevel@tonic-gate * localtime() can now return NULL if overflow is detected.
6607c478bd9Sstevel@tonic-gate * offtime_u() is the function that detects overflow, and sets
6617c478bd9Sstevel@tonic-gate * errno appropriately.
6627c478bd9Sstevel@tonic-gate *
6637c478bd9Sstevel@tonic-gate * See ctime(3C)
6647c478bd9Sstevel@tonic-gate */
6657c478bd9Sstevel@tonic-gate struct tm *
localtime(const time_t * timep)6667c478bd9Sstevel@tonic-gate localtime(const time_t *timep)
6677c478bd9Sstevel@tonic-gate {
6687c478bd9Sstevel@tonic-gate struct tm *p_tm = tsdalloc(_T_STRUCT_TM, sizeof (struct tm), NULL);
6697c478bd9Sstevel@tonic-gate
6707c478bd9Sstevel@tonic-gate if (p_tm == NULL) /* memory allocation failure */
6717c478bd9Sstevel@tonic-gate p_tm = &tm; /* use static buffer and hope for the best */
6727257d1b4Sraf return (localtime_r(timep, p_tm));
6737c478bd9Sstevel@tonic-gate }
6747c478bd9Sstevel@tonic-gate
6757c478bd9Sstevel@tonic-gate /*
6767c478bd9Sstevel@tonic-gate * This function takes a pointer to a tm struct and returns a
6777c478bd9Sstevel@tonic-gate * normalized time_t, also inducing documented side-effects in
6787c478bd9Sstevel@tonic-gate * extern global zone state variables. (See mktime(3C)).
6797c478bd9Sstevel@tonic-gate */
6806eaad1d3SGarrett D'Amore static time_t
mktime1(struct tm * tmptr,int usetz)6816eaad1d3SGarrett D'Amore mktime1(struct tm *tmptr, int usetz)
6827c478bd9Sstevel@tonic-gate {
6837c478bd9Sstevel@tonic-gate struct tm _tm;
6847c478bd9Sstevel@tonic-gate long long t; /* must hold more than 32-bit time_t */
6857c478bd9Sstevel@tonic-gate int temp;
6867c478bd9Sstevel@tonic-gate int mketimerrno;
6877c478bd9Sstevel@tonic-gate int overflow;
688*6b3dcabaSAlexander Stetsenko void *unused = NULL;
6897c478bd9Sstevel@tonic-gate
6907c478bd9Sstevel@tonic-gate mketimerrno = errno;
6917c478bd9Sstevel@tonic-gate
6927c478bd9Sstevel@tonic-gate /* mktime leaves errno unchanged if no error is encountered */
6937c478bd9Sstevel@tonic-gate
6947c478bd9Sstevel@tonic-gate /* Calculate time_t from tm arg. tm may need to be normalized. */
6957c478bd9Sstevel@tonic-gate t = tmptr->tm_sec + SECSPERMIN * tmptr->tm_min +
6967c478bd9Sstevel@tonic-gate SECSPERHOUR * tmptr->tm_hour +
6977c478bd9Sstevel@tonic-gate SECSPERDAY * (tmptr->tm_mday - 1);
6987c478bd9Sstevel@tonic-gate
6997c478bd9Sstevel@tonic-gate if (tmptr->tm_mon >= 12) {
7007c478bd9Sstevel@tonic-gate tmptr->tm_year += tmptr->tm_mon / 12;
7017c478bd9Sstevel@tonic-gate tmptr->tm_mon %= 12;
7027c478bd9Sstevel@tonic-gate } else if (tmptr->tm_mon < 0) {
7037c478bd9Sstevel@tonic-gate temp = -tmptr->tm_mon;
7047c478bd9Sstevel@tonic-gate tmptr->tm_mon = 0; /* If tm_mon divides by 12. */
7057c478bd9Sstevel@tonic-gate tmptr->tm_year -= (temp / 12);
7067c478bd9Sstevel@tonic-gate if (temp %= 12) { /* Remainder... */
7077c478bd9Sstevel@tonic-gate tmptr->tm_year--;
7087c478bd9Sstevel@tonic-gate tmptr->tm_mon = 12 - temp;
7097c478bd9Sstevel@tonic-gate }
7107c478bd9Sstevel@tonic-gate }
7117c478bd9Sstevel@tonic-gate
712d1419d5aSNobutomo Nakano lmutex_lock(&_time_lock);
713d1419d5aSNobutomo Nakano
7147c478bd9Sstevel@tonic-gate /* Avoid numerous calculations embedded in macro if possible */
7157c478bd9Sstevel@tonic-gate if (!year_is_cached || (cached_year != tmptr->tm_year)) {
7167c478bd9Sstevel@tonic-gate cached_year = tmptr->tm_year;
7177c478bd9Sstevel@tonic-gate year_is_cached = TRUE;
7187c478bd9Sstevel@tonic-gate /* For boundry values of tm_year, typecasting required */
7197c478bd9Sstevel@tonic-gate cached_secs_since_1970 =
7207c478bd9Sstevel@tonic-gate (long long)SECSPERDAY * DAYS_SINCE_70(cached_year);
7217c478bd9Sstevel@tonic-gate }
7227c478bd9Sstevel@tonic-gate t += cached_secs_since_1970;
7237c478bd9Sstevel@tonic-gate
7247c478bd9Sstevel@tonic-gate if (isleap(tmptr->tm_year + TM_YEAR_BASE))
7257c478bd9Sstevel@tonic-gate t += SECSPERDAY * __lyday_to_month[tmptr->tm_mon];
7267c478bd9Sstevel@tonic-gate else
7277c478bd9Sstevel@tonic-gate t += SECSPERDAY * __yday_to_month[tmptr->tm_mon];
7287c478bd9Sstevel@tonic-gate
7296eaad1d3SGarrett D'Amore
7306eaad1d3SGarrett D'Amore if (usetz) {
7316eaad1d3SGarrett D'Amore /*
7326eaad1d3SGarrett D'Amore * If called from mktime(), then we need to do the TZ
7336eaad1d3SGarrett D'Amore * related transformations.
7346eaad1d3SGarrett D'Amore */
7356eaad1d3SGarrett D'Amore
736d1419d5aSNobutomo Nakano unused = ltzset_u((time_t)t);
737d1419d5aSNobutomo Nakano
7387c478bd9Sstevel@tonic-gate /* Attempt to convert time to GMT based on tm_isdst setting */
7397c478bd9Sstevel@tonic-gate t += (tmptr->tm_isdst > 0) ? altzone : timezone;
7407c478bd9Sstevel@tonic-gate
7417c478bd9Sstevel@tonic-gate #ifdef _ILP32
7427c478bd9Sstevel@tonic-gate overflow = t > LONG_MAX || t < LONG_MIN ||
7437c478bd9Sstevel@tonic-gate tmptr->tm_year < 1 || tmptr->tm_year > 138;
7447c478bd9Sstevel@tonic-gate #else
7457c478bd9Sstevel@tonic-gate overflow = t > LONG_MAX || t < LONG_MIN;
7467c478bd9Sstevel@tonic-gate #endif
7477c478bd9Sstevel@tonic-gate set_zone_context((time_t)t);
7487c478bd9Sstevel@tonic-gate if (tmptr->tm_isdst < 0) {
7497c478bd9Sstevel@tonic-gate long dst_delta = timezone - altzone;
7507c478bd9Sstevel@tonic-gate switch (curr_zonerules) {
7517c478bd9Sstevel@tonic-gate case ZONEINFO:
7527c478bd9Sstevel@tonic-gate if (is_in_dst) {
7537c478bd9Sstevel@tonic-gate t -= dst_delta;
7547c478bd9Sstevel@tonic-gate set_zone_context((time_t)t);
7557c478bd9Sstevel@tonic-gate if (is_in_dst) {
7567c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t,
7577c478bd9Sstevel@tonic-gate -altzone, &_tm);
7587c478bd9Sstevel@tonic-gate _tm.tm_isdst = 1;
7597c478bd9Sstevel@tonic-gate } else {
7607c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t,
7617c478bd9Sstevel@tonic-gate -timezone, &_tm);
7627c478bd9Sstevel@tonic-gate }
7637c478bd9Sstevel@tonic-gate } else {
7646eaad1d3SGarrett D'Amore (void) offtime_u((time_t)t, -timezone,
7656eaad1d3SGarrett D'Amore &_tm);
7667c478bd9Sstevel@tonic-gate }
7677c478bd9Sstevel@tonic-gate break;
7687c478bd9Sstevel@tonic-gate case POSIX_USA:
7697c478bd9Sstevel@tonic-gate case POSIX:
7707c478bd9Sstevel@tonic-gate if (is_in_dst) {
7717c478bd9Sstevel@tonic-gate t -= dst_delta;
7727c478bd9Sstevel@tonic-gate set_zone_context((time_t)t);
7737c478bd9Sstevel@tonic-gate if (is_in_dst) {
7747c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t,
7757c478bd9Sstevel@tonic-gate -altzone, &_tm);
7767c478bd9Sstevel@tonic-gate _tm.tm_isdst = 1;
7777c478bd9Sstevel@tonic-gate } else {
7787c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t,
7797c478bd9Sstevel@tonic-gate -timezone, &_tm);
7807c478bd9Sstevel@tonic-gate }
7816eaad1d3SGarrett D'Amore } else {
7826eaad1d3SGarrett D'Amore /*
7836eaad1d3SGarrett D'Amore * check for ambiguous
7846eaad1d3SGarrett D'Amore * 'fallback' transition
7856eaad1d3SGarrett D'Amore */
7867c478bd9Sstevel@tonic-gate set_zone_context((time_t)t - dst_delta);
7876eaad1d3SGarrett D'Amore if (is_in_dst) {
7886eaad1d3SGarrett D'Amore /* In fallback, force DST */
7897c478bd9Sstevel@tonic-gate t -= dst_delta;
7907c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t,
7917c478bd9Sstevel@tonic-gate -altzone, &_tm);
7927c478bd9Sstevel@tonic-gate _tm.tm_isdst = 1;
7937c478bd9Sstevel@tonic-gate } else {
7947c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t,
7957c478bd9Sstevel@tonic-gate -timezone, &_tm);
7967c478bd9Sstevel@tonic-gate }
7977c478bd9Sstevel@tonic-gate }
7987c478bd9Sstevel@tonic-gate break;
7997c478bd9Sstevel@tonic-gate
8007c478bd9Sstevel@tonic-gate case ZONERULES_INVALID:
8017c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t, 0L, &_tm);
8027c478bd9Sstevel@tonic-gate break;
8037c478bd9Sstevel@tonic-gate
8047c478bd9Sstevel@tonic-gate }
8057c478bd9Sstevel@tonic-gate } else if (is_in_dst) {
8067c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t, -altzone, &_tm);
8077c478bd9Sstevel@tonic-gate _tm.tm_isdst = 1;
8087c478bd9Sstevel@tonic-gate } else {
8097c478bd9Sstevel@tonic-gate (void) offtime_u((time_t)t, -timezone, &_tm);
8107c478bd9Sstevel@tonic-gate }
8117c478bd9Sstevel@tonic-gate
8126eaad1d3SGarrett D'Amore } else { /* !usetz, i.e. using UTC */
8136eaad1d3SGarrett D'Amore overflow = 0;
8146eaad1d3SGarrett D'Amore /* Normalize the TM structure */
8156eaad1d3SGarrett D'Amore (void) offtime_u((time_t)t, 0, &_tm);
8166eaad1d3SGarrett D'Amore }
8176eaad1d3SGarrett D'Amore
8187c478bd9Sstevel@tonic-gate if (overflow || t > LONG_MAX || t < LONG_MIN) {
8197c478bd9Sstevel@tonic-gate mketimerrno = EOVERFLOW;
8207c478bd9Sstevel@tonic-gate t = -1;
8217c478bd9Sstevel@tonic-gate } else {
8227c478bd9Sstevel@tonic-gate *tmptr = _tm;
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate
8257c478bd9Sstevel@tonic-gate lmutex_unlock(&_time_lock);
826d1419d5aSNobutomo Nakano if (unused != NULL)
827d1419d5aSNobutomo Nakano free(unused);
8287c478bd9Sstevel@tonic-gate
8297c478bd9Sstevel@tonic-gate errno = mketimerrno;
8307c478bd9Sstevel@tonic-gate return ((time_t)t);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate
8336eaad1d3SGarrett D'Amore time_t
mktime(struct tm * tmptr)8346eaad1d3SGarrett D'Amore mktime(struct tm *tmptr)
8356eaad1d3SGarrett D'Amore {
8366eaad1d3SGarrett D'Amore return (mktime1(tmptr, TRUE));
8376eaad1d3SGarrett D'Amore }
8386eaad1d3SGarrett D'Amore
8396eaad1d3SGarrett D'Amore time_t
timegm(struct tm * tmptr)8406eaad1d3SGarrett D'Amore timegm(struct tm *tmptr)
8416eaad1d3SGarrett D'Amore {
8426eaad1d3SGarrett D'Amore return (mktime1(tmptr, FALSE));
8436eaad1d3SGarrett D'Amore }
8446eaad1d3SGarrett D'Amore
8456eaad1d3SGarrett D'Amore
8467c478bd9Sstevel@tonic-gate /*
8477c478bd9Sstevel@tonic-gate * Sets extern global zone state variables based on the current
8487c478bd9Sstevel@tonic-gate * time. Specifically, tzname[], timezone, altzone, and daylight
8497c478bd9Sstevel@tonic-gate * are updated. See ctime(3C) manpage.
8507c478bd9Sstevel@tonic-gate */
8517c478bd9Sstevel@tonic-gate void
tzset(void)8527257d1b4Sraf tzset(void)
8537c478bd9Sstevel@tonic-gate {
854d1419d5aSNobutomo Nakano void *unused;
8557c478bd9Sstevel@tonic-gate
8567c478bd9Sstevel@tonic-gate lmutex_lock(&_time_lock);
857d1419d5aSNobutomo Nakano unused = ltzset_u(time(NULL));
8587c478bd9Sstevel@tonic-gate lmutex_unlock(&_time_lock);
859d1419d5aSNobutomo Nakano if (unused != NULL)
860d1419d5aSNobutomo Nakano free(unused);
8617c478bd9Sstevel@tonic-gate }
8627c478bd9Sstevel@tonic-gate
8637c478bd9Sstevel@tonic-gate void
_ltzset(time_t tim)8647c478bd9Sstevel@tonic-gate _ltzset(time_t tim)
8657c478bd9Sstevel@tonic-gate {
866d1419d5aSNobutomo Nakano void *unused;
8677c478bd9Sstevel@tonic-gate
8687c478bd9Sstevel@tonic-gate lmutex_lock(&_time_lock);
869d1419d5aSNobutomo Nakano unused = ltzset_u(tim);
8707c478bd9Sstevel@tonic-gate lmutex_unlock(&_time_lock);
871d1419d5aSNobutomo Nakano if (unused != NULL)
872d1419d5aSNobutomo Nakano free(unused);
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate
8757c478bd9Sstevel@tonic-gate /*
8767c478bd9Sstevel@tonic-gate * Loads local zone information if TZ changed since last time zone
8777c478bd9Sstevel@tonic-gate * information was loaded, or if this is the first time thru.
8787c478bd9Sstevel@tonic-gate * We already hold _time_lock; no further locking is required.
879d1419d5aSNobutomo Nakano * Return a memory block which can be free'd at safe place.
8807c478bd9Sstevel@tonic-gate */
881d1419d5aSNobutomo Nakano static void *
ltzset_u(time_t t)882d1419d5aSNobutomo Nakano ltzset_u(time_t t)
8837c478bd9Sstevel@tonic-gate {
884d1419d5aSNobutomo Nakano const char *zonename;
885d1419d5aSNobutomo Nakano state_t *entry, *new_entry;
886d1419d5aSNobutomo Nakano const char *newtzname[2];
8877c478bd9Sstevel@tonic-gate
888d1419d5aSNobutomo Nakano if (RELOAD_INFO()) {
889d1419d5aSNobutomo Nakano reload_counter();
890d1419d5aSNobutomo Nakano purge_zone_cache();
8917c478bd9Sstevel@tonic-gate }
8927c478bd9Sstevel@tonic-gate
893d1419d5aSNobutomo Nakano if ((zonename = getsystemTZ()) == NULL || *zonename == '\0')
894d1419d5aSNobutomo Nakano zonename = _posix_gmt0;
895d1419d5aSNobutomo Nakano
896d1419d5aSNobutomo Nakano if (namecache != NULL && strcmp(namecache, zonename) == 0) {
897d1419d5aSNobutomo Nakano set_zone_context(t);
898d1419d5aSNobutomo Nakano return (NULL);
899d1419d5aSNobutomo Nakano }
900d1419d5aSNobutomo Nakano
901d1419d5aSNobutomo Nakano entry = find_zone(zonename);
9027c478bd9Sstevel@tonic-gate if (entry == NULL) {
9037c478bd9Sstevel@tonic-gate /*
904d1419d5aSNobutomo Nakano * We need to release _time_lock to call out malloc().
905d1419d5aSNobutomo Nakano * We can release _time_lock as far as global variables
906d1419d5aSNobutomo Nakano * can remain consistent. Here, we haven't touch any
907d1419d5aSNobutomo Nakano * variables, so it's okay to release lock.
908d1419d5aSNobutomo Nakano */
909d1419d5aSNobutomo Nakano lmutex_unlock(&_time_lock);
910d1419d5aSNobutomo Nakano new_entry = malloc(sizeof (state_t));
911d1419d5aSNobutomo Nakano lmutex_lock(&_time_lock);
912d1419d5aSNobutomo Nakano
913d1419d5aSNobutomo Nakano /*
914d1419d5aSNobutomo Nakano * check it again, since zone may have been loaded while
915d1419d5aSNobutomo Nakano * time_lock was unlocked.
916d1419d5aSNobutomo Nakano */
917d1419d5aSNobutomo Nakano entry = find_zone(zonename);
918d1419d5aSNobutomo Nakano } else {
919d1419d5aSNobutomo Nakano new_entry = NULL;
920d1419d5aSNobutomo Nakano goto out;
921d1419d5aSNobutomo Nakano }
922d1419d5aSNobutomo Nakano
923d1419d5aSNobutomo Nakano /*
924d1419d5aSNobutomo Nakano * We are here because the 1st attemp failed.
925d1419d5aSNobutomo Nakano * new_entry points newly allocated entry. If it was NULL, it
926d1419d5aSNobutomo Nakano * indicates that the memory allocation also failed.
927d1419d5aSNobutomo Nakano */
928d1419d5aSNobutomo Nakano if (entry == NULL) {
929d1419d5aSNobutomo Nakano /*
930d1419d5aSNobutomo Nakano * 2nd attemp also failed.
9317c478bd9Sstevel@tonic-gate * No timezone entry found in hash table, so load it,
9327c478bd9Sstevel@tonic-gate * and create a new timezone entry.
9337c478bd9Sstevel@tonic-gate */
9347c478bd9Sstevel@tonic-gate char *newzonename, *charsbuf;
9357c478bd9Sstevel@tonic-gate
9367c478bd9Sstevel@tonic-gate newzonename = libc_strdup(zonename);
9377c478bd9Sstevel@tonic-gate daylight = 0;
938d1419d5aSNobutomo Nakano entry = new_entry;
9397c478bd9Sstevel@tonic-gate
9407c478bd9Sstevel@tonic-gate if (entry == NULL || newzonename == NULL) {
9417c478bd9Sstevel@tonic-gate /* something wrong happened. */
942d1419d5aSNobutomo Nakano failed:
9437c478bd9Sstevel@tonic-gate if (newzonename != NULL)
9447c478bd9Sstevel@tonic-gate libc_free(newzonename);
945d1419d5aSNobutomo Nakano
946d1419d5aSNobutomo Nakano /* Invalidate the current timezone */
947d1419d5aSNobutomo Nakano curr_zonerules = ZONERULES_INVALID;
948d1419d5aSNobutomo Nakano namecache = NULL;
949d1419d5aSNobutomo Nakano
9507c478bd9Sstevel@tonic-gate timezone = altzone = 0;
9517c478bd9Sstevel@tonic-gate is_in_dst = 0;
952d1419d5aSNobutomo Nakano newtzname[0] = (char *)_tz_gmt;
953d1419d5aSNobutomo Nakano newtzname[1] = (char *)_tz_spaces;
954d1419d5aSNobutomo Nakano set_tzname(newtzname);
955d1419d5aSNobutomo Nakano return (entry);
9567c478bd9Sstevel@tonic-gate }
9577c478bd9Sstevel@tonic-gate
9587c478bd9Sstevel@tonic-gate /*
9597c478bd9Sstevel@tonic-gate * Builds transition cache and sets up zone state data for zone
9607c478bd9Sstevel@tonic-gate * specified in TZ, which can be specified as a POSIX zone or an
9617c478bd9Sstevel@tonic-gate * Olson zoneinfo file reference.
9627c478bd9Sstevel@tonic-gate *
9637c478bd9Sstevel@tonic-gate * If local data cannot be parsed or loaded, the local zone
9647c478bd9Sstevel@tonic-gate * tables are set up for GMT.
9657c478bd9Sstevel@tonic-gate *
9667c478bd9Sstevel@tonic-gate * Unless a leading ':' is prepended to TZ, TZ is initially
9677c478bd9Sstevel@tonic-gate * parsed as a POSIX zone; failing that, it reverts to
9687c478bd9Sstevel@tonic-gate * a zoneinfo check.
9697c478bd9Sstevel@tonic-gate * However, if a ':' is prepended, the zone will *only* be
9707c478bd9Sstevel@tonic-gate * parsed as zoneinfo. If any failure occurs parsing or
9717c478bd9Sstevel@tonic-gate * loading a zoneinfo TZ, GMT data is loaded for the local zone.
9727c478bd9Sstevel@tonic-gate *
9737c478bd9Sstevel@tonic-gate * Example: There is a zoneinfo file in the standard
9747c478bd9Sstevel@tonic-gate * distribution called 'PST8PDT'. The only way the user can
9757c478bd9Sstevel@tonic-gate * specify that file under Solaris is to set TZ to ":PST8PDT".
9767c478bd9Sstevel@tonic-gate * Otherwise the initial parse of PST8PDT as a POSIX zone will
9777c478bd9Sstevel@tonic-gate * succeed and be used.
9787c478bd9Sstevel@tonic-gate */
979d1419d5aSNobutomo Nakano if ((charsbuf = libc_malloc(TZ_MAX_CHARS)) == NULL)
980d1419d5aSNobutomo Nakano goto failed;
9817c478bd9Sstevel@tonic-gate
982d1419d5aSNobutomo Nakano entry->zonerules = ZONERULES_INVALID;
9837c478bd9Sstevel@tonic-gate entry->charsbuf_size = TZ_MAX_CHARS;
9847c478bd9Sstevel@tonic-gate entry->chars = charsbuf;
9857c478bd9Sstevel@tonic-gate entry->default_tzname0 = _tz_gmt;
9867c478bd9Sstevel@tonic-gate entry->default_tzname1 = _tz_spaces;
9877c478bd9Sstevel@tonic-gate entry->zonename = newzonename;
9887c478bd9Sstevel@tonic-gate
9897c478bd9Sstevel@tonic-gate if (*zonename == ':') {
9907c478bd9Sstevel@tonic-gate if (load_zoneinfo(zonename + 1, entry) != 0) {
9917c478bd9Sstevel@tonic-gate (void) load_posixinfo(_posix_gmt0, entry);
9927c478bd9Sstevel@tonic-gate }
9937c478bd9Sstevel@tonic-gate } else if (load_posixinfo(zonename, entry) != 0) {
9947c478bd9Sstevel@tonic-gate if (load_zoneinfo(zonename, entry) != 0) {
9957c478bd9Sstevel@tonic-gate (void) load_posixinfo(_posix_gmt0, entry);
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate }
998d1419d5aSNobutomo Nakano entry->last_ats_idx = -1;
999d1419d5aSNobutomo Nakano
10007c478bd9Sstevel@tonic-gate /*
10017c478bd9Sstevel@tonic-gate * The pre-allocated buffer is used; reset the free flag
10027c478bd9Sstevel@tonic-gate * so the buffer won't be freed.
10037c478bd9Sstevel@tonic-gate */
1004d1419d5aSNobutomo Nakano reg_zone(entry);
1005d1419d5aSNobutomo Nakano new_entry = NULL;
10067c478bd9Sstevel@tonic-gate }
10077c478bd9Sstevel@tonic-gate
1008d1419d5aSNobutomo Nakano out:
10097c478bd9Sstevel@tonic-gate curr_zonerules = entry->zonerules;
10107c478bd9Sstevel@tonic-gate namecache = entry->zonename;
10117c478bd9Sstevel@tonic-gate daylight = entry->daylight;
10127c478bd9Sstevel@tonic-gate lclzonep = entry;
10137c478bd9Sstevel@tonic-gate
10147c478bd9Sstevel@tonic-gate set_zone_context(t);
1015d1419d5aSNobutomo Nakano
1016d1419d5aSNobutomo Nakano /*
1017d1419d5aSNobutomo Nakano * We shouldn't release lock beyond this point since lclzonep
1018d1419d5aSNobutomo Nakano * can refer to invalid address if cache is invalidated.
1019d1419d5aSNobutomo Nakano * We defer the call to free till it can be done safely.
1020d1419d5aSNobutomo Nakano */
1021d1419d5aSNobutomo Nakano return (new_entry);
10227c478bd9Sstevel@tonic-gate }
10237c478bd9Sstevel@tonic-gate
10247c478bd9Sstevel@tonic-gate /*
10257c478bd9Sstevel@tonic-gate * Sets timezone, altzone, tzname[], extern globals, to represent
10267c478bd9Sstevel@tonic-gate * disposition of t with respect to TZ; See ctime(3C). is_in_dst,
10277c478bd9Sstevel@tonic-gate * internal global is also set. daylight is set at zone load time.
10287c478bd9Sstevel@tonic-gate *
10297c478bd9Sstevel@tonic-gate * Issues:
10307c478bd9Sstevel@tonic-gate *
10317c478bd9Sstevel@tonic-gate * In this function, any time_t not located in the cache is handled
10327c478bd9Sstevel@tonic-gate * as a miss. To build/update transition cache, load_zoneinfo()
10337c478bd9Sstevel@tonic-gate * must be called prior to this routine.
10347c478bd9Sstevel@tonic-gate *
10357c478bd9Sstevel@tonic-gate * If POSIX zone, cache miss penalty is slightly degraded
10367c478bd9Sstevel@tonic-gate * performance. For zoneinfo, penalty is decreased is_in_dst
10377c478bd9Sstevel@tonic-gate * accuracy.
10387c478bd9Sstevel@tonic-gate *
10397c478bd9Sstevel@tonic-gate * POSIX, despite its chicken/egg problem, ie. not knowing DST
10407c478bd9Sstevel@tonic-gate * until time known, and not knowing time until DST known, at
10417c478bd9Sstevel@tonic-gate * least uses the same algorithm for 64-bit time as 32-bit.
10427c478bd9Sstevel@tonic-gate *
10437c478bd9Sstevel@tonic-gate * The fact that zoneinfo files only contain transistions for 32-bit
10447c478bd9Sstevel@tonic-gate * time space is a well known problem, as yet unresolved.
10457c478bd9Sstevel@tonic-gate * Without an official standard for coping with out-of-range
10467c478bd9Sstevel@tonic-gate * zoneinfo times, assumptions must be made. For now
10477c478bd9Sstevel@tonic-gate * the assumption is: If t exceeds 32-bit boundries and local zone
10487c478bd9Sstevel@tonic-gate * is zoneinfo type, is_in_dst is set to to 0 for negative values
10497c478bd9Sstevel@tonic-gate * of t, and set to the same DST state as the highest ordered
10507c478bd9Sstevel@tonic-gate * transition in cache for positive values of t.
10517c478bd9Sstevel@tonic-gate */
10527c478bd9Sstevel@tonic-gate static void
set_zone_default_context(void)1053d1419d5aSNobutomo Nakano set_zone_default_context(void)
1054d1419d5aSNobutomo Nakano {
1055d1419d5aSNobutomo Nakano const char *newtzname[2];
1056d1419d5aSNobutomo Nakano
1057d1419d5aSNobutomo Nakano /* Retrieve suitable defaults for this zone */
1058d1419d5aSNobutomo Nakano altzone = lclzonep->default_altzone;
1059d1419d5aSNobutomo Nakano timezone = lclzonep->default_timezone;
1060d1419d5aSNobutomo Nakano newtzname[0] = (char *)lclzonep->default_tzname0;
1061d1419d5aSNobutomo Nakano newtzname[1] = (char *)lclzonep->default_tzname1;
1062d1419d5aSNobutomo Nakano is_in_dst = 0;
1063d1419d5aSNobutomo Nakano
1064d1419d5aSNobutomo Nakano set_tzname(newtzname);
1065d1419d5aSNobutomo Nakano }
1066d1419d5aSNobutomo Nakano
1067d1419d5aSNobutomo Nakano static void
set_zone_context(time_t t)10687c478bd9Sstevel@tonic-gate set_zone_context(time_t t)
10697c478bd9Sstevel@tonic-gate {
10707c478bd9Sstevel@tonic-gate prev_t *prevp;
1071d1419d5aSNobutomo Nakano int lo, hi, tidx, lidx;
10727c478bd9Sstevel@tonic-gate ttinfo_t *ttisp, *std, *alt;
1073d1419d5aSNobutomo Nakano const char *newtzname[2];
10747c478bd9Sstevel@tonic-gate
10757c478bd9Sstevel@tonic-gate /* If state data not loaded or TZ busted, just use GMT */
10767c478bd9Sstevel@tonic-gate if (lclzonep == NULL || curr_zonerules == ZONERULES_INVALID) {
10777c478bd9Sstevel@tonic-gate timezone = altzone = 0;
10787c478bd9Sstevel@tonic-gate daylight = is_in_dst = 0;
1079d1419d5aSNobutomo Nakano newtzname[0] = (char *)_tz_gmt;
1080d1419d5aSNobutomo Nakano newtzname[1] = (char *)_tz_spaces;
1081d1419d5aSNobutomo Nakano set_tzname(newtzname);
10827c478bd9Sstevel@tonic-gate return;
10837c478bd9Sstevel@tonic-gate }
10847c478bd9Sstevel@tonic-gate
1085d1419d5aSNobutomo Nakano if (lclzonep->timecnt <= 0 || lclzonep->typecnt < 2) {
10867c478bd9Sstevel@tonic-gate /* Loaded zone incapable of transitioning. */
1087d1419d5aSNobutomo Nakano set_zone_default_context();
10887c478bd9Sstevel@tonic-gate return;
1089d1419d5aSNobutomo Nakano }
10907c478bd9Sstevel@tonic-gate
10917c478bd9Sstevel@tonic-gate /*
10927c478bd9Sstevel@tonic-gate * At least one alt. zone and one transistion exist. Locate
10937c478bd9Sstevel@tonic-gate * state for 't' quickly as possible. Use defaults as necessary.
10947c478bd9Sstevel@tonic-gate */
10957c478bd9Sstevel@tonic-gate lo = 0;
10967c478bd9Sstevel@tonic-gate hi = lclzonep->timecnt - 1;
10977c478bd9Sstevel@tonic-gate
10987c478bd9Sstevel@tonic-gate if (t < lclzonep->ats[0] || t >= lclzonep->ats[hi]) {
1099d1419d5aSNobutomo Nakano /*
1100d1419d5aSNobutomo Nakano * Date which is out of definition.
1101d1419d5aSNobutomo Nakano * Calculate DST as best as possible
1102d1419d5aSNobutomo Nakano */
11037c478bd9Sstevel@tonic-gate if (lclzonep->zonerules == POSIX_USA ||
11047c478bd9Sstevel@tonic-gate lclzonep->zonerules == POSIX) {
1105d1419d5aSNobutomo Nakano /* Must invoke calculations to determine DST */
1106d1419d5aSNobutomo Nakano set_zone_default_context();
11077c478bd9Sstevel@tonic-gate is_in_dst = (daylight) ?
11087c478bd9Sstevel@tonic-gate posix_check_dst(t, lclzonep) : 0;
11097c478bd9Sstevel@tonic-gate return;
11107c478bd9Sstevel@tonic-gate } else if (t < lclzonep->ats[0]) { /* zoneinfo... */
11117c478bd9Sstevel@tonic-gate /* t precedes 1st transition. Use defaults */
1112d1419d5aSNobutomo Nakano set_zone_default_context();
11137c478bd9Sstevel@tonic-gate return;
11147c478bd9Sstevel@tonic-gate } else { /* zoneinfo */
11157c478bd9Sstevel@tonic-gate /* t follows final transistion. Use final */
11167c478bd9Sstevel@tonic-gate tidx = hi;
11177c478bd9Sstevel@tonic-gate }
11187c478bd9Sstevel@tonic-gate } else {
1119d1419d5aSNobutomo Nakano if ((lidx = lclzonep->last_ats_idx) != -1 &&
1120d1419d5aSNobutomo Nakano lidx != hi &&
1121d1419d5aSNobutomo Nakano t >= lclzonep->ats[lidx] &&
1122d1419d5aSNobutomo Nakano t < lclzonep->ats[lidx + 1]) {
1123d1419d5aSNobutomo Nakano /* CACHE HIT. Nothing needs to be done */
1124d1419d5aSNobutomo Nakano tidx = lidx;
1125d1419d5aSNobutomo Nakano } else {
1126d1419d5aSNobutomo Nakano /*
1127d1419d5aSNobutomo Nakano * CACHE MISS. Locate transition using binary search.
1128d1419d5aSNobutomo Nakano */
11297c478bd9Sstevel@tonic-gate while (lo <= hi) {
11307c478bd9Sstevel@tonic-gate tidx = (lo + hi) / 2;
11317c478bd9Sstevel@tonic-gate if (t == lclzonep->ats[tidx])
11327c478bd9Sstevel@tonic-gate break;
11337c478bd9Sstevel@tonic-gate else if (t < lclzonep->ats[tidx])
11347c478bd9Sstevel@tonic-gate hi = tidx - 1;
11357c478bd9Sstevel@tonic-gate else
11367c478bd9Sstevel@tonic-gate lo = tidx + 1;
11377c478bd9Sstevel@tonic-gate }
11387c478bd9Sstevel@tonic-gate if (lo > hi)
11397c478bd9Sstevel@tonic-gate tidx = hi;
11407c478bd9Sstevel@tonic-gate }
1141d1419d5aSNobutomo Nakano }
11427c478bd9Sstevel@tonic-gate
11437c478bd9Sstevel@tonic-gate /*
11447c478bd9Sstevel@tonic-gate * Set extern globals based on located transition and summary of
11457c478bd9Sstevel@tonic-gate * its previous state, which were cached when zone was loaded
11467c478bd9Sstevel@tonic-gate */
11477c478bd9Sstevel@tonic-gate ttisp = &lclzonep->ttis[lclzonep->types[tidx]];
11487c478bd9Sstevel@tonic-gate prevp = &lclzonep->prev[tidx];
11497c478bd9Sstevel@tonic-gate
11507c478bd9Sstevel@tonic-gate if ((is_in_dst = ttisp->tt_isdst) == 0) { /* std. time */
11517c478bd9Sstevel@tonic-gate timezone = -ttisp->tt_gmtoff;
1152d1419d5aSNobutomo Nakano newtzname[0] = &lclzonep->chars[ttisp->tt_abbrind];
11537c478bd9Sstevel@tonic-gate if ((alt = prevp->alt) != NULL) {
11547c478bd9Sstevel@tonic-gate altzone = -alt->tt_gmtoff;
1155d1419d5aSNobutomo Nakano newtzname[1] = &lclzonep->chars[alt->tt_abbrind];
1156d1419d5aSNobutomo Nakano } else {
1157d1419d5aSNobutomo Nakano altzone = lclzonep->default_altzone;
1158d1419d5aSNobutomo Nakano newtzname[1] = (char *)lclzonep->default_tzname1;
11597c478bd9Sstevel@tonic-gate }
11607c478bd9Sstevel@tonic-gate } else { /* alt. time */
11617c478bd9Sstevel@tonic-gate altzone = -ttisp->tt_gmtoff;
1162d1419d5aSNobutomo Nakano newtzname[1] = &lclzonep->chars[ttisp->tt_abbrind];
11637c478bd9Sstevel@tonic-gate if ((std = prevp->std) != NULL) {
11647c478bd9Sstevel@tonic-gate timezone = -std->tt_gmtoff;
1165d1419d5aSNobutomo Nakano newtzname[0] = &lclzonep->chars[std->tt_abbrind];
1166d1419d5aSNobutomo Nakano } else {
1167d1419d5aSNobutomo Nakano timezone = lclzonep->default_timezone;
1168d1419d5aSNobutomo Nakano newtzname[0] = (char *)lclzonep->default_tzname0;
11697c478bd9Sstevel@tonic-gate }
11707c478bd9Sstevel@tonic-gate }
1171d1419d5aSNobutomo Nakano
1172d1419d5aSNobutomo Nakano lclzonep->last_ats_idx = tidx;
1173d1419d5aSNobutomo Nakano set_tzname(newtzname);
11747c478bd9Sstevel@tonic-gate }
11757c478bd9Sstevel@tonic-gate
11767c478bd9Sstevel@tonic-gate /*
11777c478bd9Sstevel@tonic-gate * This function takes a time_t and gmt offset and produces a
11787c478bd9Sstevel@tonic-gate * tm struct based on specified time.
11797c478bd9Sstevel@tonic-gate *
11807c478bd9Sstevel@tonic-gate * The the following fields are calculated, based entirely
11817c478bd9Sstevel@tonic-gate * on the offset-adjusted value of t:
11827c478bd9Sstevel@tonic-gate *
11837c478bd9Sstevel@tonic-gate * tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec
11847c478bd9Sstevel@tonic-gate * tm_yday. tm_wday. (tm_isdst is ALWAYS set to 0).
11857c478bd9Sstevel@tonic-gate */
11867c478bd9Sstevel@tonic-gate
11877c478bd9Sstevel@tonic-gate static struct tm *
offtime_u(time_t t,long offset,struct tm * tmptr)11887c478bd9Sstevel@tonic-gate offtime_u(time_t t, long offset, struct tm *tmptr)
11897c478bd9Sstevel@tonic-gate {
11907c478bd9Sstevel@tonic-gate long days;
11917c478bd9Sstevel@tonic-gate long rem;
11927c478bd9Sstevel@tonic-gate long y;
11937c478bd9Sstevel@tonic-gate int yleap;
11947c478bd9Sstevel@tonic-gate const int *ip;
11957c478bd9Sstevel@tonic-gate
11967c478bd9Sstevel@tonic-gate days = t / SECSPERDAY;
11977c478bd9Sstevel@tonic-gate rem = t % SECSPERDAY;
11987c478bd9Sstevel@tonic-gate rem += offset;
11997c478bd9Sstevel@tonic-gate while (rem < 0) {
12007c478bd9Sstevel@tonic-gate rem += SECSPERDAY;
12017c478bd9Sstevel@tonic-gate --days;
12027c478bd9Sstevel@tonic-gate }
12037c478bd9Sstevel@tonic-gate while (rem >= SECSPERDAY) {
12047c478bd9Sstevel@tonic-gate rem -= SECSPERDAY;
12057c478bd9Sstevel@tonic-gate ++days;
12067c478bd9Sstevel@tonic-gate }
12077c478bd9Sstevel@tonic-gate tmptr->tm_hour = (int)(rem / SECSPERHOUR);
12087c478bd9Sstevel@tonic-gate rem = rem % SECSPERHOUR;
12097c478bd9Sstevel@tonic-gate tmptr->tm_min = (int)(rem / SECSPERMIN);
12107c478bd9Sstevel@tonic-gate tmptr->tm_sec = (int)(rem % SECSPERMIN);
12117c478bd9Sstevel@tonic-gate
12127c478bd9Sstevel@tonic-gate tmptr->tm_wday = (int)((EPOCH_WDAY + days) % DAYSPERWEEK);
12137c478bd9Sstevel@tonic-gate if (tmptr->tm_wday < 0)
12147c478bd9Sstevel@tonic-gate tmptr->tm_wday += DAYSPERWEEK;
12157c478bd9Sstevel@tonic-gate y = EPOCH_YEAR;
12167c478bd9Sstevel@tonic-gate while (days < 0 || days >= (long)__year_lengths[yleap = isleap(y)]) {
12177c478bd9Sstevel@tonic-gate long newy;
12187c478bd9Sstevel@tonic-gate
12197c478bd9Sstevel@tonic-gate newy = y + days / DAYSPERNYEAR;
12207c478bd9Sstevel@tonic-gate if (days < 0)
12217c478bd9Sstevel@tonic-gate --newy;
12227c478bd9Sstevel@tonic-gate days -= ((long)newy - (long)y) * DAYSPERNYEAR +
12237c478bd9Sstevel@tonic-gate LEAPS_THRU_END_OF(newy > 0 ? newy - 1L : newy) -
12247c478bd9Sstevel@tonic-gate LEAPS_THRU_END_OF(y > 0 ? y - 1L : y);
12257c478bd9Sstevel@tonic-gate y = newy;
12267c478bd9Sstevel@tonic-gate }
12277c478bd9Sstevel@tonic-gate tmptr->tm_year = (int)(y - TM_YEAR_BASE);
12287c478bd9Sstevel@tonic-gate tmptr->tm_yday = (int)days;
12297c478bd9Sstevel@tonic-gate ip = __mon_lengths[yleap];
12307c478bd9Sstevel@tonic-gate for (tmptr->tm_mon = 0; days >=
1231d1419d5aSNobutomo Nakano (long)ip[tmptr->tm_mon]; ++(tmptr->tm_mon)) {
12327c478bd9Sstevel@tonic-gate days = days - (long)ip[tmptr->tm_mon];
1233d1419d5aSNobutomo Nakano }
12347c478bd9Sstevel@tonic-gate tmptr->tm_mday = (int)(days + 1);
12357c478bd9Sstevel@tonic-gate tmptr->tm_isdst = 0;
12367c478bd9Sstevel@tonic-gate
12377c478bd9Sstevel@tonic-gate #ifdef _LP64
12387c478bd9Sstevel@tonic-gate /* do as much as possible before checking for error. */
12397c478bd9Sstevel@tonic-gate if ((y > (long)INT_MAX + TM_YEAR_BASE) ||
12407c478bd9Sstevel@tonic-gate (y < (long)INT_MIN + TM_YEAR_BASE)) {
12417c478bd9Sstevel@tonic-gate errno = EOVERFLOW;
12427c478bd9Sstevel@tonic-gate return (NULL);
12437c478bd9Sstevel@tonic-gate }
12447c478bd9Sstevel@tonic-gate #endif
12457c478bd9Sstevel@tonic-gate return (tmptr);
12467c478bd9Sstevel@tonic-gate }
12477c478bd9Sstevel@tonic-gate
12487c478bd9Sstevel@tonic-gate /*
12497c478bd9Sstevel@tonic-gate * Check whether DST is set for time in question. Only applies to
12507c478bd9Sstevel@tonic-gate * POSIX timezones. If explicit POSIX transition rules were provided
12517c478bd9Sstevel@tonic-gate * for the current zone, use those, otherwise use default USA POSIX
12527c478bd9Sstevel@tonic-gate * transitions.
12537c478bd9Sstevel@tonic-gate */
12547c478bd9Sstevel@tonic-gate static int
posix_check_dst(long long t,state_t * sp)12557c478bd9Sstevel@tonic-gate posix_check_dst(long long t, state_t *sp)
12567c478bd9Sstevel@tonic-gate {
12577c478bd9Sstevel@tonic-gate struct tm gmttm;
12587c478bd9Sstevel@tonic-gate long long jan01;
12597c478bd9Sstevel@tonic-gate int year, i, idx, ridx;
12607c478bd9Sstevel@tonic-gate posix_daylight_t pdaylight;
12617c478bd9Sstevel@tonic-gate
12627c478bd9Sstevel@tonic-gate (void) offtime_u(t, 0L, &gmttm);
12637c478bd9Sstevel@tonic-gate
12647c478bd9Sstevel@tonic-gate year = gmttm.tm_year + 1900;
12657c478bd9Sstevel@tonic-gate jan01 = t - ((gmttm.tm_yday * SECSPERDAY) +
12667c478bd9Sstevel@tonic-gate (gmttm.tm_hour * SECSPERHOUR) +
12677c478bd9Sstevel@tonic-gate (gmttm.tm_min * SECSPERMIN) + gmttm.tm_sec);
12687c478bd9Sstevel@tonic-gate /*
12697c478bd9Sstevel@tonic-gate * If transition rules were provided for this zone,
12707c478bd9Sstevel@tonic-gate * use them, otherwise, default to USA daylight rules,
12717c478bd9Sstevel@tonic-gate * which are historically correct for the continental USA,
12727c478bd9Sstevel@tonic-gate * excluding local provisions. (This logic may be replaced
12737c478bd9Sstevel@tonic-gate * at some point in the future with "posixrules" to offer
12747c478bd9Sstevel@tonic-gate * more flexibility to the system administrator).
12757c478bd9Sstevel@tonic-gate */
12767c478bd9Sstevel@tonic-gate if (sp->zonerules == POSIX) { /* POSIX rules */
12777c478bd9Sstevel@tonic-gate pdaylight.rules[0] = &sp->start_rule;
12787c478bd9Sstevel@tonic-gate pdaylight.rules[1] = &sp->end_rule;
12797c478bd9Sstevel@tonic-gate } else { /* POSIX_USA: USA */
12807c478bd9Sstevel@tonic-gate i = 0;
12817c478bd9Sstevel@tonic-gate while (year < __usa_rules[i].s_year && i < MAX_RULE_TABLE) {
12827c478bd9Sstevel@tonic-gate i++;
12837c478bd9Sstevel@tonic-gate }
12847c478bd9Sstevel@tonic-gate pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
12857c478bd9Sstevel@tonic-gate pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
12867c478bd9Sstevel@tonic-gate }
12877c478bd9Sstevel@tonic-gate pdaylight.offset[0] = timezone;
12887c478bd9Sstevel@tonic-gate pdaylight.offset[1] = altzone;
12897c478bd9Sstevel@tonic-gate
12907c478bd9Sstevel@tonic-gate idx = posix_daylight(&jan01, year, &pdaylight);
12917c478bd9Sstevel@tonic-gate ridx = !idx;
12927c478bd9Sstevel@tonic-gate
12937c478bd9Sstevel@tonic-gate /*
12947c478bd9Sstevel@tonic-gate * Note: t, rtime[0], and rtime[1] are all bounded within 'year'
12957c478bd9Sstevel@tonic-gate * beginning on 'jan01'
12967c478bd9Sstevel@tonic-gate */
12977c478bd9Sstevel@tonic-gate if (t >= pdaylight.rtime[idx] && t < pdaylight.rtime[ridx]) {
12987c478bd9Sstevel@tonic-gate return (ridx);
12997c478bd9Sstevel@tonic-gate } else {
13007c478bd9Sstevel@tonic-gate return (idx);
13017c478bd9Sstevel@tonic-gate }
13027c478bd9Sstevel@tonic-gate }
13037c478bd9Sstevel@tonic-gate
13047c478bd9Sstevel@tonic-gate /*
13057c478bd9Sstevel@tonic-gate * Given January 1, 00:00:00 GMT for a year as an Epoch-relative time,
13067c478bd9Sstevel@tonic-gate * along with the integer year #, a posix_daylight_t that is composed
13077c478bd9Sstevel@tonic-gate * of two rules, and two GMT offsets (timezone and altzone), calculate
13087c478bd9Sstevel@tonic-gate * the two Epoch-relative times the two rules take effect, and return
13097c478bd9Sstevel@tonic-gate * them in the two rtime fields of the posix_daylight_t structure.
13107c478bd9Sstevel@tonic-gate * Also update janfirst by a year, by adding the appropriate number of
13117c478bd9Sstevel@tonic-gate * seconds depending on whether the year is a leap year or not. (We take
13127c478bd9Sstevel@tonic-gate * advantage that this routine knows the leap year status.)
13137c478bd9Sstevel@tonic-gate */
13147c478bd9Sstevel@tonic-gate static int
posix_daylight(long long * janfirst,int year,posix_daylight_t * pdaylightp)13157c478bd9Sstevel@tonic-gate posix_daylight(long long *janfirst, int year, posix_daylight_t *pdaylightp)
13167c478bd9Sstevel@tonic-gate {
13177c478bd9Sstevel@tonic-gate rule_t *rulep;
13187c478bd9Sstevel@tonic-gate long offset;
13197c478bd9Sstevel@tonic-gate int idx;
13207c478bd9Sstevel@tonic-gate int i, d, m1, yy0, yy1, yy2, dow;
13217c478bd9Sstevel@tonic-gate long leapyear;
13227c478bd9Sstevel@tonic-gate long long value;
13237c478bd9Sstevel@tonic-gate
13247c478bd9Sstevel@tonic-gate static const int __secs_year_lengths[2] = {
13257c478bd9Sstevel@tonic-gate DAYS_PER_NYEAR * SECSPERDAY,
13267c478bd9Sstevel@tonic-gate DAYS_PER_LYEAR * SECSPERDAY
13277c478bd9Sstevel@tonic-gate };
13287c478bd9Sstevel@tonic-gate
13297c478bd9Sstevel@tonic-gate leapyear = isleap(year);
13307c478bd9Sstevel@tonic-gate
13317c478bd9Sstevel@tonic-gate for (idx = 0; idx < 2; idx++) {
13327c478bd9Sstevel@tonic-gate rulep = pdaylightp->rules[idx];
13337c478bd9Sstevel@tonic-gate offset = pdaylightp->offset[idx];
13347c478bd9Sstevel@tonic-gate
13357c478bd9Sstevel@tonic-gate switch (rulep->r_type) {
13367c478bd9Sstevel@tonic-gate
13377c478bd9Sstevel@tonic-gate case MON_WEEK_DOW:
13387c478bd9Sstevel@tonic-gate /*
13397c478bd9Sstevel@tonic-gate * Mm.n.d - nth "dth day" of month m.
13407c478bd9Sstevel@tonic-gate */
13417c478bd9Sstevel@tonic-gate value = *janfirst;
13427c478bd9Sstevel@tonic-gate for (i = 0; i < rulep->r_mon - 1; ++i)
13437c478bd9Sstevel@tonic-gate value += __mon_lengths[leapyear][i] *
13447c478bd9Sstevel@tonic-gate SECSPERDAY;
13457c478bd9Sstevel@tonic-gate
13467c478bd9Sstevel@tonic-gate /*
13477c478bd9Sstevel@tonic-gate * Use Zeller's Congruence to get day-of-week of first
13487c478bd9Sstevel@tonic-gate * day of month.
13497c478bd9Sstevel@tonic-gate */
13507c478bd9Sstevel@tonic-gate m1 = (rulep->r_mon + 9) % 12 + 1;
13517c478bd9Sstevel@tonic-gate yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
13527c478bd9Sstevel@tonic-gate yy1 = yy0 / 100;
13537c478bd9Sstevel@tonic-gate yy2 = yy0 % 100;
13547c478bd9Sstevel@tonic-gate dow = ((26 * m1 - 2) / 10 +
13557c478bd9Sstevel@tonic-gate 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
13567c478bd9Sstevel@tonic-gate
13577c478bd9Sstevel@tonic-gate if (dow < 0)
13587c478bd9Sstevel@tonic-gate dow += DAYSPERWEEK;
13597c478bd9Sstevel@tonic-gate
13607c478bd9Sstevel@tonic-gate /*
13617c478bd9Sstevel@tonic-gate * Following heuristic increases accuracy of USA rules
13627c478bd9Sstevel@tonic-gate * for negative years.
13637c478bd9Sstevel@tonic-gate */
13647c478bd9Sstevel@tonic-gate if (year < 1 && leapyear)
13657c478bd9Sstevel@tonic-gate ++dow;
13667c478bd9Sstevel@tonic-gate /*
13677c478bd9Sstevel@tonic-gate * "dow" is the day-of-week of the first day of the
13687c478bd9Sstevel@tonic-gate * month. Get the day-of-month, zero-origin, of the
13697c478bd9Sstevel@tonic-gate * first "dow" day of the month.
13707c478bd9Sstevel@tonic-gate */
13717c478bd9Sstevel@tonic-gate d = rulep->r_day - dow;
13727c478bd9Sstevel@tonic-gate if (d < 0)
13737c478bd9Sstevel@tonic-gate d += DAYSPERWEEK;
13747c478bd9Sstevel@tonic-gate for (i = 1; i < rulep->r_week; ++i) {
13757c478bd9Sstevel@tonic-gate if (d + DAYSPERWEEK >=
13767c478bd9Sstevel@tonic-gate __mon_lengths[leapyear][rulep->r_mon - 1])
13777c478bd9Sstevel@tonic-gate break;
13787c478bd9Sstevel@tonic-gate d += DAYSPERWEEK;
13797c478bd9Sstevel@tonic-gate }
13807c478bd9Sstevel@tonic-gate /*
13817c478bd9Sstevel@tonic-gate * "d" is the day-of-month, zero-origin, of the day
13827c478bd9Sstevel@tonic-gate * we want.
13837c478bd9Sstevel@tonic-gate */
13847c478bd9Sstevel@tonic-gate value += d * SECSPERDAY;
13857c478bd9Sstevel@tonic-gate break;
13867c478bd9Sstevel@tonic-gate
13877c478bd9Sstevel@tonic-gate case JULIAN_DAY:
13887c478bd9Sstevel@tonic-gate /*
13897c478bd9Sstevel@tonic-gate * Jn - Julian day, 1 == Jan 1, 60 == March 1 even
13907c478bd9Sstevel@tonic-gate * in leap yrs.
13917c478bd9Sstevel@tonic-gate */
13927c478bd9Sstevel@tonic-gate value = *janfirst + (rulep->r_day - 1) * SECSPERDAY;
13937c478bd9Sstevel@tonic-gate if (leapyear && rulep->r_day >= 60)
13947c478bd9Sstevel@tonic-gate value += SECSPERDAY;
13957c478bd9Sstevel@tonic-gate break;
13967c478bd9Sstevel@tonic-gate
13977c478bd9Sstevel@tonic-gate case DAY_OF_YEAR:
13987c478bd9Sstevel@tonic-gate /*
13997c478bd9Sstevel@tonic-gate * n - day of year.
14007c478bd9Sstevel@tonic-gate */
14017c478bd9Sstevel@tonic-gate value = *janfirst + rulep->r_day * SECSPERDAY;
14027c478bd9Sstevel@tonic-gate break;
14037c478bd9Sstevel@tonic-gate }
14047c478bd9Sstevel@tonic-gate pdaylightp->rtime[idx] = value + rulep->r_time + offset;
14057c478bd9Sstevel@tonic-gate }
14067c478bd9Sstevel@tonic-gate *janfirst += __secs_year_lengths[leapyear];
14077c478bd9Sstevel@tonic-gate
14087c478bd9Sstevel@tonic-gate return ((pdaylightp->rtime[0] > pdaylightp->rtime[1]) ? 1 : 0);
14097c478bd9Sstevel@tonic-gate }
14107c478bd9Sstevel@tonic-gate
14117c478bd9Sstevel@tonic-gate /*
14127c478bd9Sstevel@tonic-gate * Try to load zoneinfo file into internal transition tables using name
14137c478bd9Sstevel@tonic-gate * indicated in TZ, and do validity checks. The format of zic(1M)
14147c478bd9Sstevel@tonic-gate * compiled zoneinfo files isdescribed in tzfile.h
14157c478bd9Sstevel@tonic-gate */
14167c478bd9Sstevel@tonic-gate static int
load_zoneinfo(const char * name,state_t * sp)14177c478bd9Sstevel@tonic-gate load_zoneinfo(const char *name, state_t *sp)
14187c478bd9Sstevel@tonic-gate {
14197c478bd9Sstevel@tonic-gate char *cp;
14207c478bd9Sstevel@tonic-gate char *cp2;
14217c478bd9Sstevel@tonic-gate int i;
14227c478bd9Sstevel@tonic-gate long cnt;
14237c478bd9Sstevel@tonic-gate int fid;
14247c478bd9Sstevel@tonic-gate int ttisstdcnt;
14257c478bd9Sstevel@tonic-gate int ttisgmtcnt;
14267c478bd9Sstevel@tonic-gate char *fullname;
14277c478bd9Sstevel@tonic-gate size_t namelen;
14287c478bd9Sstevel@tonic-gate char *bufp;
14297c478bd9Sstevel@tonic-gate size_t flen;
14307c478bd9Sstevel@tonic-gate prev_t *prevp;
14317c478bd9Sstevel@tonic-gate /* LINTED */
14327c478bd9Sstevel@tonic-gate struct tzhead *tzhp;
14337c478bd9Sstevel@tonic-gate struct stat64 stbuf;
14347c478bd9Sstevel@tonic-gate ttinfo_t *most_recent_alt = NULL;
14357c478bd9Sstevel@tonic-gate ttinfo_t *most_recent_std = NULL;
14367c478bd9Sstevel@tonic-gate ttinfo_t *ttisp;
14377c478bd9Sstevel@tonic-gate
14387c478bd9Sstevel@tonic-gate
14397c478bd9Sstevel@tonic-gate if (name == NULL && (name = TZDEFAULT) == NULL)
14407c478bd9Sstevel@tonic-gate return (-1);
14417c478bd9Sstevel@tonic-gate
14427c478bd9Sstevel@tonic-gate if ((name[0] == '/') || strstr(name, "../"))
14437c478bd9Sstevel@tonic-gate return (-1);
14447c478bd9Sstevel@tonic-gate
14457c478bd9Sstevel@tonic-gate /*
14467c478bd9Sstevel@tonic-gate * We allocate fullname this way to avoid having
14477c478bd9Sstevel@tonic-gate * a PATH_MAX size buffer in our stack frame.
14487c478bd9Sstevel@tonic-gate */
14497c478bd9Sstevel@tonic-gate namelen = LEN_TZDIR + 1 + strlen(name) + 1;
14507c478bd9Sstevel@tonic-gate if ((fullname = lmalloc(namelen)) == NULL)
14517c478bd9Sstevel@tonic-gate return (-1);
14527c478bd9Sstevel@tonic-gate (void) strcpy(fullname, TZDIR "/");
14537c478bd9Sstevel@tonic-gate (void) strcpy(fullname + LEN_TZDIR + 1, name);
14547c478bd9Sstevel@tonic-gate if ((fid = open(fullname, O_RDONLY)) == -1) {
14557c478bd9Sstevel@tonic-gate lfree(fullname, namelen);
14567c478bd9Sstevel@tonic-gate return (-1);
14577c478bd9Sstevel@tonic-gate }
14587c478bd9Sstevel@tonic-gate lfree(fullname, namelen);
14597c478bd9Sstevel@tonic-gate
14607c478bd9Sstevel@tonic-gate if (fstat64(fid, &stbuf) == -1) {
14617c478bd9Sstevel@tonic-gate (void) close(fid);
14627c478bd9Sstevel@tonic-gate return (-1);
14637c478bd9Sstevel@tonic-gate }
14647c478bd9Sstevel@tonic-gate
14657c478bd9Sstevel@tonic-gate flen = (size_t)stbuf.st_size;
14667c478bd9Sstevel@tonic-gate if (flen < sizeof (struct tzhead)) {
14677c478bd9Sstevel@tonic-gate (void) close(fid);
14687c478bd9Sstevel@tonic-gate return (-1);
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate
14717c478bd9Sstevel@tonic-gate /*
14727c478bd9Sstevel@tonic-gate * It would be nice to use alloca() to allocate bufp but,
14737c478bd9Sstevel@tonic-gate * as above, we wish to avoid allocating a big buffer in
14747c478bd9Sstevel@tonic-gate * our stack frame, and also because alloca() gives us no
14757c478bd9Sstevel@tonic-gate * opportunity to fail gracefully on allocation failure.
14767c478bd9Sstevel@tonic-gate */
14777c478bd9Sstevel@tonic-gate cp = bufp = lmalloc(flen);
14787c478bd9Sstevel@tonic-gate if (bufp == NULL) {
14797c478bd9Sstevel@tonic-gate (void) close(fid);
14807c478bd9Sstevel@tonic-gate return (-1);
14817c478bd9Sstevel@tonic-gate }
14827c478bd9Sstevel@tonic-gate
14837c478bd9Sstevel@tonic-gate if ((cnt = read(fid, bufp, flen)) != flen) {
14847c478bd9Sstevel@tonic-gate lfree(bufp, flen);
14857c478bd9Sstevel@tonic-gate (void) close(fid);
14867c478bd9Sstevel@tonic-gate return (-1);
14877c478bd9Sstevel@tonic-gate }
14887c478bd9Sstevel@tonic-gate
14897c478bd9Sstevel@tonic-gate if (close(fid) != 0) {
14907c478bd9Sstevel@tonic-gate lfree(bufp, flen);
14917c478bd9Sstevel@tonic-gate return (-1);
14927c478bd9Sstevel@tonic-gate }
14937c478bd9Sstevel@tonic-gate
14947c478bd9Sstevel@tonic-gate cp += (sizeof (tzhp->tzh_magic)) + (sizeof (tzhp->tzh_reserved));
14957c478bd9Sstevel@tonic-gate
14967c478bd9Sstevel@tonic-gate /* LINTED: alignment */
14977c478bd9Sstevel@tonic-gate ttisstdcnt = CVTZCODE(cp);
14987c478bd9Sstevel@tonic-gate /* LINTED: alignment */
14997c478bd9Sstevel@tonic-gate ttisgmtcnt = CVTZCODE(cp);
15007c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15017c478bd9Sstevel@tonic-gate sp->leapcnt = CVTZCODE(cp);
15027c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15037c478bd9Sstevel@tonic-gate sp->timecnt = CVTZCODE(cp);
15047c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15057c478bd9Sstevel@tonic-gate sp->typecnt = CVTZCODE(cp);
15067c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15077c478bd9Sstevel@tonic-gate sp->charcnt = CVTZCODE(cp);
15087c478bd9Sstevel@tonic-gate
15097c478bd9Sstevel@tonic-gate if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
15107c478bd9Sstevel@tonic-gate sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
15117c478bd9Sstevel@tonic-gate sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
15127c478bd9Sstevel@tonic-gate sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
15137c478bd9Sstevel@tonic-gate (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
15147c478bd9Sstevel@tonic-gate (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) {
15157c478bd9Sstevel@tonic-gate lfree(bufp, flen);
15167c478bd9Sstevel@tonic-gate return (-1);
15177c478bd9Sstevel@tonic-gate }
15187c478bd9Sstevel@tonic-gate
15197c478bd9Sstevel@tonic-gate if (cnt - (cp - bufp) < (long)(sp->timecnt * 4 + /* ats */
15207c478bd9Sstevel@tonic-gate sp->timecnt + /* types */
15217c478bd9Sstevel@tonic-gate sp->typecnt * (4 + 2) + /* ttinfos */
15227c478bd9Sstevel@tonic-gate sp->charcnt + /* chars */
15237c478bd9Sstevel@tonic-gate sp->leapcnt * (4 + 4) + /* lsinfos */
15247c478bd9Sstevel@tonic-gate ttisstdcnt + /* ttisstds */
15257c478bd9Sstevel@tonic-gate ttisgmtcnt)) { /* ttisgmts */
15267c478bd9Sstevel@tonic-gate lfree(bufp, flen);
15277c478bd9Sstevel@tonic-gate return (-1);
15287c478bd9Sstevel@tonic-gate }
15297c478bd9Sstevel@tonic-gate
15307c478bd9Sstevel@tonic-gate
15317c478bd9Sstevel@tonic-gate for (i = 0; i < sp->timecnt; ++i) {
15327c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15337c478bd9Sstevel@tonic-gate sp->ats[i] = CVTZCODE(cp);
15347c478bd9Sstevel@tonic-gate }
15357c478bd9Sstevel@tonic-gate
15367c478bd9Sstevel@tonic-gate /*
15377c478bd9Sstevel@tonic-gate * Skip over types[] for now and load ttis[] so that when
15387c478bd9Sstevel@tonic-gate * types[] are loaded we can check for transitions to STD & DST.
15397c478bd9Sstevel@tonic-gate * This allows us to shave cycles in ltzset_u(), including
15407c478bd9Sstevel@tonic-gate * eliminating the need to check set 'daylight' later.
15417c478bd9Sstevel@tonic-gate */
15427c478bd9Sstevel@tonic-gate
15437c478bd9Sstevel@tonic-gate cp2 = (char *)((uintptr_t)cp + sp->timecnt);
15447c478bd9Sstevel@tonic-gate
15457c478bd9Sstevel@tonic-gate for (i = 0; i < sp->typecnt; ++i) {
15467c478bd9Sstevel@tonic-gate ttisp = &sp->ttis[i];
15477c478bd9Sstevel@tonic-gate /* LINTED: alignment */
15487c478bd9Sstevel@tonic-gate ttisp->tt_gmtoff = CVTZCODE(cp2);
15497c478bd9Sstevel@tonic-gate ttisp->tt_isdst = (uchar_t)*cp2++;
15507c478bd9Sstevel@tonic-gate
15517c478bd9Sstevel@tonic-gate if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) {
15527c478bd9Sstevel@tonic-gate lfree(bufp, flen);
15537c478bd9Sstevel@tonic-gate return (-1);
15547c478bd9Sstevel@tonic-gate }
15557c478bd9Sstevel@tonic-gate
15567c478bd9Sstevel@tonic-gate ttisp->tt_abbrind = (uchar_t)*cp2++;
15577c478bd9Sstevel@tonic-gate if (ttisp->tt_abbrind < 0 ||
15587c478bd9Sstevel@tonic-gate ttisp->tt_abbrind > sp->charcnt) {
15597c478bd9Sstevel@tonic-gate lfree(bufp, flen);
15607c478bd9Sstevel@tonic-gate return (-1);
15617c478bd9Sstevel@tonic-gate }
15627c478bd9Sstevel@tonic-gate }
15637c478bd9Sstevel@tonic-gate
15647c478bd9Sstevel@tonic-gate /*
15657c478bd9Sstevel@tonic-gate * Since ttis were loaded ahead of types, it is possible to
15667c478bd9Sstevel@tonic-gate * detect whether daylight is ever set for this zone now, and
15677c478bd9Sstevel@tonic-gate * also preload other information to avoid repeated lookups later.
15687c478bd9Sstevel@tonic-gate * This logic facilitates keeping a running tab on the state of
15697c478bd9Sstevel@tonic-gate * std zone and alternate zone transitions such that timezone,
15707c478bd9Sstevel@tonic-gate * altzone and tzname[] can be determined quickly via an
15717c478bd9Sstevel@tonic-gate * index to any transition.
15727c478bd9Sstevel@tonic-gate *
15737c478bd9Sstevel@tonic-gate * For transition #0 there are no previous transitions,
15747c478bd9Sstevel@tonic-gate * so prev->std and prev->alt will be null, but that's OK,
15757c478bd9Sstevel@tonic-gate * because null prev->std/prev->alt effectively
15767c478bd9Sstevel@tonic-gate * indicates none existed prior.
15777c478bd9Sstevel@tonic-gate */
15787c478bd9Sstevel@tonic-gate
15797c478bd9Sstevel@tonic-gate prevp = &sp->prev[0];
15807c478bd9Sstevel@tonic-gate
15817c478bd9Sstevel@tonic-gate for (i = 0; i < sp->timecnt; ++i) {
15827c478bd9Sstevel@tonic-gate
15837c478bd9Sstevel@tonic-gate sp->types[i] = (uchar_t)*cp++;
15847c478bd9Sstevel@tonic-gate ttisp = &sp->ttis[sp->types[i]];
15857c478bd9Sstevel@tonic-gate
15867c478bd9Sstevel@tonic-gate prevp->std = most_recent_std;
15877c478bd9Sstevel@tonic-gate prevp->alt = most_recent_alt;
15887c478bd9Sstevel@tonic-gate
15897c478bd9Sstevel@tonic-gate if (ttisp->tt_isdst == 1) {
15907c478bd9Sstevel@tonic-gate most_recent_alt = ttisp;
15917c478bd9Sstevel@tonic-gate } else {
15927c478bd9Sstevel@tonic-gate most_recent_std = ttisp;
15937c478bd9Sstevel@tonic-gate }
15947c478bd9Sstevel@tonic-gate
15957c478bd9Sstevel@tonic-gate if ((int)sp->types[i] >= sp->typecnt) {
15967c478bd9Sstevel@tonic-gate lfree(bufp, flen);
15977c478bd9Sstevel@tonic-gate return (-1);
15987c478bd9Sstevel@tonic-gate }
15997c478bd9Sstevel@tonic-gate
16007c478bd9Sstevel@tonic-gate ++prevp;
16017c478bd9Sstevel@tonic-gate }
16027c478bd9Sstevel@tonic-gate if (most_recent_alt == NULL)
16037c478bd9Sstevel@tonic-gate sp->daylight = 0;
16047c478bd9Sstevel@tonic-gate else
16057c478bd9Sstevel@tonic-gate sp->daylight = 1;
16067c478bd9Sstevel@tonic-gate
16077c478bd9Sstevel@tonic-gate /*
16087c478bd9Sstevel@tonic-gate * Set pointer ahead to where it would have been if we
16097c478bd9Sstevel@tonic-gate * had read types[] and ttis[] in the same order they
16107c478bd9Sstevel@tonic-gate * occurred in the file.
16117c478bd9Sstevel@tonic-gate */
16127c478bd9Sstevel@tonic-gate cp = cp2;
16137c478bd9Sstevel@tonic-gate for (i = 0; i < sp->charcnt; ++i)
16147c478bd9Sstevel@tonic-gate sp->chars[i] = *cp++;
16157c478bd9Sstevel@tonic-gate
16167c478bd9Sstevel@tonic-gate sp->chars[i] = '\0'; /* ensure '\0' at end */
16177c478bd9Sstevel@tonic-gate
16187c478bd9Sstevel@tonic-gate for (i = 0; i < sp->leapcnt; ++i) {
16197c478bd9Sstevel@tonic-gate struct lsinfo *lsisp;
16207c478bd9Sstevel@tonic-gate
16217c478bd9Sstevel@tonic-gate lsisp = &sp->lsis[i];
16227c478bd9Sstevel@tonic-gate /* LINTED: alignment */
16237c478bd9Sstevel@tonic-gate lsisp->ls_trans = CVTZCODE(cp);
16247c478bd9Sstevel@tonic-gate /* LINTED: alignment */
16257c478bd9Sstevel@tonic-gate lsisp->ls_corr = CVTZCODE(cp);
16267c478bd9Sstevel@tonic-gate }
16277c478bd9Sstevel@tonic-gate
16287c478bd9Sstevel@tonic-gate for (i = 0; i < sp->typecnt; ++i) {
16297c478bd9Sstevel@tonic-gate ttisp = &sp->ttis[i];
16307c478bd9Sstevel@tonic-gate if (ttisstdcnt == 0) {
16317c478bd9Sstevel@tonic-gate ttisp->tt_ttisstd = FALSE;
16327c478bd9Sstevel@tonic-gate } else {
16337c478bd9Sstevel@tonic-gate ttisp->tt_ttisstd = *cp++;
16347c478bd9Sstevel@tonic-gate if (ttisp->tt_ttisstd != TRUE &&
16357c478bd9Sstevel@tonic-gate ttisp->tt_ttisstd != FALSE) {
16367c478bd9Sstevel@tonic-gate lfree(bufp, flen);
16377c478bd9Sstevel@tonic-gate return (-1);
16387c478bd9Sstevel@tonic-gate }
16397c478bd9Sstevel@tonic-gate }
16407c478bd9Sstevel@tonic-gate }
16417c478bd9Sstevel@tonic-gate
16427c478bd9Sstevel@tonic-gate for (i = 0; i < sp->typecnt; ++i) {
16437c478bd9Sstevel@tonic-gate ttisp = &sp->ttis[i];
16447c478bd9Sstevel@tonic-gate if (ttisgmtcnt == 0) {
16457c478bd9Sstevel@tonic-gate ttisp->tt_ttisgmt = FALSE;
16467c478bd9Sstevel@tonic-gate } else {
16477c478bd9Sstevel@tonic-gate ttisp->tt_ttisgmt = *cp++;
16487c478bd9Sstevel@tonic-gate if (ttisp->tt_ttisgmt != TRUE &&
16497c478bd9Sstevel@tonic-gate ttisp->tt_ttisgmt != FALSE) {
16507c478bd9Sstevel@tonic-gate lfree(bufp, flen);
16517c478bd9Sstevel@tonic-gate return (-1);
16527c478bd9Sstevel@tonic-gate }
16537c478bd9Sstevel@tonic-gate }
16547c478bd9Sstevel@tonic-gate }
16557c478bd9Sstevel@tonic-gate
16567c478bd9Sstevel@tonic-gate /*
16577c478bd9Sstevel@tonic-gate * Other defaults set at beginning of this routine
16587c478bd9Sstevel@tonic-gate * to cover case where zoneinfo file cannot be loaded
16597c478bd9Sstevel@tonic-gate */
16607c478bd9Sstevel@tonic-gate sp->default_timezone = -sp->ttis[0].tt_gmtoff;
16617c478bd9Sstevel@tonic-gate sp->default_altzone = 0;
16627c478bd9Sstevel@tonic-gate sp->default_tzname0 = &sp->chars[0];
16637c478bd9Sstevel@tonic-gate sp->default_tzname1 = _tz_spaces;
16647c478bd9Sstevel@tonic-gate
16657c478bd9Sstevel@tonic-gate lfree(bufp, flen);
16667c478bd9Sstevel@tonic-gate
16677c478bd9Sstevel@tonic-gate sp->zonerules = ZONEINFO;
16687c478bd9Sstevel@tonic-gate
16697c478bd9Sstevel@tonic-gate return (0);
16707c478bd9Sstevel@tonic-gate }
16717c478bd9Sstevel@tonic-gate
1672d1419d5aSNobutomo Nakano #ifdef _TZ_DEBUG
1673d1419d5aSNobutomo Nakano static void
print_state(state_t * sp)1674d1419d5aSNobutomo Nakano print_state(state_t *sp)
1675d1419d5aSNobutomo Nakano {
1676d1419d5aSNobutomo Nakano struct tm tmp;
1677d1419d5aSNobutomo Nakano int i, c;
1678d1419d5aSNobutomo Nakano
1679d1419d5aSNobutomo Nakano (void) fprintf(stderr, "=========================================\n");
1680d1419d5aSNobutomo Nakano (void) fprintf(stderr, "zonename: \"%s\"\n", sp->zonename);
1681d1419d5aSNobutomo Nakano (void) fprintf(stderr, "next: 0x%p\n", (void *)sp->next);
1682d1419d5aSNobutomo Nakano (void) fprintf(stderr, "zonerules: %s\n",
1683d1419d5aSNobutomo Nakano sp->zonerules == ZONERULES_INVALID ? "ZONERULES_INVALID" :
1684d1419d5aSNobutomo Nakano sp->zonerules == POSIX ? "POSIX" :
1685d1419d5aSNobutomo Nakano sp->zonerules == POSIX_USA ? "POSIX_USA" :
1686d1419d5aSNobutomo Nakano sp->zonerules == ZONEINFO ? "ZONEINFO" : "UNKNOWN");
1687d1419d5aSNobutomo Nakano (void) fprintf(stderr, "daylight: %d\n", sp->daylight);
1688d1419d5aSNobutomo Nakano (void) fprintf(stderr, "default_timezone: %ld\n", sp->default_timezone);
1689d1419d5aSNobutomo Nakano (void) fprintf(stderr, "default_altzone: %ld\n", sp->default_altzone);
1690d1419d5aSNobutomo Nakano (void) fprintf(stderr, "default_tzname0: \"%s\"\n",
1691d1419d5aSNobutomo Nakano sp->default_tzname0);
1692d1419d5aSNobutomo Nakano (void) fprintf(stderr, "default_tzname1: \"%s\"\n",
1693d1419d5aSNobutomo Nakano sp->default_tzname1);
1694d1419d5aSNobutomo Nakano (void) fprintf(stderr, "leapcnt: %d\n", sp->leapcnt);
1695d1419d5aSNobutomo Nakano (void) fprintf(stderr, "timecnt: %d\n", sp->timecnt);
1696d1419d5aSNobutomo Nakano (void) fprintf(stderr, "typecnt: %d\n", sp->typecnt);
1697d1419d5aSNobutomo Nakano (void) fprintf(stderr, "charcnt: %d\n", sp->charcnt);
1698d1419d5aSNobutomo Nakano (void) fprintf(stderr, "chars: \"%s\"\n", sp->chars);
1699d1419d5aSNobutomo Nakano (void) fprintf(stderr, "charsbuf_size: %u\n", sp->charsbuf_size);
1700d1419d5aSNobutomo Nakano (void) fprintf(stderr, "prev: skipping...\n");
1701d1419d5aSNobutomo Nakano (void) fprintf(stderr, "ats = {\n");
1702d1419d5aSNobutomo Nakano for (c = 0, i = 0; i < sp->timecnt; i++) {
1703d1419d5aSNobutomo Nakano char buf[64];
1704d1419d5aSNobutomo Nakano size_t len;
1705d1419d5aSNobutomo Nakano if (c != 0) {
1706d1419d5aSNobutomo Nakano (void) fprintf(stderr, ", ");
1707d1419d5aSNobutomo Nakano }
1708d1419d5aSNobutomo Nakano (void) asctime_r(gmtime_r(&sp->ats[i], &tmp),
1709d1419d5aSNobutomo Nakano buf, sizeof (buf));
1710d1419d5aSNobutomo Nakano len = strlen(buf);
1711d1419d5aSNobutomo Nakano buf[len-1] = '\0';
1712d1419d5aSNobutomo Nakano (void) fprintf(stderr, "%s", buf);
1713d1419d5aSNobutomo Nakano if (c == 1) {
1714d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\n");
1715d1419d5aSNobutomo Nakano c = 0;
1716d1419d5aSNobutomo Nakano } else {
1717d1419d5aSNobutomo Nakano c++;
1718d1419d5aSNobutomo Nakano }
1719d1419d5aSNobutomo Nakano }
1720d1419d5aSNobutomo Nakano (void) fprintf(stderr, "}\n");
1721d1419d5aSNobutomo Nakano (void) fprintf(stderr, "types = {\n");
1722d1419d5aSNobutomo Nakano for (c = 0, i = 0; i < sp->timecnt; i++) {
1723d1419d5aSNobutomo Nakano if (c == 0) {
1724d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t");
1725d1419d5aSNobutomo Nakano } else {
1726d1419d5aSNobutomo Nakano (void) fprintf(stderr, ", ");
1727d1419d5aSNobutomo Nakano }
1728d1419d5aSNobutomo Nakano (void) fprintf(stderr, "%d", sp->types[i]);
1729d1419d5aSNobutomo Nakano if (c == 7) {
1730d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\n");
1731d1419d5aSNobutomo Nakano c = 0;
1732d1419d5aSNobutomo Nakano } else {
1733d1419d5aSNobutomo Nakano c++;
1734d1419d5aSNobutomo Nakano }
1735d1419d5aSNobutomo Nakano }
1736d1419d5aSNobutomo Nakano (void) fprintf(stderr, "}\n");
1737d1419d5aSNobutomo Nakano (void) fprintf(stderr, "ttis = {\n");
1738d1419d5aSNobutomo Nakano for (i = 0; i < sp->typecnt; i++) {
1739d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t{\n");
1740d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t\ttt_gmtoff: %ld\n",
1741d1419d5aSNobutomo Nakano sp->ttis[i].tt_gmtoff);
1742d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t\ttt_ttisdst: %d\n",
1743d1419d5aSNobutomo Nakano sp->ttis[i].tt_isdst);
1744d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t\ttt_abbrind: %d\n",
1745d1419d5aSNobutomo Nakano sp->ttis[i].tt_abbrind);
1746d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t\ttt_tt_isstd: %d\n",
1747d1419d5aSNobutomo Nakano sp->ttis[i].tt_ttisstd);
1748d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t\ttt_ttisgmt: %d\n",
1749d1419d5aSNobutomo Nakano sp->ttis[i].tt_ttisgmt);
1750d1419d5aSNobutomo Nakano (void) fprintf(stderr, "\t}\n");
1751d1419d5aSNobutomo Nakano }
1752d1419d5aSNobutomo Nakano (void) fprintf(stderr, "}\n");
1753d1419d5aSNobutomo Nakano }
1754d1419d5aSNobutomo Nakano #endif
1755d1419d5aSNobutomo Nakano
17567c478bd9Sstevel@tonic-gate /*
17577c478bd9Sstevel@tonic-gate * Given a POSIX section 8-style TZ string, fill in transition tables.
17587c478bd9Sstevel@tonic-gate *
17597c478bd9Sstevel@tonic-gate * Examples:
17607c478bd9Sstevel@tonic-gate *
17617c478bd9Sstevel@tonic-gate * TZ = PST8 or GMT0
17627c478bd9Sstevel@tonic-gate * Timecnt set to 0 and typecnt set to 1, reflecting std time only.
17637c478bd9Sstevel@tonic-gate *
17647c478bd9Sstevel@tonic-gate * TZ = PST8PDT or PST8PDT7
17657c478bd9Sstevel@tonic-gate * Create transition times by applying USA transitions from
17667c478bd9Sstevel@tonic-gate * Jan 1 of each year covering 1902-2038. POSIX offsets
17677c478bd9Sstevel@tonic-gate * as specified in the TZ are used to calculate the tt_gmtoff
17687c478bd9Sstevel@tonic-gate * for each of the two zones. If ommitted, DST defaults to
17697c478bd9Sstevel@tonic-gate * std. time minus one hour.
17707c478bd9Sstevel@tonic-gate *
17717c478bd9Sstevel@tonic-gate * TZ = <PST8>8PDT or <PST8>8<PDT9>
17727c478bd9Sstevel@tonic-gate * Quoted transition. The values in angled brackets are treated
17737c478bd9Sstevel@tonic-gate * as zone name text, not parsed as offsets. The offsets
17747c478bd9Sstevel@tonic-gate * occuring following the zonename section. In this way,
17757c478bd9Sstevel@tonic-gate * instead of PST being displayed for standard time, it could
17767c478bd9Sstevel@tonic-gate * be displayed as PST8 to give an indication of the offset
17777c478bd9Sstevel@tonic-gate * of that zone to GMT.
17787c478bd9Sstevel@tonic-gate *
17797c478bd9Sstevel@tonic-gate * TZ = GMT0BST, M3.5.0/1, M10.5.0/2 or GMT0BST, J23953, J23989
17807c478bd9Sstevel@tonic-gate * Create transition times based on the application new-year
17817c478bd9Sstevel@tonic-gate * relative POSIX transitions, parsed from TZ, from Jan 1
17827c478bd9Sstevel@tonic-gate * for each year covering 1902-2038. POSIX offsets specified
17837c478bd9Sstevel@tonic-gate * in TZ are used to calculate tt_gmtoff for each of the two
17847c478bd9Sstevel@tonic-gate * zones.
17857c478bd9Sstevel@tonic-gate *
17867c478bd9Sstevel@tonic-gate */
17877c478bd9Sstevel@tonic-gate static int
load_posixinfo(const char * name,state_t * sp)17887c478bd9Sstevel@tonic-gate load_posixinfo(const char *name, state_t *sp)
17897c478bd9Sstevel@tonic-gate {
17907c478bd9Sstevel@tonic-gate const char *stdname;
17917c478bd9Sstevel@tonic-gate const char *dstname = 0;
17927c478bd9Sstevel@tonic-gate size_t stdlen;
17937c478bd9Sstevel@tonic-gate size_t dstlen;
17947c478bd9Sstevel@tonic-gate long stdoff = 0;
17957c478bd9Sstevel@tonic-gate long dstoff = 0;
17967c478bd9Sstevel@tonic-gate char *cp;
17977c478bd9Sstevel@tonic-gate int i;
17987c478bd9Sstevel@tonic-gate ttinfo_t *dst;
17997c478bd9Sstevel@tonic-gate ttinfo_t *std;
18007c478bd9Sstevel@tonic-gate int quoted;
18017c478bd9Sstevel@tonic-gate zone_rules_t zonetype;
1802d1419d5aSNobutomo Nakano
18037c478bd9Sstevel@tonic-gate
18047c478bd9Sstevel@tonic-gate zonetype = POSIX_USA;
18057c478bd9Sstevel@tonic-gate stdname = name;
18067c478bd9Sstevel@tonic-gate
18077c478bd9Sstevel@tonic-gate if ((quoted = (*stdname == '<')) != 0)
18087c478bd9Sstevel@tonic-gate ++stdname;
18097c478bd9Sstevel@tonic-gate
18107c478bd9Sstevel@tonic-gate /* Parse/extract STD zone name, len and GMT offset */
18117c478bd9Sstevel@tonic-gate if (*name != '\0') {
18127c478bd9Sstevel@tonic-gate if ((name = getzname(name, quoted)) == NULL)
18137c478bd9Sstevel@tonic-gate return (-1);
18147c478bd9Sstevel@tonic-gate stdlen = name - stdname;
18157c478bd9Sstevel@tonic-gate if (*name == '>')
18167c478bd9Sstevel@tonic-gate ++name;
18177c478bd9Sstevel@tonic-gate if (*name == '\0' || stdlen < 1) {
18187c478bd9Sstevel@tonic-gate return (-1);
18197c478bd9Sstevel@tonic-gate } else {
18207c478bd9Sstevel@tonic-gate if ((name = getoffset(name, &stdoff)) == NULL)
18217c478bd9Sstevel@tonic-gate return (-1);
18227c478bd9Sstevel@tonic-gate }
18237c478bd9Sstevel@tonic-gate }
18247c478bd9Sstevel@tonic-gate
18257c478bd9Sstevel@tonic-gate /* If DST specified in TZ, extract DST zone details */
18267c478bd9Sstevel@tonic-gate if (*name != '\0') {
18277c478bd9Sstevel@tonic-gate
18287c478bd9Sstevel@tonic-gate dstname = name;
18297c478bd9Sstevel@tonic-gate if ((quoted = (*dstname == '<')) != 0)
18307c478bd9Sstevel@tonic-gate ++dstname;
18317c478bd9Sstevel@tonic-gate if ((name = getzname(name, quoted)) == NULL)
18327c478bd9Sstevel@tonic-gate return (-1);
18337c478bd9Sstevel@tonic-gate dstlen = name - dstname;
18347c478bd9Sstevel@tonic-gate if (dstlen < 1)
18357c478bd9Sstevel@tonic-gate return (-1);
18367c478bd9Sstevel@tonic-gate if (*name == '>')
18377c478bd9Sstevel@tonic-gate ++name;
18387c478bd9Sstevel@tonic-gate if (*name != '\0' && *name != ',' && *name != ';') {
18397c478bd9Sstevel@tonic-gate if ((name = getoffset(name, &dstoff)) == NULL)
18407c478bd9Sstevel@tonic-gate return (-1);
18417c478bd9Sstevel@tonic-gate } else {
18427c478bd9Sstevel@tonic-gate dstoff = stdoff - SECSPERHOUR;
18437c478bd9Sstevel@tonic-gate }
18447c478bd9Sstevel@tonic-gate
1845d1419d5aSNobutomo Nakano if (*name != ',' && *name != ';') {
1846d1419d5aSNobutomo Nakano /* no transtition specified; using default rule */
1847d1419d5aSNobutomo Nakano if (load_zoneinfo(TZDEFRULES, sp) == 0 &&
1848d1419d5aSNobutomo Nakano sp->daylight == 1) {
1849d1419d5aSNobutomo Nakano /* loading TZDEFRULES zoneinfo succeeded */
1850d1419d5aSNobutomo Nakano adjust_posix_default(sp, stdoff, dstoff);
1851d1419d5aSNobutomo Nakano } else {
1852d1419d5aSNobutomo Nakano /* loading TZDEFRULES zoneinfo failed */
1853d1419d5aSNobutomo Nakano load_posix_transitions(sp, stdoff, dstoff,
1854d1419d5aSNobutomo Nakano zonetype);
1855d1419d5aSNobutomo Nakano }
1856d1419d5aSNobutomo Nakano } else {
1857d1419d5aSNobutomo Nakano /* extract POSIX transitions from TZ */
18587c478bd9Sstevel@tonic-gate /* Backward compatibility using ';' separator */
18597c478bd9Sstevel@tonic-gate int compat_flag = (*name == ';');
18607c478bd9Sstevel@tonic-gate ++name;
18617c478bd9Sstevel@tonic-gate if ((name = getrule(name, &sp->start_rule, compat_flag))
18627c478bd9Sstevel@tonic-gate == NULL)
18637c478bd9Sstevel@tonic-gate return (-1);
18647c478bd9Sstevel@tonic-gate if (*name++ != ',')
18657c478bd9Sstevel@tonic-gate return (-1);
18667c478bd9Sstevel@tonic-gate if ((name = getrule(name, &sp->end_rule, compat_flag))
18677c478bd9Sstevel@tonic-gate == NULL)
18687c478bd9Sstevel@tonic-gate return (-1);
18697c478bd9Sstevel@tonic-gate if (*name != '\0')
18707c478bd9Sstevel@tonic-gate return (-1);
18717c478bd9Sstevel@tonic-gate zonetype = POSIX;
1872d1419d5aSNobutomo Nakano load_posix_transitions(sp, stdoff, dstoff, zonetype);
18737c478bd9Sstevel@tonic-gate }
18747c478bd9Sstevel@tonic-gate dst = &sp->ttis[0];
18757c478bd9Sstevel@tonic-gate std = &sp->ttis[1];
18767c478bd9Sstevel@tonic-gate } else { /* DST wasn't specified in POSIX TZ */
18777c478bd9Sstevel@tonic-gate
18787c478bd9Sstevel@tonic-gate /* Since we only have STD time, there are no transitions */
18797c478bd9Sstevel@tonic-gate dstlen = 0;
18807c478bd9Sstevel@tonic-gate sp->daylight = 0;
18817c478bd9Sstevel@tonic-gate sp->typecnt = 1;
18827c478bd9Sstevel@tonic-gate sp->timecnt = 0;
18837c478bd9Sstevel@tonic-gate std = &sp->ttis[0];
18847c478bd9Sstevel@tonic-gate std->tt_gmtoff = -stdoff;
18857c478bd9Sstevel@tonic-gate std->tt_isdst = 0;
18867c478bd9Sstevel@tonic-gate }
18877c478bd9Sstevel@tonic-gate
18887c478bd9Sstevel@tonic-gate /* Setup zone name character data for state table */
18897c478bd9Sstevel@tonic-gate sp->charcnt = (int)(stdlen + 1);
18907c478bd9Sstevel@tonic-gate if (dstlen != 0)
18917c478bd9Sstevel@tonic-gate sp->charcnt += dstlen + 1;
18927c478bd9Sstevel@tonic-gate
18937c478bd9Sstevel@tonic-gate /* If bigger than zone name abbv. buffer, grow it */
18947c478bd9Sstevel@tonic-gate if ((size_t)sp->charcnt > sp->charsbuf_size) {
18957c478bd9Sstevel@tonic-gate if ((cp = libc_realloc(sp->chars, sp->charcnt)) == NULL)
18967c478bd9Sstevel@tonic-gate return (-1);
18977c478bd9Sstevel@tonic-gate sp->chars = cp;
18987c478bd9Sstevel@tonic-gate sp->charsbuf_size = sp->charcnt;
18997c478bd9Sstevel@tonic-gate }
19007c478bd9Sstevel@tonic-gate
19017c478bd9Sstevel@tonic-gate /*
19027c478bd9Sstevel@tonic-gate * Copy zone name text null-terminatedly into state table.
19037c478bd9Sstevel@tonic-gate * By doing the copy once during zone loading, setting
19047c478bd9Sstevel@tonic-gate * tzname[] subsequently merely involves setting pointer
19057c478bd9Sstevel@tonic-gate *
19067c478bd9Sstevel@tonic-gate * If either or both std. or alt. zone name < 3 chars,
19077c478bd9Sstevel@tonic-gate * space pad the deficient name(s) to right.
19087c478bd9Sstevel@tonic-gate */
19097c478bd9Sstevel@tonic-gate
19107c478bd9Sstevel@tonic-gate std->tt_abbrind = 0;
19117c478bd9Sstevel@tonic-gate cp = sp->chars;
19127c478bd9Sstevel@tonic-gate (void) strncpy(cp, stdname, stdlen);
19137c478bd9Sstevel@tonic-gate while (stdlen < 3)
19147c478bd9Sstevel@tonic-gate cp[stdlen++] = ' ';
19157c478bd9Sstevel@tonic-gate cp[stdlen] = '\0';
19167c478bd9Sstevel@tonic-gate
19177c478bd9Sstevel@tonic-gate i = (int)(stdlen + 1);
19187c478bd9Sstevel@tonic-gate if (dstlen != 0) {
19197c478bd9Sstevel@tonic-gate dst->tt_abbrind = i;
19207c478bd9Sstevel@tonic-gate cp += i;
19217c478bd9Sstevel@tonic-gate (void) strncpy(cp, dstname, dstlen);
19227c478bd9Sstevel@tonic-gate while (dstlen < 3)
19237c478bd9Sstevel@tonic-gate cp[dstlen++] = ' ';
19247c478bd9Sstevel@tonic-gate cp[dstlen] = '\0';
19257c478bd9Sstevel@tonic-gate }
19267c478bd9Sstevel@tonic-gate
19277c478bd9Sstevel@tonic-gate /* Save default values */
19287c478bd9Sstevel@tonic-gate if (sp->typecnt == 1) {
19297c478bd9Sstevel@tonic-gate sp->default_timezone = stdoff;
19307c478bd9Sstevel@tonic-gate sp->default_altzone = stdoff;
19317c478bd9Sstevel@tonic-gate sp->default_tzname0 = &sp->chars[0];
19327c478bd9Sstevel@tonic-gate sp->default_tzname1 = _tz_spaces;
19337c478bd9Sstevel@tonic-gate } else {
19347c478bd9Sstevel@tonic-gate sp->default_timezone = -std->tt_gmtoff;
19357c478bd9Sstevel@tonic-gate sp->default_altzone = -dst->tt_gmtoff;
19367c478bd9Sstevel@tonic-gate sp->default_tzname0 = &sp->chars[std->tt_abbrind];
19377c478bd9Sstevel@tonic-gate sp->default_tzname1 = &sp->chars[dst->tt_abbrind];
19387c478bd9Sstevel@tonic-gate }
19397c478bd9Sstevel@tonic-gate
19407c478bd9Sstevel@tonic-gate sp->zonerules = zonetype;
19417c478bd9Sstevel@tonic-gate
19427c478bd9Sstevel@tonic-gate return (0);
19437c478bd9Sstevel@tonic-gate }
19447c478bd9Sstevel@tonic-gate
1945d1419d5aSNobutomo Nakano /*
1946d1419d5aSNobutomo Nakano * We loaded the TZDEFAULT which usually the one in US zones. We
1947d1419d5aSNobutomo Nakano * adjust the GMT offset for the zone which has stdoff/dstoff
1948d1419d5aSNobutomo Nakano * offset.
1949d1419d5aSNobutomo Nakano */
1950d1419d5aSNobutomo Nakano static void
adjust_posix_default(state_t * sp,long stdoff,long dstoff)1951d1419d5aSNobutomo Nakano adjust_posix_default(state_t *sp, long stdoff, long dstoff)
1952d1419d5aSNobutomo Nakano {
1953d1419d5aSNobutomo Nakano long zone_stdoff = 0;
1954d1419d5aSNobutomo Nakano long zone_dstoff = 0;
1955d1419d5aSNobutomo Nakano int zone_stdoff_flag = 0;
1956d1419d5aSNobutomo Nakano int zone_dstoff_flag = 0;
1957d1419d5aSNobutomo Nakano int isdst;
1958d1419d5aSNobutomo Nakano int i;
1959d1419d5aSNobutomo Nakano
1960d1419d5aSNobutomo Nakano /*
1961d1419d5aSNobutomo Nakano * Initial values of zone_stdoff and zone_dstoff
1962d1419d5aSNobutomo Nakano */
1963d1419d5aSNobutomo Nakano for (i = 0; (zone_stdoff_flag == 0 || zone_dstoff_flag == 0) &&
1964d1419d5aSNobutomo Nakano i < sp->timecnt; i++) {
1965d1419d5aSNobutomo Nakano ttinfo_t *zone;
1966d1419d5aSNobutomo Nakano
1967d1419d5aSNobutomo Nakano zone = &sp->ttis[sp->types[i]];
1968d1419d5aSNobutomo Nakano
1969d1419d5aSNobutomo Nakano if (zone_stdoff_flag == 0 && zone->tt_isdst == 0) {
1970d1419d5aSNobutomo Nakano zone_stdoff = -zone->tt_gmtoff;
1971d1419d5aSNobutomo Nakano zone_stdoff_flag = 1;
1972d1419d5aSNobutomo Nakano } else if (zone_dstoff_flag == 0 && zone->tt_isdst != 0) {
1973d1419d5aSNobutomo Nakano zone_dstoff = -zone->tt_gmtoff;
1974d1419d5aSNobutomo Nakano zone_dstoff_flag = 1;
1975d1419d5aSNobutomo Nakano }
1976d1419d5aSNobutomo Nakano }
1977d1419d5aSNobutomo Nakano if (zone_dstoff_flag == 0)
1978d1419d5aSNobutomo Nakano zone_dstoff = zone_stdoff;
1979d1419d5aSNobutomo Nakano
1980d1419d5aSNobutomo Nakano /*
1981d1419d5aSNobutomo Nakano * Initially we're assumed to be in standard time.
1982d1419d5aSNobutomo Nakano */
1983d1419d5aSNobutomo Nakano isdst = 0;
1984d1419d5aSNobutomo Nakano
1985d1419d5aSNobutomo Nakano for (i = 0; i < sp->timecnt; i++) {
1986d1419d5aSNobutomo Nakano ttinfo_t *zone;
1987d1419d5aSNobutomo Nakano int next_isdst;
1988d1419d5aSNobutomo Nakano
1989d1419d5aSNobutomo Nakano zone = &sp->ttis[sp->types[i]];
1990d1419d5aSNobutomo Nakano next_isdst = zone->tt_isdst;
1991d1419d5aSNobutomo Nakano
1992d1419d5aSNobutomo Nakano sp->types[i] = next_isdst ? 0 : 1;
1993d1419d5aSNobutomo Nakano
1994d1419d5aSNobutomo Nakano if (zone->tt_ttisgmt == 0) {
1995d1419d5aSNobutomo Nakano /*
1996d1419d5aSNobutomo Nakano * If summer time is in effect, and the transition time
1997d1419d5aSNobutomo Nakano * was not specified as standard time, add the summer
1998d1419d5aSNobutomo Nakano * time offset to the transition time;
1999d1419d5aSNobutomo Nakano * otherwise, add the standard time offset to the
2000d1419d5aSNobutomo Nakano * transition time.
2001d1419d5aSNobutomo Nakano */
2002d1419d5aSNobutomo Nakano /*
2003d1419d5aSNobutomo Nakano * Transitions from DST to DDST will effectively
2004d1419d5aSNobutomo Nakano * disappear since POSIX provides for only one DST
2005d1419d5aSNobutomo Nakano * offset.
2006d1419d5aSNobutomo Nakano */
2007d1419d5aSNobutomo Nakano if (isdst != 0 && zone->tt_ttisstd == 0)
2008d1419d5aSNobutomo Nakano sp->ats[i] += dstoff - zone_dstoff;
2009d1419d5aSNobutomo Nakano else
2010d1419d5aSNobutomo Nakano sp->ats[i] += stdoff - zone_stdoff;
2011d1419d5aSNobutomo Nakano }
2012d1419d5aSNobutomo Nakano if (next_isdst != 0)
2013d1419d5aSNobutomo Nakano zone_dstoff = -zone->tt_gmtoff;
2014d1419d5aSNobutomo Nakano else
2015d1419d5aSNobutomo Nakano zone_stdoff = -zone->tt_gmtoff;
2016d1419d5aSNobutomo Nakano isdst = next_isdst;
2017d1419d5aSNobutomo Nakano }
2018d1419d5aSNobutomo Nakano /*
2019d1419d5aSNobutomo Nakano * Finally, fill in ttis.
2020d1419d5aSNobutomo Nakano * ttisstd and ttisgmt need not be handled.
2021d1419d5aSNobutomo Nakano */
2022d1419d5aSNobutomo Nakano sp->ttis[0].tt_gmtoff = -dstoff;
2023d1419d5aSNobutomo Nakano sp->ttis[0].tt_isdst = 1;
2024d1419d5aSNobutomo Nakano sp->ttis[1].tt_gmtoff = -stdoff;
2025d1419d5aSNobutomo Nakano sp->ttis[1].tt_isdst = 0;
2026d1419d5aSNobutomo Nakano sp->typecnt = 2;
2027d1419d5aSNobutomo Nakano sp->daylight = 1;
2028d1419d5aSNobutomo Nakano }
2029d1419d5aSNobutomo Nakano
2030d1419d5aSNobutomo Nakano /*
2031d1419d5aSNobutomo Nakano *
2032d1419d5aSNobutomo Nakano */
2033d1419d5aSNobutomo Nakano static void
load_posix_transitions(state_t * sp,long stdoff,long dstoff,zone_rules_t zonetype)2034d1419d5aSNobutomo Nakano load_posix_transitions(state_t *sp, long stdoff, long dstoff,
2035d1419d5aSNobutomo Nakano zone_rules_t zonetype)
2036d1419d5aSNobutomo Nakano {
2037d1419d5aSNobutomo Nakano ttinfo_t *std, *dst;
2038d1419d5aSNobutomo Nakano time_t *tranp;
2039d1419d5aSNobutomo Nakano uchar_t *typep;
2040d1419d5aSNobutomo Nakano prev_t *prevp;
2041d1419d5aSNobutomo Nakano int year;
2042d1419d5aSNobutomo Nakano int i;
2043d1419d5aSNobutomo Nakano long long janfirst;
2044d1419d5aSNobutomo Nakano posix_daylight_t pdaylight;
2045d1419d5aSNobutomo Nakano
2046d1419d5aSNobutomo Nakano /*
2047d1419d5aSNobutomo Nakano * We know STD and DST zones are specified with this timezone
2048d1419d5aSNobutomo Nakano * therefore the cache will be set up with 2 transitions per
2049d1419d5aSNobutomo Nakano * year transitioning to their respective std and dst zones.
2050d1419d5aSNobutomo Nakano */
2051d1419d5aSNobutomo Nakano sp->daylight = 1;
2052d1419d5aSNobutomo Nakano sp->typecnt = 2;
2053d1419d5aSNobutomo Nakano sp->timecnt = 272;
2054d1419d5aSNobutomo Nakano
2055d1419d5aSNobutomo Nakano /*
2056d1419d5aSNobutomo Nakano * Insert zone data from POSIX TZ into state table
2057d1419d5aSNobutomo Nakano * The Olson public domain POSIX code sets up ttis[0] to be DST,
2058d1419d5aSNobutomo Nakano * as we are doing here. It seems to be the correct behavior.
2059d1419d5aSNobutomo Nakano * The US/Pacific zoneinfo file also lists DST as first type.
2060d1419d5aSNobutomo Nakano */
2061d1419d5aSNobutomo Nakano
2062d1419d5aSNobutomo Nakano dst = &sp->ttis[0];
2063d1419d5aSNobutomo Nakano dst->tt_gmtoff = -dstoff;
2064d1419d5aSNobutomo Nakano dst->tt_isdst = 1;
2065d1419d5aSNobutomo Nakano
2066d1419d5aSNobutomo Nakano std = &sp->ttis[1];
2067d1419d5aSNobutomo Nakano std->tt_gmtoff = -stdoff;
2068d1419d5aSNobutomo Nakano std->tt_isdst = 0;
2069d1419d5aSNobutomo Nakano
2070d1419d5aSNobutomo Nakano sp->prev[0].std = NULL;
2071d1419d5aSNobutomo Nakano sp->prev[0].alt = NULL;
2072d1419d5aSNobutomo Nakano
2073d1419d5aSNobutomo Nakano /* Create transition data based on POSIX TZ */
2074d1419d5aSNobutomo Nakano tranp = sp->ats;
2075d1419d5aSNobutomo Nakano prevp = &sp->prev[1];
2076d1419d5aSNobutomo Nakano typep = sp->types;
2077d1419d5aSNobutomo Nakano
2078d1419d5aSNobutomo Nakano /*
2079d1419d5aSNobutomo Nakano * We only cache from 1902 to 2037 to avoid transistions
2080d1419d5aSNobutomo Nakano * that wrap at the 32-bit boundries, since 1901 and 2038
2081d1419d5aSNobutomo Nakano * are not full years in 32-bit time. The rough edges
2082d1419d5aSNobutomo Nakano * will be handled as transition cache misses.
2083d1419d5aSNobutomo Nakano */
2084d1419d5aSNobutomo Nakano
2085d1419d5aSNobutomo Nakano janfirst = JAN_01_1902;
2086d1419d5aSNobutomo Nakano
2087d1419d5aSNobutomo Nakano pdaylight.rules[0] = &sp->start_rule;
2088d1419d5aSNobutomo Nakano pdaylight.rules[1] = &sp->end_rule;
2089d1419d5aSNobutomo Nakano pdaylight.offset[0] = stdoff;
2090d1419d5aSNobutomo Nakano pdaylight.offset[1] = dstoff;
2091d1419d5aSNobutomo Nakano
2092d1419d5aSNobutomo Nakano for (i = MAX_RULE_TABLE; i >= 0; i--) {
2093d1419d5aSNobutomo Nakano if (zonetype == POSIX_USA) {
2094d1419d5aSNobutomo Nakano pdaylight.rules[0] = (rule_t *)&__usa_rules[i].start;
2095d1419d5aSNobutomo Nakano pdaylight.rules[1] = (rule_t *)&__usa_rules[i].end;
2096d1419d5aSNobutomo Nakano }
2097d1419d5aSNobutomo Nakano for (year = __usa_rules[i].s_year;
2098d1419d5aSNobutomo Nakano year <= __usa_rules[i].e_year; year++) {
2099d1419d5aSNobutomo Nakano int idx, ridx;
2100d1419d5aSNobutomo Nakano idx = posix_daylight(&janfirst, year, &pdaylight);
2101d1419d5aSNobutomo Nakano ridx = !idx;
2102d1419d5aSNobutomo Nakano
2103d1419d5aSNobutomo Nakano /*
2104d1419d5aSNobutomo Nakano * Two transitions per year. Since there are
2105d1419d5aSNobutomo Nakano * only two zone types for this POSIX zone,
2106d1419d5aSNobutomo Nakano * previous std and alt are always set to
2107d1419d5aSNobutomo Nakano * &ttis[0] and &ttis[1].
2108d1419d5aSNobutomo Nakano */
2109d1419d5aSNobutomo Nakano *tranp++ = (time_t)pdaylight.rtime[idx];
2110d1419d5aSNobutomo Nakano *typep++ = idx;
2111d1419d5aSNobutomo Nakano prevp->std = std;
2112d1419d5aSNobutomo Nakano prevp->alt = dst;
2113d1419d5aSNobutomo Nakano ++prevp;
2114d1419d5aSNobutomo Nakano
2115d1419d5aSNobutomo Nakano *tranp++ = (time_t)pdaylight.rtime[ridx];
2116d1419d5aSNobutomo Nakano *typep++ = ridx;
2117d1419d5aSNobutomo Nakano prevp->std = std;
2118d1419d5aSNobutomo Nakano prevp->alt = dst;
2119d1419d5aSNobutomo Nakano ++prevp;
2120d1419d5aSNobutomo Nakano }
2121d1419d5aSNobutomo Nakano }
2122d1419d5aSNobutomo Nakano }
21237c478bd9Sstevel@tonic-gate
21247c478bd9Sstevel@tonic-gate /*
21257c478bd9Sstevel@tonic-gate * Given a pointer into a time zone string, scan until a character that is not
21267c478bd9Sstevel@tonic-gate * a valid character in a zone name is found. Return ptr to that character.
21277c478bd9Sstevel@tonic-gate * Return NULL if error (ie. non-printable character located in name)
21287c478bd9Sstevel@tonic-gate */
21297c478bd9Sstevel@tonic-gate static const char *
getzname(const char * strp,int quoted)21307c478bd9Sstevel@tonic-gate getzname(const char *strp, int quoted)
21317c478bd9Sstevel@tonic-gate {
21327c478bd9Sstevel@tonic-gate char c;
21337c478bd9Sstevel@tonic-gate
21347c478bd9Sstevel@tonic-gate if (quoted) {
21357c478bd9Sstevel@tonic-gate while ((c = *strp) != '\0' && c != '>' &&
2136d1419d5aSNobutomo Nakano isgraph((unsigned char)c)) {
21377c478bd9Sstevel@tonic-gate ++strp;
2138d1419d5aSNobutomo Nakano }
21397c478bd9Sstevel@tonic-gate } else {
21407c478bd9Sstevel@tonic-gate while ((c = *strp) != '\0' && isgraph((unsigned char)c) &&
21417c478bd9Sstevel@tonic-gate !isdigit((unsigned char)c) && c != ',' && c != '-' &&
2142d1419d5aSNobutomo Nakano c != '+') {
21437c478bd9Sstevel@tonic-gate ++strp;
21447c478bd9Sstevel@tonic-gate }
2145d1419d5aSNobutomo Nakano }
21467c478bd9Sstevel@tonic-gate
21477c478bd9Sstevel@tonic-gate /* Found an excessively invalid character. Discredit whole name */
21487c478bd9Sstevel@tonic-gate if (c != '\0' && !isgraph((unsigned char)c))
21497c478bd9Sstevel@tonic-gate return (NULL);
21507c478bd9Sstevel@tonic-gate
21517c478bd9Sstevel@tonic-gate return (strp);
21527c478bd9Sstevel@tonic-gate }
21537c478bd9Sstevel@tonic-gate
21547c478bd9Sstevel@tonic-gate /*
21557c478bd9Sstevel@tonic-gate * Given pointer into time zone string, extract first
21567c478bd9Sstevel@tonic-gate * number pointed to. Validate number within range specified,
21577c478bd9Sstevel@tonic-gate * Return ptr to first char following valid numeric sequence.
21587c478bd9Sstevel@tonic-gate */
21597c478bd9Sstevel@tonic-gate static const char *
getnum(const char * strp,int * nump,int min,int max)21607c478bd9Sstevel@tonic-gate getnum(const char *strp, int *nump, int min, int max)
21617c478bd9Sstevel@tonic-gate {
21627c478bd9Sstevel@tonic-gate char c;
21637c478bd9Sstevel@tonic-gate int num;
21647c478bd9Sstevel@tonic-gate
21657c478bd9Sstevel@tonic-gate if (strp == NULL || !isdigit((unsigned char)(c = *strp)))
21667c478bd9Sstevel@tonic-gate return (NULL);
21677c478bd9Sstevel@tonic-gate num = 0;
21687c478bd9Sstevel@tonic-gate do {
21697c478bd9Sstevel@tonic-gate num = num * 10 + (c - '0');
21707c478bd9Sstevel@tonic-gate if (num > max)
21717c478bd9Sstevel@tonic-gate return (NULL); /* illegal value */
21727c478bd9Sstevel@tonic-gate c = *++strp;
21737c478bd9Sstevel@tonic-gate } while (isdigit((unsigned char)c));
21747c478bd9Sstevel@tonic-gate if (num < min)
21757c478bd9Sstevel@tonic-gate return (NULL); /* illegal value */
21767c478bd9Sstevel@tonic-gate *nump = num;
21777c478bd9Sstevel@tonic-gate return (strp);
21787c478bd9Sstevel@tonic-gate }
21797c478bd9Sstevel@tonic-gate
21807c478bd9Sstevel@tonic-gate /*
21817c478bd9Sstevel@tonic-gate * Given a pointer into a time zone string, extract a number of seconds,
21827c478bd9Sstevel@tonic-gate * in hh[:mm[:ss]] form, from the string. If an error occurs, return NULL,
21837c478bd9Sstevel@tonic-gate * otherwise, return a pointer to the first character not part of the number
21847c478bd9Sstevel@tonic-gate * of seconds.
21857c478bd9Sstevel@tonic-gate */
21867c478bd9Sstevel@tonic-gate static const char *
getsecs(const char * strp,long * secsp)21877c478bd9Sstevel@tonic-gate getsecs(const char *strp, long *secsp)
21887c478bd9Sstevel@tonic-gate {
21897c478bd9Sstevel@tonic-gate int num;
21907c478bd9Sstevel@tonic-gate
21917c478bd9Sstevel@tonic-gate /*
21927c478bd9Sstevel@tonic-gate * `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
21937c478bd9Sstevel@tonic-gate * "M10.4.6/26", which does not conform to Posix,
21947c478bd9Sstevel@tonic-gate * but which specifies the equivalent of
21957c478bd9Sstevel@tonic-gate * ``02:00 on the first Sunday on or after 23 Oct''.
21967c478bd9Sstevel@tonic-gate */
21977c478bd9Sstevel@tonic-gate strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
21987c478bd9Sstevel@tonic-gate if (strp == NULL)
21997c478bd9Sstevel@tonic-gate return (NULL);
22007c478bd9Sstevel@tonic-gate *secsp = num * (long)SECSPERHOUR;
22017c478bd9Sstevel@tonic-gate if (*strp == ':') {
22027c478bd9Sstevel@tonic-gate ++strp;
22037c478bd9Sstevel@tonic-gate strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
22047c478bd9Sstevel@tonic-gate if (strp == NULL)
22057c478bd9Sstevel@tonic-gate return (NULL);
22067c478bd9Sstevel@tonic-gate *secsp += num * SECSPERMIN;
22077c478bd9Sstevel@tonic-gate if (*strp == ':') {
22087c478bd9Sstevel@tonic-gate ++strp;
22097c478bd9Sstevel@tonic-gate /* `SECSPERMIN' allows for leap seconds. */
22107c478bd9Sstevel@tonic-gate strp = getnum(strp, &num, 0, SECSPERMIN);
22117c478bd9Sstevel@tonic-gate if (strp == NULL)
22127c478bd9Sstevel@tonic-gate return (NULL);
22137c478bd9Sstevel@tonic-gate *secsp += num;
22147c478bd9Sstevel@tonic-gate }
22157c478bd9Sstevel@tonic-gate }
22167c478bd9Sstevel@tonic-gate return (strp);
22177c478bd9Sstevel@tonic-gate }
22187c478bd9Sstevel@tonic-gate
22197c478bd9Sstevel@tonic-gate /*
22207c478bd9Sstevel@tonic-gate * Given a pointer into a time zone string, extract an offset, in
22217c478bd9Sstevel@tonic-gate * [+-]hh[:mm[:ss]] form, from the string.
22227c478bd9Sstevel@tonic-gate * If any error occurs, return NULL.
22237c478bd9Sstevel@tonic-gate * Otherwise, return a pointer to the first character not part of the time.
22247c478bd9Sstevel@tonic-gate */
22257c478bd9Sstevel@tonic-gate static const char *
getoffset(const char * strp,long * offsetp)22267c478bd9Sstevel@tonic-gate getoffset(const char *strp, long *offsetp)
22277c478bd9Sstevel@tonic-gate {
22287c478bd9Sstevel@tonic-gate int neg = 0;
22297c478bd9Sstevel@tonic-gate
22307c478bd9Sstevel@tonic-gate if (*strp == '-') {
22317c478bd9Sstevel@tonic-gate neg = 1;
22327c478bd9Sstevel@tonic-gate ++strp;
22337c478bd9Sstevel@tonic-gate } else if (*strp == '+') {
22347c478bd9Sstevel@tonic-gate ++strp;
22357c478bd9Sstevel@tonic-gate }
22367c478bd9Sstevel@tonic-gate strp = getsecs(strp, offsetp);
22377c478bd9Sstevel@tonic-gate if (strp == NULL)
22387c478bd9Sstevel@tonic-gate return (NULL); /* illegal time */
22397c478bd9Sstevel@tonic-gate if (neg)
22407c478bd9Sstevel@tonic-gate *offsetp = -*offsetp;
22417c478bd9Sstevel@tonic-gate return (strp);
22427c478bd9Sstevel@tonic-gate }
22437c478bd9Sstevel@tonic-gate
22447c478bd9Sstevel@tonic-gate /*
22457c478bd9Sstevel@tonic-gate * Given a pointer into a time zone string, extract a rule in the form
22467c478bd9Sstevel@tonic-gate * date[/time]. See POSIX section 8 for the format of "date" and "time".
22477c478bd9Sstevel@tonic-gate * If a valid rule is not found, return NULL.
22487c478bd9Sstevel@tonic-gate * Otherwise, return a pointer to the first character not part of the rule.
22497c478bd9Sstevel@tonic-gate *
22507c478bd9Sstevel@tonic-gate * If compat_flag is set, support old 1-based day of year values.
22517c478bd9Sstevel@tonic-gate */
22527c478bd9Sstevel@tonic-gate static const char *
getrule(const char * strp,rule_t * rulep,int compat_flag)22537c478bd9Sstevel@tonic-gate getrule(const char *strp, rule_t *rulep, int compat_flag)
22547c478bd9Sstevel@tonic-gate {
22557c478bd9Sstevel@tonic-gate if (compat_flag == 0 && *strp == 'M') {
22567c478bd9Sstevel@tonic-gate /*
22577c478bd9Sstevel@tonic-gate * Month, week, day.
22587c478bd9Sstevel@tonic-gate */
22597c478bd9Sstevel@tonic-gate rulep->r_type = MON_WEEK_DOW;
22607c478bd9Sstevel@tonic-gate ++strp;
22617c478bd9Sstevel@tonic-gate strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
22627c478bd9Sstevel@tonic-gate if (strp == NULL)
22637c478bd9Sstevel@tonic-gate return (NULL);
22647c478bd9Sstevel@tonic-gate if (*strp++ != '.')
22657c478bd9Sstevel@tonic-gate return (NULL);
22667c478bd9Sstevel@tonic-gate strp = getnum(strp, &rulep->r_week, 1, 5);
22677c478bd9Sstevel@tonic-gate if (strp == NULL)
22687c478bd9Sstevel@tonic-gate return (NULL);
22697c478bd9Sstevel@tonic-gate if (*strp++ != '.')
22707c478bd9Sstevel@tonic-gate return (NULL);
22717c478bd9Sstevel@tonic-gate strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
22727c478bd9Sstevel@tonic-gate } else if (compat_flag == 0 && *strp == 'J') {
22737c478bd9Sstevel@tonic-gate /*
22747c478bd9Sstevel@tonic-gate * Julian day.
22757c478bd9Sstevel@tonic-gate */
22767c478bd9Sstevel@tonic-gate rulep->r_type = JULIAN_DAY;
22777c478bd9Sstevel@tonic-gate ++strp;
22787c478bd9Sstevel@tonic-gate strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
22797c478bd9Sstevel@tonic-gate
22807c478bd9Sstevel@tonic-gate } else if (isdigit((unsigned char)*strp)) {
22817c478bd9Sstevel@tonic-gate /*
22827c478bd9Sstevel@tonic-gate * Day of year.
22837c478bd9Sstevel@tonic-gate */
22847c478bd9Sstevel@tonic-gate rulep->r_type = DAY_OF_YEAR;
22857c478bd9Sstevel@tonic-gate if (compat_flag == 0) {
22867c478bd9Sstevel@tonic-gate /* zero-based day of year */
22877c478bd9Sstevel@tonic-gate strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
22887c478bd9Sstevel@tonic-gate } else {
22897c478bd9Sstevel@tonic-gate /* one-based day of year */
22907c478bd9Sstevel@tonic-gate strp = getnum(strp, &rulep->r_day, 1, DAYSPERLYEAR);
22917c478bd9Sstevel@tonic-gate rulep->r_day--;
22927c478bd9Sstevel@tonic-gate }
22937c478bd9Sstevel@tonic-gate } else {
22947c478bd9Sstevel@tonic-gate return (NULL); /* ZONERULES_INVALID format */
22957c478bd9Sstevel@tonic-gate }
22967c478bd9Sstevel@tonic-gate if (strp == NULL)
22977c478bd9Sstevel@tonic-gate return (NULL);
22987c478bd9Sstevel@tonic-gate if (*strp == '/') {
22997c478bd9Sstevel@tonic-gate /*
23007c478bd9Sstevel@tonic-gate * Time specified.
23017c478bd9Sstevel@tonic-gate */
23027c478bd9Sstevel@tonic-gate ++strp;
23037c478bd9Sstevel@tonic-gate strp = getsecs(strp, &rulep->r_time);
23047c478bd9Sstevel@tonic-gate } else {
23057c478bd9Sstevel@tonic-gate rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
23067c478bd9Sstevel@tonic-gate }
23077c478bd9Sstevel@tonic-gate return (strp);
23087c478bd9Sstevel@tonic-gate }
23097c478bd9Sstevel@tonic-gate
23107c478bd9Sstevel@tonic-gate /*
23117c478bd9Sstevel@tonic-gate * Returns default value for TZ as specified in /etc/default/init file, if
23127c478bd9Sstevel@tonic-gate * a default value for TZ is provided there.
23137c478bd9Sstevel@tonic-gate */
23147c478bd9Sstevel@tonic-gate static char *
get_default_tz(void)23157c478bd9Sstevel@tonic-gate get_default_tz(void)
23167c478bd9Sstevel@tonic-gate {
231706e1a714Sraf char *tz = NULL;
231806e1a714Sraf uchar_t *tzp, *tzq;
231906e1a714Sraf int flags;
2320b9175c69SKenjiro Tsuji void *defp;
23217c478bd9Sstevel@tonic-gate
2322d1419d5aSNobutomo Nakano assert_no_libc_locks_held();
2323d1419d5aSNobutomo Nakano
2324b9175c69SKenjiro Tsuji if ((defp = defopen_r(TIMEZONE)) != NULL) {
2325b9175c69SKenjiro Tsuji flags = defcntl_r(DC_GETFLAGS, 0, defp);
232606e1a714Sraf TURNON(flags, DC_STRIP_QUOTES);
2327b9175c69SKenjiro Tsuji (void) defcntl_r(DC_SETFLAGS, flags, defp);
23287c478bd9Sstevel@tonic-gate
2329b9175c69SKenjiro Tsuji if ((tzp = (uchar_t *)defread_r(TZSTRING, defp)) != NULL) {
23307c478bd9Sstevel@tonic-gate while (isspace(*tzp))
23317c478bd9Sstevel@tonic-gate tzp++;
23327c478bd9Sstevel@tonic-gate tzq = tzp;
233306e1a714Sraf while (!isspace(*tzq) &&
23347c478bd9Sstevel@tonic-gate *tzq != ';' &&
23357c478bd9Sstevel@tonic-gate *tzq != '#' &&
233606e1a714Sraf *tzq != '\0')
23377c478bd9Sstevel@tonic-gate tzq++;
23387c478bd9Sstevel@tonic-gate *tzq = '\0';
233906e1a714Sraf if (*tzp != '\0')
2340d1419d5aSNobutomo Nakano tz = libc_strdup((char *)tzp);
23417c478bd9Sstevel@tonic-gate }
234206e1a714Sraf
2343b9175c69SKenjiro Tsuji defclose_r(defp);
23447c478bd9Sstevel@tonic-gate }
23457c478bd9Sstevel@tonic-gate return (tz);
23467c478bd9Sstevel@tonic-gate }
23477c478bd9Sstevel@tonic-gate
2348d1419d5aSNobutomo Nakano /*
2349d1419d5aSNobutomo Nakano * Purge all cache'd state_t
2350d1419d5aSNobutomo Nakano */
2351d1419d5aSNobutomo Nakano static void
purge_zone_cache(void)2352d1419d5aSNobutomo Nakano purge_zone_cache(void)
23537c478bd9Sstevel@tonic-gate {
23547c478bd9Sstevel@tonic-gate int hashid;
2355d1419d5aSNobutomo Nakano state_t *p, *n, *r;
23567c478bd9Sstevel@tonic-gate
2357d1419d5aSNobutomo Nakano /*
2358d1419d5aSNobutomo Nakano * Create a single list of caches which are detached
2359d1419d5aSNobutomo Nakano * from hash table.
2360d1419d5aSNobutomo Nakano */
2361d1419d5aSNobutomo Nakano r = NULL;
2362d1419d5aSNobutomo Nakano for (hashid = 0; hashid < HASHTABLE; hashid++) {
2363d1419d5aSNobutomo Nakano for (p = tzcache[hashid]; p != NULL; p = n) {
2364d1419d5aSNobutomo Nakano n = p->next;
2365d1419d5aSNobutomo Nakano p->next = r;
2366d1419d5aSNobutomo Nakano r = p;
23677c478bd9Sstevel@tonic-gate }
2368d1419d5aSNobutomo Nakano tzcache[hashid] = NULL;
23697c478bd9Sstevel@tonic-gate }
2370d1419d5aSNobutomo Nakano namecache = NULL;
23717c478bd9Sstevel@tonic-gate
2372d1419d5aSNobutomo Nakano /* last_tzname[] may point cache being freed */
2373d1419d5aSNobutomo Nakano last_tzname[0] = NULL;
2374d1419d5aSNobutomo Nakano last_tzname[1] = NULL;
2375d1419d5aSNobutomo Nakano
2376d1419d5aSNobutomo Nakano /* We'll reload system TZ as well */
2377d1419d5aSNobutomo Nakano systemTZ = NULL;
2378d1419d5aSNobutomo Nakano
2379d1419d5aSNobutomo Nakano /*
2380d1419d5aSNobutomo Nakano * Hash table has been cleared, and all elements are detached from
2381d1419d5aSNobutomo Nakano * the hash table. Now we are safe to release _time_lock.
2382d1419d5aSNobutomo Nakano * We need to unlock _time_lock because we need to call out to
2383d1419d5aSNobutomo Nakano * free().
2384d1419d5aSNobutomo Nakano */
2385d1419d5aSNobutomo Nakano lmutex_unlock(&_time_lock);
2386d1419d5aSNobutomo Nakano
2387d1419d5aSNobutomo Nakano assert_no_libc_locks_held();
2388d1419d5aSNobutomo Nakano
2389d1419d5aSNobutomo Nakano while (r != NULL) {
2390d1419d5aSNobutomo Nakano n = r->next;
2391d1419d5aSNobutomo Nakano libc_free((char *)r->zonename);
2392d1419d5aSNobutomo Nakano libc_free((char *)r->chars);
2393d1419d5aSNobutomo Nakano free(r);
2394d1419d5aSNobutomo Nakano r = n;
2395d1419d5aSNobutomo Nakano }
2396d1419d5aSNobutomo Nakano
2397d1419d5aSNobutomo Nakano lmutex_lock(&_time_lock);
2398d1419d5aSNobutomo Nakano }
2399d1419d5aSNobutomo Nakano
2400d1419d5aSNobutomo Nakano /*
2401d1419d5aSNobutomo Nakano * When called first time, open the counter device and load
2402d1419d5aSNobutomo Nakano * the initial value. If counter is updated, copy value to
2403d1419d5aSNobutomo Nakano * private memory.
2404d1419d5aSNobutomo Nakano */
2405d1419d5aSNobutomo Nakano static void
reload_counter(void)2406d1419d5aSNobutomo Nakano reload_counter(void)
2407d1419d5aSNobutomo Nakano {
2408d1419d5aSNobutomo Nakano int fd;
2409d1419d5aSNobutomo Nakano caddr_t addr;
2410d1419d5aSNobutomo Nakano
2411d1419d5aSNobutomo Nakano if (zoneinfo_seqadr != &zoneinfo_seqno_init) {
2412d1419d5aSNobutomo Nakano zoneinfo_seqno = *zoneinfo_seqadr;
2413d1419d5aSNobutomo Nakano return;
2414d1419d5aSNobutomo Nakano }
2415d1419d5aSNobutomo Nakano
2416d1419d5aSNobutomo Nakano if ((fd = open(TZSYNC_FILE, O_RDONLY)) < 0)
2417d1419d5aSNobutomo Nakano return;
2418d1419d5aSNobutomo Nakano
2419d1419d5aSNobutomo Nakano addr = mmap(0, sizeof (uint32_t), PROT_READ, MAP_SHARED, fd, 0);
2420d1419d5aSNobutomo Nakano (void) close(fd);
2421d1419d5aSNobutomo Nakano
2422d1419d5aSNobutomo Nakano if (addr == MAP_FAILED)
2423d1419d5aSNobutomo Nakano return;
2424d1419d5aSNobutomo Nakano /*LINTED*/
2425d1419d5aSNobutomo Nakano zoneinfo_seqadr = (uint32_t *)addr;
2426d1419d5aSNobutomo Nakano zoneinfo_seqno = *zoneinfo_seqadr;
24277c478bd9Sstevel@tonic-gate }
24287c478bd9Sstevel@tonic-gate
24297c478bd9Sstevel@tonic-gate /*
24307c478bd9Sstevel@tonic-gate * getsystemTZ() returns the TZ value if it is set in the environment, or
2431d1419d5aSNobutomo Nakano * it returns the system TZ; if the systemTZ has not yet been set, or
2432d1419d5aSNobutomo Nakano * cleared by tzreload, get_default_tz() is called to read the
2433d1419d5aSNobutomo Nakano * /etc/default/init file to get the value.
24347c478bd9Sstevel@tonic-gate */
2435d1419d5aSNobutomo Nakano static const char *
getsystemTZ()2436d1419d5aSNobutomo Nakano getsystemTZ()
24377c478bd9Sstevel@tonic-gate {
2438d1419d5aSNobutomo Nakano tznmlist_t *tzn;
24397c478bd9Sstevel@tonic-gate char *tz;
24407c478bd9Sstevel@tonic-gate
24417c478bd9Sstevel@tonic-gate tz = getenv("TZ");
2442d1419d5aSNobutomo Nakano if (tz != NULL && *tz != '\0')
2443d1419d5aSNobutomo Nakano return ((const char *)tz);
24447c478bd9Sstevel@tonic-gate
2445d1419d5aSNobutomo Nakano if (systemTZ != NULL)
2446d1419d5aSNobutomo Nakano return (systemTZ);
24477c478bd9Sstevel@tonic-gate
2448d1419d5aSNobutomo Nakano /*
2449d1419d5aSNobutomo Nakano * get_default_tz calls out stdio functions via defread.
2450d1419d5aSNobutomo Nakano */
2451d1419d5aSNobutomo Nakano lmutex_unlock(&_time_lock);
24527c478bd9Sstevel@tonic-gate tz = get_default_tz();
24537c478bd9Sstevel@tonic-gate lmutex_lock(&_time_lock);
2454d1419d5aSNobutomo Nakano
2455d1419d5aSNobutomo Nakano if (tz == NULL) {
2456d1419d5aSNobutomo Nakano /* no TZ entry in the file */
2457d1419d5aSNobutomo Nakano systemTZ = _posix_gmt0;
2458d1419d5aSNobutomo Nakano return (systemTZ);
24597c478bd9Sstevel@tonic-gate }
24607c478bd9Sstevel@tonic-gate
24617c478bd9Sstevel@tonic-gate /*
2462d1419d5aSNobutomo Nakano * look up timezone used previously. We will not free the
2463d1419d5aSNobutomo Nakano * old timezone name, because ltzset_u() can release _time_lock
2464d1419d5aSNobutomo Nakano * while it has references to systemTZ (via zonename). If we
2465d1419d5aSNobutomo Nakano * free the systemTZ, the reference via zonename can access
2466d1419d5aSNobutomo Nakano * invalid memory when systemTZ is reset.
24677c478bd9Sstevel@tonic-gate */
2468d1419d5aSNobutomo Nakano for (tzn = systemTZrec; tzn != NULL; tzn = tzn->link) {
2469d1419d5aSNobutomo Nakano if (strcmp(tz, tzn->name) == 0)
2470d1419d5aSNobutomo Nakano break;
2471d1419d5aSNobutomo Nakano }
2472d1419d5aSNobutomo Nakano if (tzn == NULL) {
2473d1419d5aSNobutomo Nakano /* This is new timezone name */
2474d1419d5aSNobutomo Nakano tzn = lmalloc(sizeof (tznmlist_t *) + strlen(tz) + 1);
2475d1419d5aSNobutomo Nakano (void) strcpy(tzn->name, tz);
2476d1419d5aSNobutomo Nakano tzn->link = systemTZrec;
2477d1419d5aSNobutomo Nakano systemTZrec = tzn;
2478d1419d5aSNobutomo Nakano }
2479d1419d5aSNobutomo Nakano
2480d1419d5aSNobutomo Nakano libc_free(tz);
2481d1419d5aSNobutomo Nakano
2482d1419d5aSNobutomo Nakano return (systemTZ = tzn->name);
2483d1419d5aSNobutomo Nakano }
2484d1419d5aSNobutomo Nakano
24857c478bd9Sstevel@tonic-gate /*
2486d1419d5aSNobutomo Nakano * tzname[] is the user visible string which applications may have
2487d1419d5aSNobutomo Nakano * references. Even though TZ was changed, references to the old tzname
2488d1419d5aSNobutomo Nakano * may continue to remain in the application, and those references need
2489d1419d5aSNobutomo Nakano * to be valid. They were valid by our implementation because strings being
2490d1419d5aSNobutomo Nakano * pointed by tzname were never be freed nor altered by the change of TZ.
2491d1419d5aSNobutomo Nakano * However, this will no longer be the case.
2492d1419d5aSNobutomo Nakano *
2493d1419d5aSNobutomo Nakano * state_t is now freed when cache is purged. Therefore, reading string
2494d1419d5aSNobutomo Nakano * from old tzname[] addr may end up with accessing a stale data(freed area).
2495d1419d5aSNobutomo Nakano * To avoid this, we maintain a copy of all timezone name strings which will
2496d1419d5aSNobutomo Nakano * never be freed, and tzname[] will point those copies.
2497d1419d5aSNobutomo Nakano *
24987c478bd9Sstevel@tonic-gate */
2499d1419d5aSNobutomo Nakano static int
set_one_tzname(const char * name,int idx)2500d1419d5aSNobutomo Nakano set_one_tzname(const char *name, int idx)
2501d1419d5aSNobutomo Nakano {
2502d1419d5aSNobutomo Nakano const unsigned char *nm;
2503d1419d5aSNobutomo Nakano int hashid, i;
2504d1419d5aSNobutomo Nakano char *s;
2505d1419d5aSNobutomo Nakano tznmlist_t *tzn;
2506d1419d5aSNobutomo Nakano
2507d1419d5aSNobutomo Nakano if (name == _tz_gmt || name == _tz_spaces) {
2508d1419d5aSNobutomo Nakano tzname[idx] = (char *)name;
2509d1419d5aSNobutomo Nakano return (0);
2510d1419d5aSNobutomo Nakano }
2511d1419d5aSNobutomo Nakano
2512d1419d5aSNobutomo Nakano nm = (const unsigned char *)name;
2513d1419d5aSNobutomo Nakano hashid = (nm[0] * 29 + nm[1] * 3) % TZNMC_SZ;
2514d1419d5aSNobutomo Nakano for (tzn = tznmhash[hashid]; tzn != NULL; tzn = tzn->link) {
2515d1419d5aSNobutomo Nakano s = tzn->name;
2516d1419d5aSNobutomo Nakano /* do the strcmp() */
2517d1419d5aSNobutomo Nakano for (i = 0; s[i] == name[i]; i++) {
2518d1419d5aSNobutomo Nakano if (s[i] == '\0') {
2519d1419d5aSNobutomo Nakano tzname[idx] = tzn->name;
2520d1419d5aSNobutomo Nakano return (0);
2521d1419d5aSNobutomo Nakano }
2522d1419d5aSNobutomo Nakano }
2523d1419d5aSNobutomo Nakano }
2524d1419d5aSNobutomo Nakano /*
2525d1419d5aSNobutomo Nakano * allocate new entry. This entry is never freed, so use lmalloc
2526d1419d5aSNobutomo Nakano */
2527d1419d5aSNobutomo Nakano tzn = lmalloc(sizeof (tznmlist_t *) + strlen(name) + 1);
2528d1419d5aSNobutomo Nakano if (tzn == NULL)
2529d1419d5aSNobutomo Nakano return (1);
2530d1419d5aSNobutomo Nakano
2531d1419d5aSNobutomo Nakano (void) strcpy(tzn->name, name);
2532d1419d5aSNobutomo Nakano
2533d1419d5aSNobutomo Nakano /* link it */
2534d1419d5aSNobutomo Nakano tzn->link = tznmhash[hashid];
2535d1419d5aSNobutomo Nakano tznmhash[hashid] = tzn;
2536d1419d5aSNobutomo Nakano
2537d1419d5aSNobutomo Nakano tzname[idx] = tzn->name;
2538d1419d5aSNobutomo Nakano return (0);
2539d1419d5aSNobutomo Nakano }
2540d1419d5aSNobutomo Nakano
2541d1419d5aSNobutomo Nakano /*
2542d1419d5aSNobutomo Nakano * Set tzname[] after testing parameter to see if we are setting
2543d1419d5aSNobutomo Nakano * same zone name. If we got same address, it should be same zone
2544d1419d5aSNobutomo Nakano * name as tzname[], unless cache have been purged.
2545d1419d5aSNobutomo Nakano * Note, purge_zone_cache() resets last_tzname[].
2546d1419d5aSNobutomo Nakano */
2547d1419d5aSNobutomo Nakano static void
set_tzname(const char ** namep)2548d1419d5aSNobutomo Nakano set_tzname(const char **namep)
2549d1419d5aSNobutomo Nakano {
2550d1419d5aSNobutomo Nakano if (namep[0] != last_tzname[0]) {
2551d1419d5aSNobutomo Nakano if (set_one_tzname(namep[0], 0)) {
2552d1419d5aSNobutomo Nakano tzname[0] = (char *)_tz_gmt;
2553d1419d5aSNobutomo Nakano last_tzname[0] = NULL;
25547c478bd9Sstevel@tonic-gate } else {
2555d1419d5aSNobutomo Nakano last_tzname[0] = namep[0];
25567c478bd9Sstevel@tonic-gate }
2557d1419d5aSNobutomo Nakano }
2558d1419d5aSNobutomo Nakano
2559d1419d5aSNobutomo Nakano if (namep[1] != last_tzname[1]) {
2560d1419d5aSNobutomo Nakano if (set_one_tzname(namep[1], 1)) {
2561d1419d5aSNobutomo Nakano tzname[1] = (char *)_tz_spaces;
2562d1419d5aSNobutomo Nakano last_tzname[1] = NULL;
2563d1419d5aSNobutomo Nakano } else {
2564d1419d5aSNobutomo Nakano last_tzname[1] = namep[1];
2565d1419d5aSNobutomo Nakano }
2566d1419d5aSNobutomo Nakano }
25677c478bd9Sstevel@tonic-gate }
2568