time-util: rework localtime_or_gmtime() into localtime_or_gmtime_usec()
We typically want to deal in usec_t, hence let's change the prototype accordingly, and do proper range checks. Also, make sure are not confused by negative times. Do something similar for mktime_or_timegm(). This is a more comprehensive alternative to #34065 Replaces: #34065
This commit is contained in:
+51
-19
@@ -332,7 +332,6 @@ char* format_timestamp_style(
|
||||
|
||||
struct tm tm;
|
||||
bool utc, us;
|
||||
time_t sec;
|
||||
size_t n;
|
||||
|
||||
assert(buf);
|
||||
@@ -375,9 +374,7 @@ char* format_timestamp_style(
|
||||
return strcpy(buf, xxx[style]);
|
||||
}
|
||||
|
||||
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
|
||||
|
||||
if (!localtime_or_gmtime_r(&sec, &tm, utc))
|
||||
if (localtime_or_gmtime_usec(t, utc, &tm) < 0)
|
||||
return NULL;
|
||||
|
||||
/* Start with the week day */
|
||||
@@ -665,7 +662,6 @@ static int parse_timestamp_impl(
|
||||
unsigned fractional = 0;
|
||||
const char *k;
|
||||
struct tm tm, copy;
|
||||
time_t sec;
|
||||
|
||||
/* Allowed syntaxes:
|
||||
*
|
||||
@@ -778,10 +774,9 @@ static int parse_timestamp_impl(
|
||||
}
|
||||
}
|
||||
|
||||
sec = (time_t) (usec / USEC_PER_SEC);
|
||||
|
||||
if (!localtime_or_gmtime_r(&sec, &tm, utc))
|
||||
return -EINVAL;
|
||||
r = localtime_or_gmtime_usec(usec, utc, &tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
tm.tm_isdst = isdst;
|
||||
|
||||
@@ -939,11 +934,11 @@ from_tm:
|
||||
} else
|
||||
minus = gmtoff * USEC_PER_SEC;
|
||||
|
||||
sec = mktime_or_timegm(&tm, utc);
|
||||
if (sec < 0)
|
||||
return -EINVAL;
|
||||
r = mktime_or_timegm_usec(&tm, utc, &usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
usec = usec_add(sec * USEC_PER_SEC, fractional);
|
||||
usec = usec_add(usec, fractional);
|
||||
|
||||
finish:
|
||||
usec = usec_add(usec, plus);
|
||||
@@ -1625,17 +1620,54 @@ int get_timezone(char **ret) {
|
||||
return strdup_to(ret, e);
|
||||
}
|
||||
|
||||
time_t mktime_or_timegm(struct tm *tm, bool utc) {
|
||||
int mktime_or_timegm_usec(
|
||||
struct tm *tm, /* input + normalized output */
|
||||
bool utc,
|
||||
usec_t *ret) {
|
||||
|
||||
time_t t;
|
||||
|
||||
assert(tm);
|
||||
|
||||
return utc ? timegm(tm) : mktime(tm);
|
||||
if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!)*/
|
||||
return -ERANGE;
|
||||
if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */
|
||||
return -ERANGE;
|
||||
|
||||
/* timegm()/mktime() is a bit weird to use, since it returns -1 in two cases: on error as well as a
|
||||
* valid time indicating one second before the UNIX epoch. Let's treat both cases the same here, and
|
||||
* return -ERANGE for anything negative, since usec_t is unsigned, and we can thus not express
|
||||
* negative times anyway. */
|
||||
|
||||
t = utc ? timegm(tm) : mktime(tm);
|
||||
if (t < 0) /* Refuse negative times and errors */
|
||||
return -ERANGE;
|
||||
if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */
|
||||
return -ERANGE;
|
||||
|
||||
if (ret)
|
||||
*ret = (usec_t) t * USEC_PER_SEC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
|
||||
assert(t);
|
||||
assert(tm);
|
||||
int localtime_or_gmtime_usec(
|
||||
usec_t t,
|
||||
bool utc,
|
||||
struct tm *ret) {
|
||||
|
||||
return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
|
||||
t /= USEC_PER_SEC; /* Round down */
|
||||
if (t > (usec_t) TIME_T_MAX)
|
||||
return -ERANGE;
|
||||
time_t sec = (time_t) t;
|
||||
|
||||
struct tm buf = {};
|
||||
if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf)))
|
||||
return -EINVAL;
|
||||
|
||||
if (ret)
|
||||
*ret = buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t sysconf_clock_ticks_cached(void) {
|
||||
|
||||
Reference in New Issue
Block a user