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 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 * 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 * 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 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 * 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 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 * 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 * 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 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 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 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 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 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 * 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 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 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 * 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 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 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 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 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 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 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 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 * 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 * 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 * 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 * 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 * 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 * 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 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 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 * 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 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 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