1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1985-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* Phong Vo <kpv@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * time conversion support
1N/A */
1N/A
1N/A#include <tm.h>
1N/A#include <ctype.h>
1N/A#include <namval.h>
1N/A
1N/A#include "FEATURE/tmlib"
1N/A
1N/A#ifndef tzname
1N/A# if defined(__DYNAMIC__)
1N/A# define tzname __DYNAMIC__(tzname)
1N/A# else
1N/A# if !_dat_tzname
1N/A# if _dat__tzname
1N/A# undef _dat_tzname
1N/A# define _dat_tzname 1
1N/A# define tzname _tzname
1N/A# endif
1N/A# endif
1N/A# endif
1N/A# if _dat_tzname
1N/A extern char* tzname[];
1N/A# endif
1N/A#endif
1N/A
1N/A#define TM_type (-1)
1N/A
1N/Astatic const Namval_t options[] =
1N/A{
1N/A "adjust", TM_ADJUST,
1N/A "format", TM_DEFAULT,
1N/A "leap", TM_LEAP,
1N/A "subsecond", TM_SUBSECOND,
1N/A "type", TM_type,
1N/A "utc", TM_UTC,
1N/A 0, 0
1N/A};
1N/A
1N/A/*
1N/A * 2007-03-19 move tm_info from _tm_info_ to (*_tm_infop_)
1N/A * to allow future Tm_info_t growth
1N/A * by 2009 _tm_info_ can be static
1N/A */
1N/A
1N/A#if _BLD_ast && defined(__EXPORT__)
1N/A#define extern extern __EXPORT__
1N/A#endif
1N/A
1N/Aextern Tm_info_t _tm_info_;
1N/A
1N/A#undef extern
1N/A
1N/ATm_info_t _tm_info_ = { 0 };
1N/A
1N/A__EXTERN__(Tm_info_t, _tm_info_);
1N/A
1N/A__EXTERN__(Tm_info_t*, _tm_infop_);
1N/A
1N/ATm_info_t* _tm_infop_ = &_tm_info_;
1N/A
1N/A#if _tzset_environ
1N/A
1N/Astatic char TZ[256];
1N/Astatic char* TE[2];
1N/A
1N/Astruct tm*
1N/A_tm_localtime(const time_t* t)
1N/A{
1N/A struct tm* r;
1N/A char* e;
1N/A char** v = environ;
1N/A
1N/A if (TZ[0])
1N/A {
1N/A if (!environ || !*environ)
1N/A environ = TE;
1N/A else
1N/A e = environ[0];
1N/A environ[0] = TZ;
1N/A }
1N/A r = localtime(t);
1N/A if (TZ[0])
1N/A {
1N/A if (environ != v)
1N/A environ = v;
1N/A else
1N/A environ[0] = e;
1N/A }
1N/A return r;
1N/A}
1N/A
1N/A#endif
1N/A
1N/A/*
1N/A * return minutes west of GMT for local time clock
1N/A *
1N/A * isdst will point to non-zero if DST is in effect
1N/A * this routine also kicks in the local initialization
1N/A */
1N/A
1N/Astatic int
1N/Atzwest(time_t* clock, int* isdst)
1N/A{
1N/A register struct tm* tp;
1N/A register int n;
1N/A register int m;
1N/A int h;
1N/A time_t epoch;
1N/A
1N/A /*
1N/A * convert to GMT assuming local time
1N/A */
1N/A
1N/A if (!(tp = gmtime(clock)))
1N/A {
1N/A /*
1N/A * some systems return 0 for negative time_t
1N/A */
1N/A
1N/A epoch = 0;
1N/A clock = &epoch;
1N/A tp = gmtime(clock);
1N/A }
1N/A n = tp->tm_yday;
1N/A h = tp->tm_hour;
1N/A m = tp->tm_min;
1N/A
1N/A /*
1N/A * tmlocaltime() handles DST and GMT offset
1N/A */
1N/A
1N/A tp = tmlocaltime(clock);
1N/A if (n = tp->tm_yday - n)
1N/A {
1N/A if (n > 1)
1N/A n = -1;
1N/A else if (n < -1)
1N/A n = 1;
1N/A }
1N/A *isdst = tp->tm_isdst;
1N/A return (h - tp->tm_hour - n * 24) * 60 + m - tp->tm_min;
1N/A}
1N/A
1N/A/*
1N/A * stropt() option handler
1N/A */
1N/A
1N/Astatic int
1N/Atmopt(void* a, const void* p, int n, const char* v)
1N/A{
1N/A Tm_zone_t* zp;
1N/A
1N/A NoP(a);
1N/A if (p)
1N/A switch (((Namval_t*)p)->value)
1N/A {
1N/A case TM_DEFAULT:
1N/A tm_info.deformat = (n && (n = strlen(v)) > 0 && (n < 2 || v[n-2] != '%' || v[n-1] != '?')) ? strdup(v) : tm_info.format[TM_DEFAULT];
1N/A break;
1N/A case TM_type:
1N/A tm_info.local->type = (n && *v) ? ((zp = tmtype(v, NiL)) ? zp->type : strdup(v)) : 0;
1N/A break;
1N/A default:
1N/A if (n)
1N/A tm_info.flags |= ((Namval_t*)p)->value;
1N/A else
1N/A tm_info.flags &= ~((Namval_t*)p)->value;
1N/A break;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * initialize the local timezone
1N/A */
1N/A
1N/Astatic void
1N/Atmlocal(void)
1N/A{
1N/A register Tm_zone_t* zp;
1N/A register int n;
1N/A register char* s;
1N/A register char* e;
1N/A int i;
1N/A int m;
1N/A int isdst;
1N/A char* t;
1N/A struct tm* tp;
1N/A time_t now;
1N/A char buf[16];
1N/A
1N/A static Tm_zone_t local;
1N/A
1N/A#if _tzset_environ
1N/A {
1N/A char** v = environ;
1N/A
1N/A if (s = getenv("TZ"))
1N/A {
1N/A sfsprintf(TZ, sizeof(TZ), "TZ=%s", s);
1N/A if (!environ || !*environ)
1N/A environ = TE;
1N/A else
1N/A e = environ[0];
1N/A environ[0] = TZ;
1N/A }
1N/A else
1N/A {
1N/A TZ[0] = 0;
1N/A e = 0;
1N/A }
1N/A#endif
1N/A#if _lib_tzset
1N/A tzset();
1N/A#endif
1N/A#if _tzset_environ
1N/A if (environ != v)
1N/A environ = v;
1N/A else if (e)
1N/A environ[0] = e;
1N/A }
1N/A#endif
1N/A#if _dat_tzname
1N/A local.standard = strdup(tzname[0]);
1N/A local.daylight = strdup(tzname[1]);
1N/A#endif
1N/A tmlocale();
1N/A
1N/A /*
1N/A * tm_info.local
1N/A */
1N/A
1N/A tm_info.zone = tm_info.local = &local;
1N/A time(&now);
1N/A n = tzwest(&now, &isdst);
1N/A
1N/A /*
1N/A * compute local DST offset by roaming
1N/A * through the last 12 months until tzwest() changes
1N/A */
1N/A
1N/A for (i = 0; i < 12; i++)
1N/A {
1N/A now -= 31 * 24 * 60 * 60;
1N/A if ((m = tzwest(&now, &isdst)) != n)
1N/A {
1N/A if (!isdst)
1N/A {
1N/A isdst = n;
1N/A n = m;
1N/A m = isdst;
1N/A }
1N/A m -= n;
1N/A break;
1N/A }
1N/A }
1N/A local.west = n;
1N/A local.dst = m;
1N/A
1N/A /*
1N/A * now get the time zone names
1N/A */
1N/A
1N/A#if _dat_tzname
1N/A if (tzname[0])
1N/A {
1N/A /*
1N/A * POSIX
1N/A */
1N/A
1N/A if (!local.standard)
1N/A local.standard = strdup(tzname[0]);
1N/A if (!local.daylight)
1N/A local.daylight = strdup(tzname[1]);
1N/A }
1N/A else
1N/A#endif
1N/A if ((s = getenv("TZNAME")) && *s && (s = strdup(s)))
1N/A {
1N/A /*
1N/A * BSD
1N/A */
1N/A
1N/A local.standard = s;
1N/A if (s = strchr(s, ','))
1N/A *s++ = 0;
1N/A else
1N/A s = "";
1N/A local.daylight = s;
1N/A }
1N/A else if ((s = getenv("TZ")) && *s && *s != ':' && (s = strdup(s)))
1N/A {
1N/A /*
1N/A * POSIX style but skipped by tmlocaltime()
1N/A */
1N/A
1N/A local.standard = s;
1N/A if (*++s && *++s && *++s)
1N/A {
1N/A *s++ = 0;
1N/A tmgoff(s, &t, 0);
1N/A for (s = t; isalpha(*t); t++);
1N/A *t = 0;
1N/A }
1N/A else
1N/A s = "";
1N/A local.daylight = s;
1N/A }
1N/A else
1N/A {
1N/A /*
1N/A * tm_data.zone table lookup
1N/A */
1N/A
1N/A t = 0;
1N/A for (zp = tm_data.zone; zp->standard; zp++)
1N/A {
1N/A if (zp->type)
1N/A t = zp->type;
1N/A if (zp->west == n && zp->dst == m)
1N/A {
1N/A local.type = t;
1N/A local.standard = zp->standard;
1N/A if (!(s = zp->daylight))
1N/A {
1N/A e = (s = buf) + sizeof(buf);
1N/A s = tmpoff(s, e - s, zp->standard, 0, 0);
1N/A if (s < e - 1)
1N/A {
1N/A *s++ = ' ';
1N/A tmpoff(s, e - s, tm_info.format[TM_DT], m, TM_DST);
1N/A }
1N/A s = strdup(buf);
1N/A }
1N/A local.daylight = s;
1N/A break;
1N/A }
1N/A }
1N/A if (!zp->standard)
1N/A {
1N/A /*
1N/A * not in the table
1N/A */
1N/A
1N/A e = (s = buf) + sizeof(buf);
1N/A s = tmpoff(s, e - s, tm_info.format[TM_UT], n, 0);
1N/A local.standard = strdup(buf);
1N/A if (s < e - 1)
1N/A {
1N/A *s++ = ' ';
1N/A tmpoff(s, e - s, tm_info.format[TM_UT], m, TM_DST);
1N/A local.daylight = strdup(buf);
1N/A }
1N/A }
1N/A }
1N/A
1N/A /*
1N/A * set the options
1N/A */
1N/A
1N/A stropt(getenv("TM_OPTIONS"), options, sizeof(*options), tmopt, NiL);
1N/A
1N/A /*
1N/A * the time zone type is probably related to the locale
1N/A */
1N/A
1N/A if (!local.type)
1N/A {
1N/A s = local.standard;
1N/A t = 0;
1N/A for (zp = tm_data.zone; zp->standard; zp++)
1N/A {
1N/A if (zp->type)
1N/A t = zp->type;
1N/A if (tmword(s, NiL, zp->standard, NiL, 0))
1N/A {
1N/A local.type = t;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A
1N/A /*
1N/A * tm_info.flags
1N/A */
1N/A
1N/A if (!(tm_info.flags & TM_ADJUST))
1N/A {
1N/A now = (time_t)78811200; /* Jun 30 1972 23:59:60 */
1N/A tp = tmlocaltime(&now);
1N/A if (tp->tm_sec != 60)
1N/A tm_info.flags |= TM_ADJUST;
1N/A }
1N/A if (!(tm_info.flags & TM_UTC))
1N/A {
1N/A s = local.standard;
1N/A zp = tm_data.zone;
1N/A if (local.daylight)
1N/A zp++;
1N/A for (; !zp->type && zp->standard; zp++)
1N/A if (tmword(s, NiL, zp->standard, NiL, 0))
1N/A {
1N/A tm_info.flags |= TM_UTC;
1N/A break;
1N/A }
1N/A }
1N/A}
1N/A
1N/A/*
1N/A * initialize tm data
1N/A */
1N/A
1N/Avoid
1N/Atminit(register Tm_zone_t* zp)
1N/A{
1N/A static uint32_t serial = ~(uint32_t)0;
1N/A
1N/A if (serial != ast.env_serial)
1N/A {
1N/A serial = ast.env_serial;
1N/A if (tm_info.local)
1N/A {
1N/A memset(tm_info.local, 0, sizeof(*tm_info.local));
1N/A tm_info.local = 0;
1N/A }
1N/A }
1N/A if (!tm_info.local)
1N/A tmlocal();
1N/A if (!zp)
1N/A zp = tm_info.local;
1N/A tm_info.zone = zp;
1N/A}