nstime: fix unix_epoch_to_nstime().

The text format for UNIX Epoch times is <secs>[[.,]<nsecs>], with <secs>
representing "seconds since the Epoch".

The format of an nstime is a time_t and a nanoseonds value, where a
time_t is normally "seconds since the Epoch" (don't get me started on
why that's quoted and why I added "normally"; hint, it involves the
words "leap" and "seconds").

So all unix_epoch_to_nstime() needs to do is conveert <secs> from ASCII
decimal to binary and use it as the time_t (if it fits) and, if there's
a [.,]<nsecs>, convert <nsecs> it from ASCII decimal to binary (as an
"after the decimal indicator" value) and use it as the nanoseconds,
otherwise set the nanoseconds to 0.

It does *not*, in particular, have to involve struct tm in any fashion;
doing that runs the risk of dragging in local time, but this isn't local
time.

This also means that it doesn't depend on strptime supporting "%s";
that's not specified in the Single UNIX Specification.
This commit is contained in:
Guy Harris 2023-09-02 16:01:24 -07:00
parent d6dfd89740
commit 9af86689dd
1 changed files with 19 additions and 8 deletions

View File

@ -17,6 +17,7 @@
#include "epochs.h"
#include "time_util.h"
#include "to_str.h"
#include "strtoi.h"
/* this is #defined so that we can clearly see that we have the right number of
zeros, rather than as a guard against the number of nanoseconds in a second
@ -524,24 +525,34 @@ iso8601_to_nstime(nstime_t *nstime, const char *ptr, iso8601_fmt_e format)
guint8
unix_epoch_to_nstime(nstime_t *nstime, const char *ptr)
{
struct tm tm;
char *ptr_new;
gint64 secs;
const char *ptr_new;
gint n_chars = 0;
guint frac = 0;
guint8 ret_val = 0;
const char *start = ptr;
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1;
nstime_set_unset(nstime);
if (!(ptr_new = ws_strptime(ptr, "%s", &tm))) {
/*
* Extract the seconds as a 64-bit signed number, as time_t
* might be 64-bit.
*/
if (!ws_strtoi64(ptr, &ptr_new, &secs)) {
return 0;
}
/* No UTC offset given; ISO 8601 says this means local time */
nstime->secs = mktime(&tm);
/* For now, reject times before the Epoch. */
if (secs < 0) {
return 0;
}
/* Make sure it fits. */
nstime->secs = (time_t) secs;
if (nstime->secs != secs) {
return 0;
}
/* Now let's test for fractional seconds */
if (*ptr_new == '.' || *ptr_new == ',') {
@ -569,7 +580,7 @@ unix_epoch_to_nstime(nstime_t *nstime, const char *ptr)
/* If we didn't get frac, it's still its default of 0 */
}
else {
tm.tm_sec = 0;
frac = 0;
}
nstime->nsecs = frac;