Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libc/time PR/50009: David CARLIER: Enhance strptime to f...



details:   https://anonhg.NetBSD.org/src/rev/9e152ab07a06
branches:  trunk
changeset: 339192:9e152ab07a06
user:      christos <christos%NetBSD.org@localhost>
date:      Fri Jul 03 13:06:54 2015 +0000

description:
PR/50009: David CARLIER: Enhance strptime to fill out more fields when it
can. From FreeBSD.

diffstat:

 lib/libc/time/strptime.c |  119 ++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 113 insertions(+), 6 deletions(-)

diffs (269 lines):

diff -r 3088e0c861cc -r 9e152ab07a06 lib/libc/time/strptime.c
--- a/lib/libc/time/strptime.c  Fri Jul 03 10:19:29 2015 +0000
+++ b/lib/libc/time/strptime.c  Fri Jul 03 13:06:54 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: strptime.c,v 1.39 2015/04/06 14:38:22 ginsbach Exp $   */
+/*     $NetBSD: strptime.c,v 1.40 2015/07/03 13:06:54 christos Exp $   */
 
 /*-
  * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
@@ -31,11 +31,13 @@
 
 #include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: strptime.c,v 1.39 2015/04/06 14:38:22 ginsbach Exp $");
+__RCSID("$NetBSD: strptime.c,v 1.40 2015/07/03 13:06:54 christos Exp $");
 #endif
 
 #include "namespace.h"
 #include <sys/localedef.h>
+#include <sys/types.h>
+#include <sys/clock.h>
 #include <ctype.h>
 #include <locale.h>
 #include <string.h>
@@ -60,6 +62,12 @@
 #define ALT_O                  0x02
 #define        LEGAL_ALT(x)            { if (alt_format & ~(x)) return NULL; }
 
+#define        FLAG_YEAR       (1 << 0)
+#define        FLAG_MONTH      (1 << 1)
+#define        FLAG_YDAY       (1 << 2)
+#define        FLAG_MDAY       (1 << 3)
+#define        FLAG_WDAY       (1 << 4)
+
 static char gmt[] = { "GMT" };
 static char utc[] = { "UTC" };
 /* RFC-822/RFC-2822 */
@@ -74,6 +82,25 @@
 static const u_char *find_string(const u_char *, int *, const char * const *,
        const char * const *, int);
 
+static const int start_of_month[2][13] = {
+       { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+       { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+/*
+ * Calculate the week day of the first day of a year. Valid for
+ * the Gregorian calendar, which began Sept 14, 1752 in the UK
+ * and its colonies. Ref:
+ * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
+ */
+
+static int
+first_wday_of(int yr)
+{
+       return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) /  4) +
+           (is_leap_year(yr) ? 6 : 0) + 1) % 7;
+}
+
 char *
 strptime(const char *buf, const char *fmt, struct tm *tm)
 {
@@ -85,7 +112,8 @@
 {
        unsigned char c;
        const unsigned char *bp, *ep;
-       int alt_format, i, split_year = 0, neg = 0, offs;
+       int alt_format, i, split_year = 0, neg = 0, flags = 0,
+           day_offset = -1, week_offset = 0, offs;
        const char *new_fmt;
 
        bp = (const u_char *)buf;
@@ -133,16 +161,20 @@
                 */
                case 'c':       /* Date and time, using the locale's format. */
                        new_fmt = _TIME_LOCALE(loc)->d_t_fmt;
+                       flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY |
+                           FLAG_YEAR;
                        goto recurse;
 
                case 'D':       /* The date as "%m/%d/%y". */
                        new_fmt = "%m/%d/%y";
                        LEGAL_ALT(0);
+                       flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
                        goto recurse;
 
                case 'F':       /* The date as "%Y-%m-%d". */
                        new_fmt = "%Y-%m-%d";
                        LEGAL_ALT(0);
+                       flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
                        goto recurse;
 
                case 'R':       /* The time as "%H:%M". */
@@ -166,6 +198,7 @@
 
                case 'x':       /* The date, using the locale's format. */
                        new_fmt = _TIME_LOCALE(loc)->d_fmt;
+                       flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
                    recurse:
                        bp = (const u_char *)strptime((const char *)bp,
                                                            new_fmt, tm);
@@ -180,6 +213,7 @@
                        bp = find_string(bp, &tm->tm_wday,
                            _TIME_LOCALE(loc)->day, _TIME_LOCALE(loc)->abday, 7);
                        LEGAL_ALT(0);
+                       flags |= FLAG_WDAY;
                        continue;
 
                case 'B':       /* The month, using the locale's form. */
@@ -189,6 +223,7 @@
                            _TIME_LOCALE(loc)->mon, _TIME_LOCALE(loc)->abmon,
                            12);
                        LEGAL_ALT(0);
+                       flags |= FLAG_MONTH;
                        continue;
 
                case 'C':       /* The century number. */
@@ -201,12 +236,14 @@
                        split_year = 1;
                        tm->tm_year = i;
                        LEGAL_ALT(ALT_E);
+                       flags |= FLAG_YEAR;
                        continue;
 
                case 'd':       /* The day of month. */
                case 'e':
                        bp = conv_num(bp, &tm->tm_mday, 1, 31);
                        LEGAL_ALT(ALT_O);
+                       flags |= FLAG_MDAY;
                        continue;
 
                case 'k':       /* The hour (24-hour clock representation). */
@@ -232,6 +269,7 @@
                        bp = conv_num(bp, &i, 1, 366);
                        tm->tm_yday = i - 1;
                        LEGAL_ALT(0);
+                       flags |= FLAG_YDAY;
                        continue;
 
                case 'M':       /* The minute. */
@@ -244,6 +282,7 @@
                        bp = conv_num(bp, &i, 1, 12);
                        tm->tm_mon = i - 1;
                        LEGAL_ALT(ALT_O);
+                       flags |= FLAG_MONTH;
                        continue;
 
                case 'p':       /* The locale's equivalent of AM/PM. */
@@ -287,6 +326,9 @@
 
                                if (localtime_r(&sse, tm) == NULL)
                                        bp = NULL;
+                               else
+                                       flags |= FLAG_YDAY | FLAG_WDAY |
+                                           FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
                        }
                        continue;
 
@@ -298,13 +340,19 @@
                         * point to calculate a real value, so just check the
                         * range for now.
                         */
-                        bp = conv_num(bp, &i, 0, 53);
-                        LEGAL_ALT(ALT_O);
-                        continue;
+                       bp = conv_num(bp, &i, 0, 53);
+                       LEGAL_ALT(ALT_O);
+                       if (c == 'U')
+                               day_offset = TM_SUNDAY;
+                       else
+                               day_offset = TM_MONDAY;
+                       week_offset = i;
+                       continue;
 
                case 'w':       /* The day of week, beginning on sunday. */
                        bp = conv_num(bp, &tm->tm_wday, 0, 6);
                        LEGAL_ALT(ALT_O);
+                       flags |= FLAG_WDAY;
                        continue;
 
                case 'u':       /* The day of week, monday = 1. */
@@ -336,6 +384,7 @@
                        bp = conv_num(bp, &i, 0, 9999);
                        tm->tm_year = i - TM_YEAR_BASE;
                        LEGAL_ALT(ALT_E);
+                       flags |= FLAG_YEAR;
                        continue;
 
                case 'y':       /* The year within 100 years of the epoch. */
@@ -353,6 +402,7 @@
                                        i = i + 1900 - TM_YEAR_BASE;
                        }
                        tm->tm_year = i;
+                       flags |= FLAG_YEAR;
                        continue;
 
                case 'Z':
@@ -425,6 +475,8 @@
                                continue;
                        case '+':
                                neg = 0;
+                               flags |= FLAG_WDAY | FLAG_MONTH |  FLAG_MDAY |
+                                   FLAG_YEAR;
                                break;
                        case '-':
                                neg = 1;
@@ -529,6 +581,61 @@
                }
        }
 
+       if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
+               if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
+                   (FLAG_MONTH |  FLAG_MDAY)) {
+                       tm->tm_yday =  start_of_month[is_leap_year(tm->tm_year +
+                           TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
+                       flags |= FLAG_YDAY;
+               } else if (day_offset != -1) {
+                       /* Set the date to the first Sunday (or Monday)
+                        * of the specified week of the year.
+                        */
+                       if (!(flags & FLAG_WDAY)) {
+                               tm->tm_wday = day_offset;
+                               flags |= FLAG_WDAY;
+                       }
+                       tm->tm_yday = (7 -
+                           first_wday_of(tm->tm_year + TM_YEAR_BASE) +
+                           day_offset) % 7 + (week_offset - 1) * 7 +
+                           tm->tm_wday  - day_offset;
+                       flags |= FLAG_YDAY;
+               }
+       }
+
+       if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
+               int isleap;
+               if (!(flags & FLAG_MONTH)) {
+                       i = 0;
+                       isleap = is_leap_year(tm->tm_year + TM_YEAR_BASE);
+                       while (tm->tm_yday >= start_of_month[isleap][i])
+                               i++;
+                       if (i > 12) {
+                               i = 1;
+                               tm->tm_yday -= start_of_month[isleap][12];
+                               tm->tm_year++;
+                       }
+                       tm->tm_mon = i - 1;
+                       flags |= FLAG_MONTH;
+               }
+               if (!(flags & FLAG_MDAY)) {
+                       isleap = is_leap_year(tm->tm_year + TM_YEAR_BASE);
+                       tm->tm_mday = tm->tm_yday -
+                           start_of_month[isleap][tm->tm_mon] + 1;
+                       flags |= FLAG_MDAY;
+               }
+               if (!(flags & FLAG_WDAY)) {
+                       i = 0;
+                       week_offset = first_wday_of(tm->tm_year);
+                       while (i++ <= tm->tm_yday) {
+                               if (week_offset++ >= 6)
+                                       week_offset = 0;
+                       }
+                       tm->tm_wday = week_offset;
+                       flags |= FLAG_WDAY;
+               }
+       }
+
        return __UNCONST(bp);
 }
 



Home | Main Index | Thread Index | Old Index