diff -urp old/strptime.c new/strptime.c --- old/strptime.c 2009-07-31 10:10:54.000000000 -0400 +++ new/strptime.c 2009-07-31 10:10:58.000000000 -0400 @@ -76,15 +76,34 @@ static char * _strptime(const char *, co #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) +#define leapyear(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + +/* 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/Calculating_the_day_of_the_week + */ + +static int +first_wday_of(int year) +{ + return (((2 * (3 - (year / 100) % 4)) + (year % 100) + + ((year % 100) / 4) + (leapyear(year) ? 6 : 0) + 1) % 7); +} + static char * _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp) { char c; const char *ptr; - int i, + int day_offset, + i, len; int Ealternative, Oalternative; struct lc_time_T *tptr = __get_current_time_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}}; ptr = fmt; while (*ptr != 0) { @@ -154,6 +173,12 @@ label: Ealternative++; goto label; + case 'n': + case 't': + while (*buf != 0 && isspace ((unsigned char)*buf)) + buf++; + break; + case 'O': if (Ealternative || Oalternative) break; @@ -324,12 +349,8 @@ label: case 'U': case 'W': - /* - * XXX This is bogus, as we can not assume any valid - * information present in the tm structure at this - * point to calculate a real value, so just check the - * range for now. - */ + /* We expect that the year has already been + parsed. */ if (!isdigit((unsigned char)*buf)) return 0; @@ -345,6 +366,40 @@ label: if (*buf != 0 && isspace((unsigned char)*buf)) while (*ptr != 0 && !isspace((unsigned char)*ptr)) ptr++; + + /* Week numbers are 1-origin. So that we can always + return the date of a Sunday (or Monday), treat week + 0 as week 1. */ + + if (i == 0) + i = 1; + + if (c == 'U') + day_offset = 0; /* Sunday */ + else day_offset = 1; /* Monday */ + + /* Set the date to the first Sunday (or Monday) + of the specified week of the year. */ + + tm->tm_yday = (7 - first_wday_of(tm->tm_year+1900) + + day_offset) % 7 + (i-1) * 7; + i = 0; + while (tm->tm_yday >= + start_of_month[leapyear(tm->tm_year+1900)][i]) + { + i++; + } + if (i > 12) + { + i = 1; + tm->tm_yday -= + start_of_month[leapyear(tm->tm_year+1900)][12]; + tm->tm_year++; + } + tm->tm_mon = i - 1; + tm->tm_mday = tm->tm_yday - + start_of_month[leapyear(tm->tm_year+1900)][i - 1] + 1; + tm->tm_wday = day_offset; break; case 'w': diff -urp old/t_first_wday.c new/t_first_wday.c --- old/t_first_wday.c 2009-07-31 10:11:12.000000000 -0400 +++ new/t_first_wday.c 2009-07-31 10:11:17.000000000 -0400 @@ -0,0 +1,61 @@ +/* Beginning of modification history */ +/* Written 2009-07-30 by Paul Green. */ +/* End of modification history */ + +/* Program to test the calculation of the first day of a year. + */ + +#define _POSIX_C_SOURCE 200112L +#include + +static char *day_name[7] = { "Sunday", "Monday", "Tuesday", + "Wednesday", "Thursday", "Friday", "Saturday"}; + +static int known_wday [12] = {4, 5, 6, 1, 2, 3, 4, 6, 0, 1, 2, 4}; + +/* Returns 1 if y is a leap year, 0 otherwise. */ + +#define leapyear(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + +/* 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/Calculating_the_day_of_the_week + */ + +static int first_wday_of(int year) +{ + return ( + (2 * (3 - (year / 100) % 4)) + + (year % 100) + + ((year % 100) / 4) + + (leapyear(year) ? 6 : 0) + + 1 + ) % 7; +} + +int main (int argc, char **argv) +{ + int wday,year; + int errors=0; + + for (year=1998; year<2010; year++) + { + wday = first_wday_of (year); + if (wday != known_wday [year-1998]) + { + printf ("Error calculating first day of %4d.\n", year); + printf ("Expected: January %d.\n", known_wday [year-1998]); + printf ("Got: January %d.\n", wday); + errors++; + } + else printf ("The first day of %4d is a %s (%d)\n", + year, day_name [wday], wday); + } + + if (errors) + printf ("Test failed.\n"); + else printf ("Test passed.\n"); + + return errors; +} diff -urp old/t_strptime.c new/t_strptime.c --- old/t_strptime.c 2009-07-31 10:11:21.000000000 -0400 +++ new/t_strptime.c 2009-07-31 10:11:25.000000000 -0400 @@ -0,0 +1,349 @@ +/* Beginning of modification history */ +/* Written 09-07-27 by Paul Green. */ +/* End of modification history */ + +#define _XOPEN_SOURCE 600 +#pragma longmap_check, no_default_mapping, system_programming; + +#include +#include +#include +#include +#include "time.h" + +static int failures = 0; + +static void explain (char *title, char *fmt, char *src) +{ + printf ("Testing: %s\n", title); + printf ("Format: %s\n", fmt); + printf ("Source: %s\n", src); +} + +static void try1 (char *title, char *fmt, char *src, off_t off, int answer) +{ +struct tm r; +int value; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + value = * (int *) ((char *)&r + off); + + if (value == answer) + printf ("Result: %d (ok)\n", answer); + else + { + printf ("Result: %d instead of %d (bad)\n", value, answer); + failures++; + } + printf ("\n"); +} + +/* Special test for the %c format string. Try to convert + "Mon Mday HH:MM:SS YYYY". */ + +static void try2 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_mon == 11 && + r.tm_mday == 31 && + r.tm_year == 109 && + r.tm_hour == 23 && + r.tm_min == 59 && + r.tm_sec == 59) + printf ("Result: December 31 23:59:59 2009 (ok)\n"); + else + { + failures++; + printf ("Result: failed. %d/%d/%d %02d:%02d:%02d %2d %2d\n", + r.tm_mon+1, r.tm_mday, r.tm_year+1900, r.tm_hour, + r.tm_min, r.tm_sec, r.tm_wday, r.tm_yday); + } + printf ("\n"); +} + +/* Special test for the %D format string. Try to convert + "MM/DD/YY". */ + +static void try3 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_mon == 11 && + r.tm_mday == 31 && + r.tm_year == 109) + printf ("Result: 12/31/2009 (ok)\n"); + else + { + failures++; + printf ("Result: failed. %02d/%02d/%4d\n", + r.tm_mon+1, r.tm_mday, r.tm_year+1900); + } + printf ("\n"); +} + +/* Special test for the %r format string. Try to convert + "HH:MM:SS XM". */ + +static void try4 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_hour == 23 && + r.tm_min == 59 && + r.tm_sec == 59) + printf ("Result: 11:59:59 pm (ok)\n"); + else + { + failures++; + printf ("Result: failed. %02d:%02d:%02d\n", + r.tm_hour, r.tm_min, r.tm_sec); + } + printf ("\n"); +} + +/* Special test for the %R format string. Try to convert + "HH:MM". */ + +static void try5 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_hour == 23 && + r.tm_min == 59) + printf ("Result: 23:59 (ok)\n"); + else + { + failures++; + printf ("Result: failed. %02d:%02d\n", + r.tm_hour, r.tm_min); + } + printf ("\n"); +} + +/* Special test for the %T format string. Try to convert + "HH:MM:SS". */ + +static void try6 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_hour == 23 && + r.tm_min == 59 && + r.tm_sec == 59) + printf ("Result: 23:59:59 (ok)\n"); + else + { + failures++; + printf ("Result: failed. %02d:%02d:%02d\n", + r.tm_hour, r.tm_min, r.tm_sec); + } + printf ("\n"); +} + +/* Special test for the %U format string. Try to convert + "2009 21", which is 5/31/2009 */ + +static void try7 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_mon == 4 && + r.tm_mday == 31 && + r.tm_year == 109 && + r.tm_wday == 0 && + r.tm_yday == 151) + printf ("Result: Sunday May 31 2009, yday 151 [week 21] (ok)\n"); + else + { + failures++; + printf ("Result: failed. %02d/%02d/%04d wday=%d yday=%d\n", + r.tm_mon+1, r.tm_mday, r.tm_year+1900, r.tm_wday, r.tm_yday); + printf ("Expected 05/31/2009 wday=0 yday=151\n"); + } + printf ("\n"); +} + +/* Special test for the %W format string. Try to convert + "2009 21", which is 6/1/2009 */ + +static void try8 (char *title, char *fmt, char *src) +{ +struct tm r; + + explain (title, fmt, src); + memset (&r, 0, sizeof (r)); + strptime (src, fmt, &r); + + if (r.tm_mon == 5 && + r.tm_mday == 1 && + r.tm_year == 109 && + r.tm_wday == 1 && + r.tm_yday == 152) + printf ("Result: Monday June 1 2009, yday 152 [week 21] (ok)\n"); + else + { + failures++; + printf ("Result: failed. %02d/%02d/%04d wday=%d yday=%d\n", + r.tm_mon+1, r.tm_mday, r.tm_year+1900, r.tm_wday, r.tm_yday); + printf ("Expected: 06/01/2009 wday=1 yday=152\n"); + } + printf ("\n"); +} + +/* Special test for the %U format string. Try to convert + a range of values. */ + +static void tryU (char *title, char *fmt, char *range) +{ +/* automatic */ + +int mon, mday, w, yday, year, yr; +char *p; +char src[32]; +struct tm r; + +/* static */ + +/* The date in January of the first Sunday, for 1998-2004. */ +static int first_sunday [7] = {4, 3, 2, 7, 6, 5, 4}; + +/* The month*100 + the date of Sunday, for 1998-2004. */ +static int known_answer [7][54] = { + {0,104,111,118,125,201,208,215,222,301,308,315,322,329,405,412,419,426,503,510,517,524,531,607,614,621,628,705,712,719,726,802,809,816,823,830,906,913,920,927,1004,1011,1018,1025,1101,1108,1115,1122,1129,1206,1213,1220,1227,103}, /* 1998 */ + {0,103,110,117,124,131,207,214,221,228,307,314,321,328,404,411,418,425,502,509,516,523,530,606,613,620,627,704,711,718,725,801,808,815,822,829,905,912,919,926,1003,1010,1017,1024,1031,1107,1114,1121,1128,1205,1212,1219,1226,102}, /* 1999 */ + {0,102,109,116,123,130,206,213,220,227,305,312,319,326,402,409,416,423,430,507,514,521,528,604,611,618,625,702,709,716,723,730,806,813,820,827,903,910,917,924,1001,1008,1015,1022,1029,1105,1112,1119,1126,1203,1210,1217,1224,1231}, /* 2000 */ + {0,107,114,121,128,204,211,218,225,304,311,318,325,401,408,415,422,429,506,513,520,527,603,610,617,624,701,708,715,722,729,805,812,819,826,902,909,916,923,930,1007,1014,1021,1028,1104,1111,1118,1125,1202,1209,1216,1223,1230,106}, /* 2001 */ + {0,106,113,120,127,203,210,217,224,303,310,317,324,331,407,414,421,428,505,512,519,526,602,609,616,623,630,707,714,721,728,804,811,818,825,901,908,915,922,929,1006,1013,1020,1027,1103,1110,1117,1124,1201,1208,1215,1222,1229,105}, /* 2002 */ + {0,105,112,119,126,202,209,216,223,302,309,316,323,330,406,413,420,427,504,511,518,525,601,608,615,622,629,706,713,720,727,803,810,817,824,831,907,914,921,928,1005,1012,1019,1026,1102,1109,1116,1123,1130,1207,1214,1221,1228,104}, /* 2003 */ + {0,104,111,118,125,201,208,215,222,229,307,314,321,328,404,411,418,425,502,509,516,523,530,606,613,620,627,704,711,718,725,801,808,815,822,829,905,912,919,926,1003,1010,1017,1024,1031,1107,1114,1121,1128,1205,1212,1219,1226,102}}; /* 2004 */ + +/* execution */ + + explain (title, fmt, range); + memset (&r, 0, sizeof (r)); + + for (year=1998; year<2005; year++) + { + mday = first_sunday[year-1998]; + yday = mday; + printf ("The first Sunday of %4d is Jan %d.\n", year, mday); + + for (w=1; w<54; w++) + { + sprintf (src, "%4d %d", year, w); + p = strptime (src, fmt, &r); + mon = (known_answer[year-1998][w] / 100) - 1; + mday = (known_answer[year-1998][w] % 100); + + if (w == 53 && mon == 0) + { + yr = year + 1; + yday = mday; + } + else yr = year; + + if (p == NULL || + r.tm_mon != mon || + r.tm_mday != mday || + r.tm_year != yr - 1900 || + r.tm_wday != 0 || + r.tm_yday != yday) + { + failures++; + printf ("%4d week %d failed.\n", year, w); + printf (" got: %02d/%02d/%04d wday=%d yday=%d\n", + r.tm_mon+1, r.tm_mday, r.tm_year+1900, r.tm_wday, r.tm_yday); + printf (" expected: %02d/%02d/%04d wday=%d yday=%d\n", + mon+1, mday, yr, 0, yday); + printf ("\n"); + } + yday += 7; + } + } + printf ("\n"); +} + +/* Special test for the %W format string. */ + +int main (int argc, char **argv) +{ +struct tm t; + + t.tm_sec = 59; + t.tm_min = 59; + t.tm_hour = 23; + t.tm_mday = 31; + t.tm_mon = 12; + t.tm_year = 2009; + t.tm_wday = 6; + t.tm_yday = 364; + t.tm_isdst = 0; + + try1 ("Day of the week", "%a", "Sat", offsetof (struct tm, tm_wday), 6); + try1 ("Day of the week", "%A", "Saturday", offsetof (struct tm, tm_wday), 6); + try1 ("Month name", "%b", "Dec", offsetof (struct tm, tm_mon), 11); + try1 ("Month name", "%B", "December", offsetof (struct tm, tm_mon), 11); + try2 ("Date and time", "%c", "December 31 23:59:59 2009"); + try1 ("Century", "%C", "2009", offsetof (struct tm, tm_year), 100); + try1 ("Date of month", "%d", "31", offsetof (struct tm, tm_mday), 31); + try3 ("MM/DD/YY", "%D", "12/31/09"); + /* skip E codes */ + /* skip O codes */ + try1 ("Date of month", "%e", "31", offsetof (struct tm, tm_mday), 31); + try1 ("Month name", "%h", "Dec", offsetof (struct tm, tm_mon), 11); + try1 ("Hours, 24-hr", "%H", "23", offsetof (struct tm, tm_hour), 23); + try1 ("Hours, 12-hr", "%I", "12", offsetof (struct tm, tm_hour), 12); + try1 ("Day of year", "%j", "365", offsetof (struct tm, tm_yday), 364); + try1 ("Month number", "%m", "12", offsetof (struct tm, tm_mon), 11); + try1 ("Minute number", "%M", "59", offsetof (struct tm, tm_min), 59); + try1 ("AM/PM 12-hr", "%I %p", "11 AM", offsetof (struct tm, tm_hour), 11); + try1 ("AM/PM 12-hr", "%I %p", "11 PM", offsetof (struct tm, tm_hour), 23); + try1 ("AM/PM 24-hr", "%H", "11", offsetof (struct tm, tm_hour), 11); + try1 ("AM/PM 24-hr", "%H", "23", offsetof (struct tm, tm_hour), 23); + try4 ("Time of day H:M:S XM", "%r", "11:59:59 pm"); + try5 ("Time of day H:M", "%R", "23:59"); + try1 ("Second number", "%S", "59", offsetof (struct tm, tm_sec), 59); + try6 ("Time of day H:M:S", "%T", "23:59:59"); + try7 ("Week number Sun=0", "%Y %U", "2009 22"); + try1 ("Week day number", "%w", "4", offsetof (struct tm, tm_wday), 4); + try8 ("Week number Mon=0", "%Y %W", "2009 22"); + try1 ("Year in century", "%y", "9", offsetof (struct tm, tm_year), 109); + try1 ("Year in century", "%Y", "2009", offsetof (struct tm, tm_year), 109); + + tryU ("U conversion", "%Y %U", "range"); + + if (failures) + printf ("%d tests failed.\n", failures); + else printf ("All tests passed.\n"); + + return failures; +} +