When parsing %s, the result should be in the local time zone.
authormillert <millert@openbsd.org>
Thu, 2 Mar 2023 16:21:51 +0000 (16:21 +0000)
committermillert <millert@openbsd.org>
Thu, 2 Mar 2023 16:21:51 +0000 (16:21 +0000)
Based on a patch from enh@google.  OK tb@

lib/libc/time/strptime.c

index 0201788..2fae1f4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: strptime.c,v 1.30 2019/05/12 12:49:52 schwarze Exp $ */
+/*     $OpenBSD: strptime.c,v 1.31 2023/03/02 16:21:51 millert Exp $ */
 /*     $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $    */
 /*-
  * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
  */
 
 #include <ctype.h>
+#include <errno.h>
+#include <limits.h>
 #include <locale.h>
-#include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
@@ -72,8 +74,8 @@ static const int mon_lengths[2][MONSPERYEAR] = {
         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
 };
 
-static int _conv_num64(const unsigned char **, int64_t *, int64_t, int64_t);
 static int _conv_num(const unsigned char **, int *, int, int);
+static int epoch_to_tm(const unsigned char **, struct tm *);
 static int leaps_thru_end_of(const int y);
 static char *_strptime(const char *, const char *, struct tm *, int);
 static const u_char *_find_string(const u_char *, int *, const char * const *,
@@ -338,15 +340,10 @@ literal:
                        if (!(_conv_num(&bp, &tm->tm_sec, 0, 60)))
                                return (NULL);
                        break;
-               case 's':       /* Seconds since epoch */
-                       {
-                               int64_t i64;
-                               if (!(_conv_num64(&bp, &i64, 0, INT64_MAX)))
-                                       return (NULL);
-                               if (!gmtime_r(&i64, tm))
-                                       return (NULL);
-                               fields = 0xffff;         /* everything */
-                       }
+               case 's':       /* Seconds since epoch. */
+                       if (!(epoch_to_tm(&bp, tm)))
+                               return (NULL);
+                       fields = 0xffff;         /* everything */
                        break;
                case 'U':       /* The week of year, beginning on sunday. */
                case 'W':       /* The week of year, beginning on monday. */
@@ -610,26 +607,27 @@ _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
 }
 
 static int
-_conv_num64(const unsigned char **buf, int64_t *dest, int64_t llim, int64_t ulim)
+epoch_to_tm(const unsigned char **buf, struct tm *tm)
 {
-       int result = 0;
-       int64_t rulim = ulim;
-
-       if (**buf < '0' || **buf > '9')
-               return (0);
-
-       /* we use rulim to break out of the loop when we run out of digits */
-       do {
-               result *= 10;
-               result += *(*buf)++ - '0';
-               rulim /= 10;
-       } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
-
-       if (result < llim || result > ulim)
-               return (0);
-
-       *dest = result;
-       return (1);
+       int saved_errno = errno;
+       int ret = 0;
+       time_t secs;
+       char *ep;
+
+       errno = 0;
+       secs = strtoll(*buf, &ep, 10);
+       if (*buf == (unsigned char *)ep)
+               goto done;
+       if (secs < 0 ||
+           secs == LLONG_MAX && errno == ERANGE)
+               goto done;
+       if (localtime_r(&secs, tm) == NULL)
+               goto done;
+       ret = 1;
+done:
+       *buf = ep;
+       errno = saved_errno;
+       return (ret);
 }
 
 static const u_char *