summaryrefslogtreecommitdiff
path: root/localtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'localtime.c')
-rw-r--r--localtime.c128
1 files changed, 55 insertions, 73 deletions
diff --git a/localtime.c b/localtime.c
index 818d58f8e7be..940a04fd09ab 100644
--- a/localtime.c
+++ b/localtime.c
@@ -7,7 +7,7 @@
/*
** Leap second handling from Bradley White.
-** POSIX-style TZ environment variable handling from Guy Harris.
+** POSIX.1-1988 style TZ environment variable handling from Guy Harris.
*/
/*LINTLIBRARY*/
@@ -15,6 +15,7 @@
#define LOCALTIME_IMPLEMENTATION
#include "private.h"
+#include "tzdir.h"
#include "tzfile.h"
#include <fcntl.h>
@@ -105,12 +106,17 @@ static char const UNSPEC[] = "-00";
for ttunspecified to work without crashing. */
enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
-/* Limit to time zone abbreviation length in POSIX-style TZ strings.
+/* Limit to time zone abbreviation length in POSIX.1-2017-style TZ strings.
This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */
#ifndef TZNAME_MAXIMUM
# define TZNAME_MAXIMUM 255
#endif
+/* A representation of the contents of a TZif file. Ideally this
+ would have no size limits; the following sizes should suffice for
+ practical use. This struct should not be too large, as instances
+ are put on the stack and stacks are relatively small on some platforms.
+ See tzfile.h for more about the sizes. */
struct state {
int leapcnt;
int timecnt;
@@ -153,8 +159,7 @@ static int_fast32_t leapcorr(struct state const *, time_t);
static bool normalize_overflow32(int_fast32_t *, int *, int);
static struct tm *timesub(time_t const *, int_fast32_t, struct state const *,
struct tm *);
-static bool typesequiv(struct state const *, int, int);
-static bool tzparse(char const *, struct state *, struct state *);
+static bool tzparse(char const *, struct state *, struct state const *);
#ifdef ALL_STATE
static struct state * lclptr;
@@ -369,7 +374,8 @@ union input_buffer {
/* The first part of the buffer, interpreted as a header. */
struct tzhead tzhead;
- /* The entire buffer. */
+ /* The entire buffer. Ideally this would have no size limits;
+ the following should suffice for practical use. */
char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
+ 4 * TZ_MAX_TIMES];
};
@@ -388,7 +394,12 @@ union local_storage {
struct state st;
} u;
- /* The file name to be opened. */
+ /* The name of the file to be opened. Ideally this would have no
+ size limits, to support arbitrarily long Zone names.
+ Limiting Zone names to 1024 bytes should suffice for practical use.
+ However, there is no need for this to be smaller than struct
+ file_analysis as that struct is allocated anyway, as the other
+ union member. */
char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)];
};
@@ -674,14 +685,18 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
== sp->types[sp->timecnt - 2]))
sp->timecnt--;
- for (i = 0;
- i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES;
- i++) {
+ sp->goahead = ts->goahead;
+
+ for (i = 0; i < ts->timecnt; i++) {
time_t t = ts->ats[i];
if (increment_overflow_time(&t, leapcorr(sp, t))
|| (0 < sp->timecnt
&& t <= sp->ats[sp->timecnt - 1]))
continue;
+ if (TZ_MAX_TIMES <= sp->timecnt) {
+ sp->goahead = false;
+ break;
+ }
sp->ats[sp->timecnt] = t;
sp->types[sp->timecnt] = (sp->typecnt
+ ts->types[i]);
@@ -694,28 +709,6 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
}
if (sp->typecnt == 0)
return EINVAL;
- if (sp->timecnt > 1) {
- if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) {
- time_t repeatat = sp->ats[0] + SECSPERREPEAT;
- int repeattype = sp->types[0];
- for (i = 1; i < sp->timecnt; ++i)
- if (sp->ats[i] == repeatat
- && typesequiv(sp, sp->types[i], repeattype)) {
- sp->goback = true;
- break;
- }
- }
- if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) {
- time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT;
- int repeattype = sp->types[sp->timecnt - 1];
- for (i = sp->timecnt - 2; i >= 0; --i)
- if (sp->ats[i] == repeatat
- && typesequiv(sp, sp->types[i], repeattype)) {
- sp->goahead = true;
- break;
- }
- }
- }
/* Infer sp->defaulttype from the data. Although this default
type is always zero for data from recent tzdb releases,
@@ -792,31 +785,6 @@ tzload(char const *name, struct state *sp, bool doextend)
#endif
}
-static bool
-typesequiv(const struct state *sp, int a, int b)
-{
- register bool result;
-
- if (sp == NULL ||
- a < 0 || a >= sp->typecnt ||
- b < 0 || b >= sp->typecnt)
- result = false;
- else {
- /* Compare the relevant members of *AP and *BP.
- Ignore tt_ttisstd and tt_ttisut, as they are
- irrelevant now and counting them could cause
- sp->goahead to mistakenly remain false. */
- register const struct ttinfo * ap = &sp->ttis[a];
- register const struct ttinfo * bp = &sp->ttis[b];
- result = (ap->tt_utoff == bp->tt_utoff
- && ap->tt_isdst == bp->tt_isdst
- && (strcmp(&sp->chars[ap->tt_desigidx],
- &sp->chars[bp->tt_desigidx])
- == 0));
- }
- return result;
-}
-
static const int mon_lengths[2][MONSPERYEAR] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
@@ -913,8 +881,8 @@ getsecs(register const char *strp, int_fast32_t *const secsp)
int_fast32_t secsperhour = SECSPERHOUR;
/*
- ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
- ** "M10.4.6/26", which does not conform to Posix,
+ ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-POSIX rules like
+ ** "M10.4.6/26", which does not conform to POSIX,
** but which specifies the equivalent of
** "02:00 on the first Sunday on or after 23 Oct".
*/
@@ -967,7 +935,8 @@ getoffset(register const char *strp, int_fast32_t *const offsetp)
/*
** Given a pointer into a timezone string, extract a rule in the form
-** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** date[/time]. See POSIX Base Definitions section 8.3 variable TZ
+** for the format of "date" and "time".
** If a valid rule is not found, return NULL.
** Otherwise, return a pointer to the first character not part of the rule.
*/
@@ -1111,12 +1080,12 @@ transtime(const int year, register const struct rule *const rulep,
}
/*
-** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** Given a POSIX.1-2017-style TZ string, fill in the rule tables as
** appropriate.
*/
static bool
-tzparse(const char *name, struct state *sp, struct state *basep)
+tzparse(const char *name, struct state *sp, struct state const *basep)
{
const char * stdname;
const char * dstname;
@@ -1159,6 +1128,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
}
if (0 < sp->leapcnt)
leaplo = sp->lsis[sp->leapcnt - 1].ls_trans;
+ sp->goback = sp->goahead = false;
if (*name != '\0') {
if (*name == '<') {
dstname = ++name;
@@ -1206,7 +1176,6 @@ tzparse(const char *name, struct state *sp, struct state *basep)
*/
init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
- sp->defaulttype = 0;
timecnt = 0;
janfirst = 0;
yearbeg = EPOCH_YEAR;
@@ -1236,7 +1205,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
}
yearlim = yearbeg;
- if (increment_overflow(&yearlim, YEARSPERREPEAT + 1))
+ if (increment_overflow(&yearlim, years_of_observations))
yearlim = INT_MAX;
for (year = yearbeg; year < yearlim; year++) {
int_fast32_t
@@ -1273,7 +1242,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
if (endtime < leaplo) {
yearlim = year;
if (increment_overflow(&yearlim,
- YEARSPERREPEAT + 1))
+ years_of_observations))
yearlim = INT_MAX;
}
if (increment_overflow_time
@@ -1285,7 +1254,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
if (! timecnt) {
sp->ttis[0] = sp->ttis[1];
sp->typecnt = 1; /* Perpetual DST. */
- } else if (YEARSPERREPEAT < year - yearbeg)
+ } else if (years_of_observations <= year - yearbeg)
sp->goback = sp->goahead = true;
} else {
register int_fast32_t theirstdoffset;
@@ -1344,8 +1313,8 @@ tzparse(const char *name, struct state *sp, struct state *basep)
/*
** Transitions from DST to DDST
** will effectively disappear since
- ** POSIX provides for only one DST
- ** offset.
+ ** POSIX.1-2017 provides for only one
+ ** DST offset.
*/
if (isdst && !sp->ttis[j].tt_ttisstd) {
sp->ats[i] += dstoffset -
@@ -1366,15 +1335,14 @@ tzparse(const char *name, struct state *sp, struct state *basep)
init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
sp->typecnt = 2;
- sp->defaulttype = 0;
}
} else {
dstlen = 0;
sp->typecnt = 1; /* only standard time */
sp->timecnt = 0;
init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
- sp->defaulttype = 0;
}
+ sp->defaulttype = 0;
sp->charcnt = charcnt;
cp = sp->chars;
memcpy(cp, stdname, stdlen);
@@ -1516,7 +1484,8 @@ tzfree(timezone_t sp)
**
** If successful and SETNAME is nonzero,
** set the applicable parts of tzname, timezone and altzone;
-** however, it's OK to omit this step if the timezone is POSIX-compatible,
+** however, it's OK to omit this step
+** if the timezone is compatible with POSIX.1-2017
** since in that case tzset should have already done this step correctly.
** SETNAME's type is int_fast32_t for compatibility with gmtsub,
** but it is actually a boolean and its value should be 0 or 1.
@@ -1703,6 +1672,9 @@ gmtime(const time_t *timep)
#if STD_INSPIRED
+/* This function is obsolescent and may disappear in future releases.
+ Callers can instead use localtime_rz with a fixed-offset zone. */
+
struct tm *
offtime(const time_t *timep, long offset)
{
@@ -2313,6 +2285,8 @@ mktime(struct tm *tmp)
}
#if STD_INSPIRED
+/* This function is obsolescent and may disapper in future releases.
+ Callers can instead use mktime. */
time_t
timelocal(struct tm *tmp)
{
@@ -2320,10 +2294,18 @@ timelocal(struct tm *tmp)
tmp->tm_isdst = -1; /* in case it wasn't initialized */
return mktime(tmp);
}
-#else
-static
#endif
-time_t
+
+#ifndef EXTERN_TIMEOFF
+# ifndef timeoff
+# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
+# endif
+# define EXTERN_TIMEOFF static
+#endif
+
+/* This function is obsolescent and may disapper in future releases.
+ Callers can instead use mktime_z with a fixed-offset zone. */
+EXTERN_TIMEOFF time_t
timeoff(struct tm *tmp, long offset)
{
if (tmp)