#include <string.h>
#include "gettext.h"
-#include "xalloc.h"
#define _(str) gettext (str)
return 0;
}
-/* If *TM0 is the old and *TM1 is the new value of a struct tm after
- passing it to mktime, return true if it's OK that mktime returned T.
- It's not OK if *TM0 has out-of-range members. */
+/* In timezone TZ, if *TM0 is the old and *TM1 is the new value of a
+ struct tm after passing it to mktime_z, return true if it's OK that
+ mktime_z returned T. It's not OK if *TM0 has out-of-range
+ members. */
static bool
-mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
+mktime_ok (timezone_t tz, struct tm const *tm0, struct tm const *tm1, time_t t)
{
+ struct tm ltm;
if (t == (time_t) -1)
{
/* Guard against falsely reporting an error when parsing a
timestamp that happens to equal (time_t) -1, on a host that
supports such a timestamp. */
- tm1 = localtime (&t);
+ tm1 = localtime_rz (tz, &t, <m);
if (!tm1)
return false;
}
see days_to_name(), debug_strftime() and debug_mktime_not_ok() */
enum { DBGBUFSIZE = 100 };
-/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
- otherwise. */
-static char *
-get_tz (char tzbuf[TZBUFSIZE])
-{
- char *tz = getenv ("TZ");
- if (tz)
- {
- size_t tzsize = strlen (tz) + 1;
- tz = (tzsize <= TZBUFSIZE
- ? memcpy (tzbuf, tz, tzsize)
- : xmemdup (tz, tzsize));
- }
- return tz;
-}
-
/* debugging: format a 'struct tm' into a buffer, taking the parser's
timezone information into account (if pc!=NULL). */
static const char*
/* Returns the effective local timezone, in minutes. */
static long int
-get_effective_timezone (void)
+get_effective_timezone (timezone_t tz)
{
- /* TODO: check for failures */
- const time_t z = 0;
- time_t lz ;
- struct tm *ltm;
- ltm = localtime (&z);
- lz = timegm (ltm)/60;
- return (long int)lz;
+ time_t z = 0;
+ struct tm tm;
+ if (! localtime_rz (tz, &z, &tm))
+ return 0;
+ return timegm (&tm) / 60;
}
-/* The original interface: run with debug=false */
+/* The original interface: run with debug=false and the default timezone. */
bool
parse_datetime (struct timespec *result, char const *p,
struct timespec const *now)
{
- return parse_datetime2 (result, p, now, 0);
+ char const *tzstring = getenv ("TZ");
+ timezone_t tz = tzalloc (tzstring);
+ if (!tz)
+ return false;
+ bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring);
+ tzfree (tz);
+ return ok;
}
/* Parse a date/time string, storing the resulting time value into *RESULT.
The string itself is pointed to by P. Return true if successful.
P can be an incomplete or relative time specification; if so, use
- *NOW as the basis for the returned time. */
+ *NOW as the basis for the returned time. Default to timezone
+ TZDEFAULT, which corresponds to tzalloc (TZSTRING). */
bool
parse_datetime2 (struct timespec *result, char const *p,
- struct timespec const *now, unsigned int flags)
+ struct timespec const *now, unsigned int flags,
+ timezone_t tzdefault, char const *tzstring)
{
time_t Start;
long int Start_ns;
- struct tm const *tmp;
+ struct tm tmp;
struct tm tm;
struct tm tm0;
parser_control pc;
struct timespec gettime_buffer;
unsigned char c;
- bool tz_was_altered = false;
- char *tz0 = NULL;
- char tz0buf[TZBUFSIZE];
+ timezone_t tz = tzdefault;
bool ok = true;
char dbg_ord[DBGBUFSIZE];
char dbg_tm[DBGBUFSIZE];
char const *input_sentinel = p + strlen (p);
+ char *tz1alloc = NULL;
+ char tz1buf[TZBUFSIZE];
if (! now)
{
Start = now->tv_sec;
Start_ns = now->tv_nsec;
- tmp = localtime (&now->tv_sec);
- if (! tmp)
- return false;
-
while (c = *p, c_isspace (c))
p++;
}
else if (*s == '"')
{
+ timezone_t tz1;
+ char *tz1string = tz1buf;
char *z;
- char *tz1;
- char tz1buf[TZBUFSIZE];
- bool large_tz = TZBUFSIZE < tzsize;
- bool setenv_ok;
- tz0 = get_tz (tz0buf);
- z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
+ if (TZBUFSIZE < tzsize)
+ {
+ tz1alloc = malloc (tzsize);
+ if (!tz1alloc)
+ goto fail;
+ tz1string = tz1alloc;
+ }
+ z = tz1string;
for (s = tzbase; *s != '"'; s++)
*z++ = *(s += *s == '\\');
*z = '\0';
- setenv_ok = setenv ("TZ", tz1, 1) == 0;
- if (large_tz)
- free (tz1);
- if (!setenv_ok)
+ tz1 = tzalloc (tz1string);
+ if (!tz1)
goto fail;
- tz_was_altered = true;
+ tz = tz1;
+ tzstring = tz1string;
p = s + 1;
while (c = *p, c_isspace (c))
}
}
+ if (! localtime_rz (tz, &now->tv_sec, &tmp))
+ return false;
+
/* As documented, be careful to treat the empty string just like
a date string of "0". Without this, an empty string would be
declared invalid when parsed during a DST transition. */
p = "0";
pc.input = p;
- pc.year.value = tmp->tm_year;
+ pc.year.value = tmp.tm_year;
pc.year.value += TM_YEAR_BASE;
pc.year.digits = 0;
- pc.month = tmp->tm_mon + 1;
- pc.day = tmp->tm_mday;
- pc.hour = tmp->tm_hour;
- pc.minutes = tmp->tm_min;
- pc.seconds.tv_sec = tmp->tm_sec;
+ pc.month = tmp.tm_mon + 1;
+ pc.day = tmp.tm_mday;
+ pc.hour = tmp.tm_hour;
+ pc.minutes = tmp.tm_min;
+ pc.seconds.tv_sec = tmp.tm_sec;
pc.seconds.tv_nsec = Start_ns;
- tm.tm_isdst = tmp->tm_isdst;
+ tm.tm_isdst = tmp.tm_isdst;
pc.meridian = MER24;
pc.rel = RELATIVE_TIME_0;
pc.debug_default_input_timezone = 0;
#if HAVE_STRUCT_TM_TM_ZONE
- pc.local_time_zone_table[0].name = tmp->tm_zone;
+ pc.local_time_zone_table[0].name = tmp.tm_zone;
pc.local_time_zone_table[0].type = tLOCAL_ZONE;
- pc.local_time_zone_table[0].value = tmp->tm_isdst;
+ pc.local_time_zone_table[0].value = tmp.tm_isdst;
pc.local_time_zone_table[1].name = NULL;
/* Probe the names used in the next three calendar quarters, looking
for (quarter = 1; quarter <= 3; quarter++)
{
time_t probe = Start + quarter * (90 * 24 * 60 * 60);
- struct tm const *probe_tm = localtime (&probe);
- if (probe_tm && probe_tm->tm_zone
- && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
+ struct tm probe_tm;
+ if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
+ && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
{
{
- pc.local_time_zone_table[1].name = probe_tm->tm_zone;
+ pc.local_time_zone_table[1].name = probe_tm.tm_zone;
pc.local_time_zone_table[1].type = tLOCAL_ZONE;
- pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
+ pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
pc.local_time_zone_table[2].name = NULL;
}
break;
pc.local_time_zone_table[1].name = NULL;
}
- pc.debug_default_input_timezone = get_effective_timezone ();
+ pc.debug_default_input_timezone = get_effective_timezone (tz);
if (yyparse (&pc) != 0)
{
/* determine effective timezone source */
if (pc.parse_datetime_debug)
{
- long int tz = pc.debug_default_input_timezone;
- const char* tz_env;
+ long int time_zone = pc.debug_default_input_timezone;
if (pc.timespec_seen)
{
- tz = 0 ;
+ time_zone = 0;
strncpy (dbg_tm, _("'@timespec' - always UTC0"), sizeof (dbg_tm)-1);
}
else if (pc.zones_seen)
{
- tz = pc.time_zone;
+ time_zone = pc.time_zone;
strncpy (dbg_tm, _("parsed date/time string"), sizeof (dbg_tm)-1);
}
- else if ((tz_env = getenv("TZ")))
+ else if (tzstring)
{
- if (tz_was_altered)
+ if (tz != tzdefault)
{
snprintf (dbg_tm, sizeof(dbg_tm), _("TZ=\"%s\" in date string"),
- tz_env);
+ tzstring);
}
- else if (STREQ(tz_env,"UTC0"))
+ else if (STREQ (tzstring, "UTC0"))
{
/* Special case: using 'date -u' simply set TZ=UTC0 */
strncpy (dbg_tm, _("TZ=UTC0 environment value or -u"),
else
{
snprintf (dbg_tm, sizeof(dbg_tm),
- _("TZ=\"%s\" environment value"), tz_env);
+ _("TZ=\"%s\" environment value"), tzstring);
}
}
else
default timezone.*/
if (pc.local_zones_seen && !pc.zones_seen && pc.local_isdst==1)
{
- tz += 60;
+ time_zone += 60;
strncat (dbg_tm, ", dst",
sizeof (dbg_tm) - strlen (dbg_tm) - 1);
}
if (pc.parse_datetime_debug)
dbg_printf (_("input timezone: %+03d:%02d (set from %s)\n"),
- (int)(tz/60), abs ((int)tz)%60, dbg_tm);
+ (int) (time_zone / 60), abs ((int) (time_zone % 60)),
+ dbg_tm);
}
tm0 = tm;
- Start = mktime (&tm);
+ Start = mktime_z (tz, &tm);
- if (! mktime_ok (&tm0, &tm, Start))
+ if (! mktime_ok (tz, &tm0, &tm, Start))
{
if (! pc.zones_seen)
{
long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
long int abs_time_zone_hour = abs_time_zone / 60;
int abs_time_zone_min = abs_time_zone % 60;
- char tz1buf[sizeof "XXX+0:00" + TYPE_WIDTH (pc.time_zone) / 3];
- if (!tz_was_altered)
- tz0 = get_tz (tz0buf);
- sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
+ char tz2buf[sizeof "XXX+0:00" + TYPE_WIDTH (pc.time_zone) / 3];
+ timezone_t tz2;
+ sprintf (tz2buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
abs_time_zone_hour, abs_time_zone_min);
- if (setenv ("TZ", tz1buf, 1) != 0)
+ tz2 = tzalloc (tz2buf);
+ if (!tz2)
{
- /* TODO: was warn () + print errno? */
if (pc.parse_datetime_debug)
- dbg_printf (_("error: setenv('TZ','%s') failed\n"), tz1buf);
+ dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
goto fail;
}
- tz_was_altered = true;
tm = tm0;
- Start = mktime (&tm);
- if (! mktime_ok (&tm0, &tm, Start))
+ Start = mktime_z (tz2, &tm);
+ ok = mktime_ok (tz2, &tm0, &tm, Start);
+ tzfree (tz2);
+ if (! ok)
{
debug_mktime_not_ok (&tm0, &tm, &pc, pc.zones_seen);
- goto fail;
+ goto done;
}
}
}
- (0 < pc.day_ordinal
&& tm.tm_wday != pc.day_number)));
tm.tm_isdst = -1;
- Start = mktime (&tm);
+ Start = mktime_z (tz, &tm);
if (Start == (time_t) -1)
{
if (pc.parse_datetime_debug)
tm.tm_min = tm0.tm_min;
tm.tm_sec = tm0.tm_sec;
tm.tm_isdst = tm0.tm_isdst;
- Start = mktime (&tm);
+ Start = mktime_z (tz, &tm);
if (Start == (time_t) -1)
{
if (pc.parse_datetime_debug)
delta -= tm.tm_gmtoff;
#else
time_t t = Start;
- struct tm const *gmt = gmtime (&t);
- if (! gmt)
+ struct tm gmt;
+ if (! gmtime_r (&t, &gmt))
{
/* TODO: use 'warn(3)' + print errno ? */
if (pc.parse_datetime_debug)
goto fail;
}
- delta -= tm_diff (&tm, gmt);
+ delta -= tm_diff (&tm, &gmt);
#endif
t1 = Start - delta;
if ((Start < t1) != (delta < 0))
if (pc.parse_datetime_debug
&& (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
{
+ struct tm lmt;
dbg_printf (_("after time adjustment (%+ld hours, " \
"%+ld minutes, %+ld seconds, %+ld ns),\n"),
pc.rel.hour,pc.rel.minutes,pc.rel.seconds,pc.rel.ns);
'tm.tm_isdst' contains the date after date adjustment.
*/
- struct tm const *lmt = localtime (&t5);
- if ((tm.tm_isdst!=-1) && (tm.tm_isdst != lmt->tm_isdst))
+ if (tm.tm_isdst != -1 && localtime_rz (tz, &t5, &lmt)
+ && tm.tm_isdst != lmt.tm_isdst)
dbg_printf (_("warning: daylight saving time changed after " \
"time adjustment\n"));
}
fail:
ok = false;
done:
- if (tz_was_altered)
- ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
- if (tz0 != tz0buf)
- free (tz0);
-
if (ok && pc.parse_datetime_debug)
{
- /* print local timezone AFTER restoring TZ (if tz_was_altered)*/
- const long int otz = get_effective_timezone ();
- const char* tz_src;
- const char* tz_env;
+ const long int otz = get_effective_timezone (tz);
+ const char *tz_src;
- if ((tz_env = getenv("TZ")))
+ if (tzstring)
{
/* Special case: using 'date -u' simply set TZ=UTC0 */
- if (STREQ(tz_env,"UTC0"))
+ if (STREQ (tzstring, "UTC0"))
{
tz_src = _("TZ=UTC0 environment value or -u");
}
else
{
snprintf (dbg_tm, sizeof(dbg_tm),
- _("TZ=\"%s\" environment value"), tz_env);
+ _("TZ=\"%s\" environment value"), tzstring);
tz_src = dbg_tm;
}
}
dbg_printf (_("final: %ld.%09ld (epoch-seconds)\n"),
result->tv_sec,result->tv_nsec);
- struct tm const *gmt = gmtime (&result->tv_sec);
- dbg_printf (_("final: %s (UTC0)\n"),
- debug_strfdatetime (gmt, NULL, dbg_tm, sizeof (dbg_tm)));
- struct tm const *lmt = localtime (&result->tv_sec);
- dbg_printf (_("final: %s (output timezone TZ=%+03d:%02d)\n"),
- debug_strfdatetime (lmt, NULL, dbg_tm, sizeof (dbg_tm)),
- (int)(otz/60), abs ((int)otz)%60);
+ struct tm gmt, lmt;
+ if (gmtime_r (&result->tv_sec, &gmt))
+ dbg_printf (_("final: %s (UTC0)\n"),
+ debug_strfdatetime (&gmt, NULL,
+ dbg_tm, sizeof dbg_tm));
+ if (localtime_rz (tz, &result->tv_sec, &lmt))
+ dbg_printf (_("final: %s (output timezone TZ=%+03d:%02d)\n"),
+ debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
+ (int) (otz / 60), abs ((int) (otz % 60)));
}
}
+ if (tz != tzdefault)
+ tzfree (tz);
+ free (tz1alloc);
return ok;
}