This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

strptime doesn't fill in tm_wday and tm_yday.


Hi!

Here's a patch for the problems described in [1] and for some
other variations on that theme, that I was nearly finished with
when I realized that Cygwin had its own strptime.

I used the below test program.

Cheers,
Peter

[1] http://cygwin.com/ml/cygwin/2011-05/msg00159.html

-------------------------8<--------------------------
#include <stdio.h>
#include <time.h>
#include <string.h>

int
main(void)
{
	struct tm tm;
	time_t t;
	int y, d;
	int wday;
	char buf[200];

	y = 2008;
	tm.tm_year = y - 1900;                  /* 2008, a leap year */
	tm.tm_mon = 0;                          /* Jan */
	tm.tm_mday = 1;                         /* 1st */
	tm.tm_hour = tm.tm_min = tm.tm_sec = 0; /* midnight */
	wday = 2;                               /* was a Tuesday */
	t = timegm(&tm);

	for (y = 2008; y < 2013; ++y) {
		for (d = 0; d < 365 + !(y % 4); ++d) {
			gmtime_r(&t, &tm);
			strftime(buf, sizeof(buf), "%Y-%m-%d %T", &tm);
			memset(&tm, 0x55, sizeof(tm));
			if (!strptime(buf, "%Y-%m-%d %T", &tm)) {
				fprintf(stderr, "strptime error\n");
				return 1;
			}
			if (wday != tm.tm_wday) {
				fprintf(stderr, "strptime wday failure\n"
					"year %d yday %d exp-wday %d tm_wday %d\n",
					y, d, wday, tm.tm_wday);
				return 1;
			}
			if (d != tm.tm_yday) {
				fprintf(stderr, "strptime yday failure\n"
					"year %d exp-yday %d tm_yday %d\n",
					y, d, tm.tm_yday);
				return 1;
			}

			gmtime_r(&t, &tm);
			strftime(buf, sizeof(buf), "%Y %j %T", &tm);
			memset(&tm, 0x55, sizeof(tm));
			if (!strptime(buf, "%Y %j %T", &tm)) {
				fprintf(stderr, "strptime error\n");
				return 1;
			}
			if (wday != tm.tm_wday) {
				fprintf(stderr, "strptime wday failure (2)\n"
					"year %d yday %d exp-wday %d tm_wday %d\n",
					y, d, wday, tm.tm_wday);
				return 1;
			}
			if (t != timegm(&tm)) {
				fprintf(stderr, "strptime time_t failure\n"
					"year %d exp-time_t %d time_t %d\n",
					y, t, timegm(&tm));
				return 1;
			}

			t += 60 * 60 * 24;
			wday = (tm.tm_wday + 1) % 7;
		}
	}
	printf("ok\n");
	return 0;
}
-------------------------8<--------------------------

2011-05-12  Peter Rosin  <peda@lysator.liu.se>

	* libc/time/strptime.c (strptime): Fill in tm_yday when
	all of tm_year, tm_mon and tm_mday are updated. Fill in
	tm_mon, tm_mday and tm_wday when both of tm_year and tm_yday
	are updated.

Index: libc/time/strptime.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/time/strptime.c,v
retrieving revision 1.5
diff -u -p -r1.5 strptime.c
--- libc/time/strptime.c	22 Jan 2010 13:03:42 -0000	1.5
+++ libc/time/strptime.c	12 May 2011 07:29:38 -0000
@@ -40,6 +40,16 @@
 
 #define _ctloc(x) (_CurrentTimeLocale->x)
 
+static _CONST int _DAYS_BEFORE_MONTH[12] =
+{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+#define SET_MDAY 1
+#define SET_MON  2
+#define SET_YEAR 4
+#define SET_WDAY 8
+#define SET_YDAY 16
+#define SET_YMD  (SET_YEAR | SET_MON | SET_MDAY)
+
 /*
  * tm_year is relative this year 
  */
@@ -147,6 +157,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 	struct tm *timeptr)
 {
     char c;
+    int ymd = 0;
 
     struct lc_time_T *_CurrentTimeLocale = __get_current_time_locale ();
     for (; (c = *format) != '\0'; ++format) {
@@ -166,18 +177,21 @@ _DEFUN (strptime, (buf, format, timeptr)
 		if (ret < 0)
 		    return NULL;
 		timeptr->tm_wday = ret;
+		ymd |= SET_WDAY;
 		break;
 	    case 'a' :
 		ret = match_string (&buf, _ctloc (wday));
 		if (ret < 0)
 		    return NULL;
 		timeptr->tm_wday = ret;
+		ymd |= SET_WDAY;
 		break;
 	    case 'B' :
 		ret = match_string (&buf, _ctloc (month));
 		if (ret < 0)
 		    return NULL;
 		timeptr->tm_mon = ret;
+		ymd |= SET_MON;
 		break;
 	    case 'b' :
 	    case 'h' :
@@ -185,6 +199,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		if (ret < 0)
 		    return NULL;
 		timeptr->tm_mon = ret;
+		ymd |= SET_MON;
 		break;
 	    case 'C' :
 		ret = strtol (buf, &s, 10);
@@ -192,18 +207,21 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_year = (ret * 100) - tm_year_base;
 		buf = s;
+		ymd |= SET_YEAR;
 		break;
 	    case 'c' :		/* %a %b %e %H:%M:%S %Y */
 		s = strptime (buf, _ctloc (c_fmt), timeptr);
 		if (s == NULL)
 		    return NULL;
 		buf = s;
+		ymd |= SET_WDAY | SET_YMD;
 		break;
 	    case 'D' :		/* %m/%d/%y */
 		s = strptime (buf, "%m/%d/%y", timeptr);
 		if (s == NULL)
 		    return NULL;
 		buf = s;
+		ymd |= SET_YMD;
 		break;
 	    case 'd' :
 	    case 'e' :
@@ -212,6 +230,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_mday = ret;
 		buf = s;
+		ymd |= SET_MDAY;
 		break;
 	    case 'H' :
 	    case 'k' :
@@ -238,6 +257,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_yday = ret - 1;
 		buf = s;
+		ymd |= SET_YDAY;
 		break;
 	    case 'm' :
 		ret = strtol (buf, &s, 10);
@@ -245,6 +265,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_mon = ret - 1;
 		buf = s;
+		ymd |= SET_MON;
 		break;
 	    case 'M' :
 		ret = strtol (buf, &s, 10);
@@ -306,6 +327,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_wday = ret - 1;
 		buf = s;
+		ymd |= SET_WDAY;
 		break;
 	    case 'w' :
 		ret = strtol (buf, &s, 10);
@@ -313,6 +335,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_wday = ret;
 		buf = s;
+		ymd |= SET_WDAY;
 		break;
 	    case 'U' :
 		ret = strtol (buf, &s, 10);
@@ -320,6 +343,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		set_week_number_sun (timeptr, ret);
 		buf = s;
+		ymd |= SET_YDAY;
 		break;
 	    case 'V' :
 		ret = strtol (buf, &s, 10);
@@ -327,6 +351,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		set_week_number_mon4 (timeptr, ret);
 		buf = s;
+		ymd |= SET_YDAY;
 		break;
 	    case 'W' :
 		ret = strtol (buf, &s, 10);
@@ -334,12 +359,14 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		set_week_number_mon (timeptr, ret);
 		buf = s;
+		ymd |= SET_YDAY;
 		break;
 	    case 'x' :
 		s = strptime (buf, _ctloc (x_fmt), timeptr);
 		if (s == NULL)
 		    return NULL;
 		buf = s;
+		ymd |= SET_YMD;
 		break;
 	    case 'X' :
 		s = strptime (buf, _ctloc (X_fmt), timeptr);
@@ -356,6 +383,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		else
 		    timeptr->tm_year = ret;
 		buf = s;
+		ymd |= SET_YEAR;
 		break;
 	    case 'Y' :
 		ret = strtol (buf, &s, 10);
@@ -363,6 +391,7 @@ _DEFUN (strptime, (buf, format, timeptr)
 		    return NULL;
 		timeptr->tm_year = ret - tm_year_base;
 		buf = s;
+		ymd |= SET_YEAR;
 		break;
 	    case 'Z' :
 		/* Unsupported. Just ignore.  */
@@ -390,6 +419,58 @@ _DEFUN (strptime, (buf, format, timeptr)
 		return NULL;
 	}
     }
+
+    if ((ymd & SET_YMD) == SET_YMD) {
+	/* all of tm_year, tm_mon and tm_mday, but... */
+
+	if (!(ymd & SET_YDAY)) {
+	    /* ...not tm_yday, so fill it in */
+	    timeptr->tm_yday = _DAYS_BEFORE_MONTH[timeptr->tm_mon]
+		+ timeptr->tm_mday;
+	    if (!is_leap_year (timeptr->tm_year + tm_year_base)
+		|| timeptr->tm_mon < 2)
+	    {
+		timeptr->tm_yday--;
+	    }
+	    ymd |= SET_YDAY;
+	}
+    }
+    else if ((ymd & (SET_YEAR | SET_YDAY)) == (SET_YEAR | SET_YDAY)) {
+	/* both of tm_year and tm_yday, but... */
+
+	if (!(ymd & SET_MON)) {
+	    /* ...not tm_mon, so fill it in, and/or... */
+	    if (timeptr->tm_yday < _DAYS_BEFORE_MONTH[1])
+		timeptr->tm_mon = 0;
+	    else {
+		int leap = is_leap_year (timeptr->tm_year + tm_year_base);
+		int i;
+		for (i = 2; i < 12; ++i) {
+		    if (timeptr->tm_yday < _DAYS_BEFORE_MONTH[i] + leap)
+			break;
+		}
+		timeptr->tm_mon = i - 1;
+	    }
+	}
+
+	if (!(ymd & SET_MDAY)) {
+	    /* ...not tm_mday, so fill it in */
+	    timeptr->tm_mday = timeptr->tm_yday
+		- _DAYS_BEFORE_MONTH[timeptr->tm_mon];
+	    if (!is_leap_year (timeptr->tm_year + tm_year_base)
+		|| timeptr->tm_mon < 2)
+	    {
+		timeptr->tm_mday++;
+	    }
+	}
+    }
+
+    if ((ymd & (SET_YEAR | SET_YDAY | SET_WDAY)) == (SET_YEAR | SET_YDAY)) {
+	/* fill in tm_wday */
+	int fday = first_day (timeptr->tm_year + tm_year_base);
+	timeptr->tm_wday = (fday + timeptr->tm_yday) % 7;
+    }
+
     return (char *)buf;
 }
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]