Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 8 Oct 2014 16:32:02 +0000 (UTC)
From:      "Pedro F. Giffuni" <pfg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r272759 - stable/9/lib/libc/stdtime
Message-ID:  <201410081632.s98GW2Jb081156@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: pfg
Date: Wed Oct  8 16:32:01 2014
New Revision: 272759
URL: https://svnweb.freebsd.org/changeset/base/272759

Log:
  MFC	r272273, r272387, r272443, r272533 :
  
  Add strptime(3) support for %U and %W
  
  Add support for the missing POSIX-2001 %U and %W features: the
  existing FreeBSD strptime code recognizes both directives and
  validates that the week number lies in the permitted range,
  but then simply discards the value.
  
  Initial support for the feature was written by Paul Green.
  David Carlier added the initial handling of tm_wday/tm_yday.
  Major credit goes to Andrey Chernov for detecting much of the
  brokenness and rewriting/cleaning most of the code, making it
  much more robust.
  
  Tested independently with the strptime test from the GNU C
  library.
  
  PR:		137307
  Relnotes:	yes
  
  MFC r272441 :
  
  strptime: %s format fix.
  
  Almost never needed in real life because %s is tends to be
  only one format spec.
  1) Return code of gmtime_r() is checked.
  2) All flags are set.
  
  Submitted by:	ache

Modified:
  stable/9/lib/libc/stdtime/strptime.3
  stable/9/lib/libc/stdtime/strptime.c
Directory Properties:
  stable/9/lib/libc/   (props changed)
  stable/9/lib/libc/stdtime/   (props changed)

Modified: stable/9/lib/libc/stdtime/strptime.3
==============================================================================
--- stable/9/lib/libc/stdtime/strptime.3	Wed Oct  8 16:29:46 2014	(r272758)
+++ stable/9/lib/libc/stdtime/strptime.3	Wed Oct  8 16:32:01 2014	(r272759)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\" "
-.Dd June 25, 2012
+.Dd October 2, 2014
 .Dt STRPTIME 3
 .Os
 .Sh NAME
@@ -80,6 +80,11 @@ and
 are now interpreted as beginning at 1969 per POSIX requirements.
 Years 69-00 are interpreted in the 20th century (1969-2000), years
 01-68 in the 21st century (2001-2068).
+The
+.Fa \&%U
+and
+.Fa %W
+format specifiers accept any value within the range 00 to 53.
 .Pp
 If the
 .Fa format
@@ -161,14 +166,6 @@ and 12PM
 is taken as noon.
 .Pp
 The
-.Fa \&%U
-and
-.Fa %W
-format specifiers accept any value within the range 00 to 53
-without validating against other values supplied (like month
-or day of the year, for example).
-.Pp
-The
 .Fa %Z
 format specifier only accepts time zone abbreviations of the local time zone,
 or the value "GMT".

Modified: stable/9/lib/libc/stdtime/strptime.c
==============================================================================
--- stable/9/lib/libc/stdtime/strptime.c	Wed Oct  8 16:29:46 2014	(r272758)
+++ stable/9/lib/libc/stdtime/strptime.c	Wed Oct  8 16:32:01 2014	(r272759)
@@ -55,10 +55,32 @@ __FBSDID("$FreeBSD$");
 #include "un-namespace.h"
 #include "libc_private.h"
 #include "timelocal.h"
+#include "tzfile.h"
 
 static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
 
-#define	asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
+#define	asizeof(a)	(sizeof(a) / sizeof((a)[0]))
+
+#define	FLAG_NONE	(1 << 0)
+#define	FLAG_YEAR	(1 << 1)
+#define	FLAG_MONTH	(1 << 2)
+#define	FLAG_YDAY	(1 << 3)
+#define	FLAG_MDAY	(1 << 4)
+#define	FLAG_WDAY	(1 << 5)
+
+/*
+ * 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 year)
+{
+	return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
+		((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
+}
 
 static char *
 _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
@@ -66,9 +88,18 @@ _strptime(const char *buf, const char *f
 {
 	char	c;
 	const char *ptr;
+	int	day_offset = -1, wday_offset;
+	int week_offset;
 	int	i, len;
+	int flags;
 	int Ealternative, Oalternative;
-	struct lc_time_T *tptr = __get_current_time_locale(locale);
+	const struct lc_time_T *tptr = __get_current_time_locale(locale);
+	static 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}
+	};
+
+	flags = FLAG_NONE;
 
 	ptr = fmt;
 	while (*ptr != 0) {
@@ -102,6 +133,7 @@ label:
 			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
 			if (buf == NULL)
 				return (NULL);
+			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
 			break;
 
 		case 'C':
@@ -119,19 +151,23 @@ label:
 			if (i < 19)
 				return (NULL);
 
-			tm->tm_year = i * 100 - 1900;
+			tm->tm_year = i * 100 - TM_YEAR_BASE;
+			flags |= FLAG_YEAR;
+
 			break;
 
 		case 'c':
 			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
 			if (buf == NULL)
 				return (NULL);
+			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
 			break;
 
 		case 'D':
 			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
 			if (buf == NULL)
 				return (NULL);
+			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
 			break;
 
 		case 'E':
@@ -150,6 +186,7 @@ label:
 			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
 			if (buf == NULL)
 				return (NULL);
+			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
 			break;
 
 		case 'R':
@@ -180,6 +217,7 @@ label:
 			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
 			if (buf == NULL)
 				return (NULL);
+			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
 			break;
 
 		case 'j':
@@ -197,6 +235,8 @@ label:
 				return (NULL);
 
 			tm->tm_yday = i - 1;
+			flags |= FLAG_YDAY;
+
 			break;
 
 		case 'M':
@@ -302,8 +342,9 @@ label:
 			if (i == asizeof(tptr->weekday))
 				return (NULL);
 
-			tm->tm_wday = i;
 			buf += len;
+			tm->tm_wday = i;
+			flags |= FLAG_WDAY;
 			break;
 
 		case 'U':
@@ -327,6 +368,14 @@ label:
 			if (i > 53)
 				return (NULL);
 
+			if (c == 'U')
+				day_offset = TM_SUNDAY;
+			else
+				day_offset = TM_MONDAY;
+
+
+			week_offset = i;
+
 			break;
 
 		case 'w':
@@ -338,6 +387,7 @@ label:
 				return (NULL);
 
 			tm->tm_wday = i;
+			flags |= FLAG_WDAY;
 
 			break;
 
@@ -374,6 +424,7 @@ label:
 				return (NULL);
 
 			tm->tm_mday = i;
+			flags |= FLAG_MDAY;
 
 			break;
 
@@ -413,6 +464,8 @@ label:
 
 			tm->tm_mon = i;
 			buf += len;
+			flags |= FLAG_MONTH;
+
 			break;
 
 		case 'm':
@@ -430,6 +483,7 @@ label:
 				return (NULL);
 
 			tm->tm_mon = i - 1;
+			flags |= FLAG_MONTH;
 
 			break;
 
@@ -449,8 +503,11 @@ label:
 			}
 			errno = sverrno;
 			buf = cp;
-			gmtime_r(&t, tm);
+			if (gmtime_r(&t, tm) == NULL)
+				return (NULL);
 			*GMTp = 1;
+			flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
+			    FLAG_MDAY | FLAG_YEAR;
 			}
 			break;
 
@@ -471,13 +528,14 @@ label:
 				len--;
 			}
 			if (c == 'Y')
-				i -= 1900;
+				i -= TM_YEAR_BASE;
 			if (c == 'y' && i < 69)
 				i += 100;
 			if (i < 0)
 				return (NULL);
 
 			tm->tm_year = i;
+			flags |= FLAG_YEAR;
 
 			break;
 
@@ -543,10 +601,67 @@ label:
 			break;
 		}
 	}
+
+	if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
+		if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
+		    (FLAG_MONTH | FLAG_MDAY)) {
+			tm->tm_yday = start_of_month[isleap(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)) {
+		if (!(flags & FLAG_MONTH)) {
+			i = 0;
+			while (tm->tm_yday >=
+			    start_of_month[isleap(tm->tm_year +
+			    TM_YEAR_BASE)][i])
+				i++;
+			if (i > 12) {
+				i = 1;
+				tm->tm_yday -=
+				    start_of_month[isleap(tm->tm_year +
+				    TM_YEAR_BASE)][12];
+				tm->tm_year++;
+			}
+			tm->tm_mon = i - 1;
+			flags |= FLAG_MONTH;
+		}
+		if (!(flags & FLAG_MDAY)) {
+			tm->tm_mday = tm->tm_yday -
+			    start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
+			    [tm->tm_mon] + 1;
+			flags |= FLAG_MDAY;
+		}
+		if (!(flags & FLAG_WDAY)) {
+			i = 0;
+			wday_offset = first_wday_of(tm->tm_year);
+			while (i++ <= tm->tm_yday) {
+				if (wday_offset++ >= 6)
+					wday_offset = 0;
+			}
+			tm->tm_wday = wday_offset;
+			flags |= FLAG_WDAY;
+		}
+	}
+
 	return ((char *)buf);
 }
 
-
 char *
 strptime_l(const char * __restrict buf, const char * __restrict fmt,
     struct tm * __restrict tm, locale_t loc)
@@ -564,6 +679,7 @@ strptime_l(const char * __restrict buf, 
 
 	return (ret);
 }
+
 char *
 strptime(const char * __restrict buf, const char * __restrict fmt,
     struct tm * __restrict tm)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201410081632.s98GW2Jb081156>