Mail Archives: djgpp-workers/2004/10/24/22:14:27
[repost with longer line length]
CONTENTS C99 strftime and Related Patches
contrib/liblocal.02/src/ansi/locale/setlocal.c
contrib/liblocal.02/src/ansi/time/strftime.c
djgpp/src/libc/ansi/time/strftime.c
djgpp/src/libc/ansi/time/strftime.txh
djgpp/tests/libc/ansi/time/makefile
djgpp/tests/libc/ansi/time/strftime.c
djgpp/tests/libc/ansi/time/xstrftm.c
djgpp/tests/libc/ansi/time/strftiml.c NEW
djgpp/tests/libc/ansi/time/strftimt.c NEW
CHANGES C99 strftime and Related Patches
contrib/liblocal.02/src/ansi/locale/setlocal.c
fix problem resetting standard locale and returning updated locale name
contrib/liblocal.02/src/ansi/time/strftime.c
src/libc/ansi/time/strftime.c
#define required constants;
add YEAR() conversion macro and ISLEAPYEAR() test macro;
add weekday enum definitions;
add iso_year() and iso_week() functions;
accept and ignore C99 E and O modifiers;
add C99 %F, %G, %g, %V formats;
add %s (GNU) format;
fix %z ISO 8601/RFC 822 time zone results
src/libc/ansi/time/strftime.txh
add new formats; add all modifiers; make descriptions consistent
tests/libc/ansi/time/makefile
add strftiml.c and strftimt.c tests
tests/libc/ansi/time/strftime.c
change inline macro calls to table driven tests;
add new tests and results for all formats, including unsupported letters
tests/libc/ansi/time/xstrftm.c
add standard and current locale changes; output each locale name
tests/libc/ansi/time/strftiml.c
list all formats, including unsupported letters
tests/libc/ansi/time/strftimt.c
exception tester covering all formats;
table driven supporting locales and time zones;
many corner cases for non/ISO week and weekdays
diff -put contrib/liblocal.02/src/ansi/locale/setlocal.c contrib/liblocal.02/src/ansi/new/setlocal.c
--- contrib/liblocal.02/src/ansi/locale/setlocal.c 2002-07-17 02:16:36.000000000 -0600
+++ contrib/liblocal.02/src/ansi/new/setlocal.c 2004-02-24 01:57:18.000000000 -0700
@@ -465,15 +465,17 @@ setlocale(int category, const char *loca
if (locale[0] == '\0')
{
const char *env;
- if ((env = getenv ("LANG")) != NULL)
+ if ((env = getenv (cat[i].env)) != NULL)
{
locale = env;
}
- if ((env = getenv (cat[i].env)) != NULL)
+ else
+ if ((env = getenv ("LC_ALL")) != NULL)
{
locale = env;
}
- if ((env = getenv ("LC_ALL")) != NULL)
+ else
+ if ((env = getenv ("LANG")) != NULL)
{
locale = env;
}
@@ -481,7 +483,10 @@ setlocale(int category, const char *loca
if ((stricmp(locale, "C") == 0) || (stricmp(locale, "POSIX") == 0))
{
if (cat[i].reset() == 0)
+ {
honored = 0;
+ continue;
+ }
}
else
{
@@ -559,10 +564,10 @@ setlocale(int category, const char *loca
honored = 0;
continue;
}
-
- strncpy(lc_current[i], locale, LC_MAXNAMESIZE);
- lc_current[i][LC_MAXNAMESIZE - 1] = '\0';
}
+
+ strncpy(lc_current[i], locale, LC_MAXNAMESIZE);
+ lc_current[i][LC_MAXNAMESIZE - 1] = '\0';
}
}
if (segment != -1)
@@ -607,8 +612,8 @@ setlocale(int category, const char *loca
#include <stdio.h>
unsigned char __dj_collate_table[256];
-char __dj_date_format[10] = "%m/%d/%y";
-char __dj_time_format[16] = "%H:%M:%S";
+extern char __dj_date_format[10];
+extern char __dj_time_format[16];
int
main(int ac, char *av[])
diff -put contrib/liblocal.02/src/ansi/time/strftime.c contrib/liblocal.02/src/ansi/new/strftime.c
--- contrib/liblocal.02/src/ansi/time/strftime.c 2002-06-08 04:18:06.000000000 -0600
+++ contrib/liblocal.02/src/ansi/new/strftime.c 2004-02-29 00:04:48.000000000 -0700
@@ -1,13 +1,57 @@
+/* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */
-
#include <string.h>
#include <time.h>
#include <ctype.h>
+#include <stdlib.h>
+#if 0 /* DEBUG */
+#include <stdio.h>
+#endif /* 0 */
+
+
+
+#define TM_YEAR_BASE 1900 /* tm year epoch base */
+#define LEAP_CENTURIES 4 /* centuries between leap centuries */
+#define CENTURY_YEARS 100 /* years per century */
+#define LEAP_CENTURY_YEARS (LEAP_CENTURIES * CENTURY_YEARS) /* years */
+#define LEAP_YEARS 4 /* years between leap years */
+#define YEAR_DAYS 365 /* days in common year */
+#define HOUR_MINS 60 /* minutes per hour */
+#define MIN_SECS 60 /* seconds per minute */
+
+
+#define FMT_DATE_ISO "%Y-%m-%d" /* ISO date format */
+
+
+/* convert tm year to Gregorian year */
+#define YEAR(tm_y) ((tm_y) + TM_YEAR_BASE)
+/* test if Gregorian year is a leap year */
+#define IS_LEAP_YEAR(y) (!((y) % LEAP_YEARS) && (((y) % CENTURY_YEARS) \
+ || !((y) % LEAP_CENTURY_YEARS)))
+
-#define TM_YEAR_BASE 1900
+
+/* tm wday values */
+typedef enum weekday
+{
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY,
+ WEEK_DAYS
+
+} weekday; /* enum */
+
+
+
+
+/* locale dependent strings */
static const char *afmt[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
@@ -24,11 +68,92 @@ static const char *Bfmt[] = {
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December",
};
+
+
+/* locale dependent formats */
+
char __dj_date_format[10] = "%m/%d/%y";
char __dj_time_format[16] = "%H:%M:%S";
-static size_t gsize;
-static char *pt;
+
+/* internal global variables */
+
+static size_t gsize; /* user buffer space left */
+static char *pt; /* next char to use in buffer */
+
+
+
+/* calculate ISO year from Gregorian year and days of year and week */
+static int
+iso_year( int year, int doy, int dow)
+{
+ int ISOyear = year; /* ISO year may not be same as Gregorian */
+ int near_thu = doy + THURSDAY - (dow ? dow : WEEK_DAYS);
+ /* day in year of closest Thu to date */
+
+
+#if 0 /* DEBUG */
+ printf( "year %d doy %d dow %d thu %d near thu %d max %d \n",
+ year, doy, dow, THURSDAY, near_thu, YEAR_DAYS + IS_LEAP_YEAR( year ));
+#endif /* 0 */
+
+/* if nearest Thu before start of year, then it's in last year */
+ if (near_thu < 0)
+ {
+ --ISOyear;
+ }
+ else
+/* if nearest Thu after end of year, then it's in next year */
+ if (near_thu >= YEAR_DAYS + IS_LEAP_YEAR( year ))
+ {
+ ++ISOyear;
+ }
+
+ return ISOyear;
+
+} /* iso_year() */
+
+
+
+/* calculate ISO week from Gregorian year and days of year and week */
+static int
+iso_week( int year, int doy, int dow)
+{
+ int near_thu = doy + THURSDAY - (dow ? dow : WEEK_DAYS);
+ /* day in year of closest Thu to date */
+
+
+#if 0 /* DEBUG */
+ printf( "year %d doy %d dow %d thu %d near thu %d"
+ " max prev %d this %d \n",
+ year, doy, dow, THURSDAY, near_thu,
+ YEAR_DAYS + IS_LEAP_YEAR( year - 1 ),
+ YEAR_DAYS + IS_LEAP_YEAR( year ));
+#endif /* 0 */
+
+/* if nearest Thu before start of year, then it's in last year */
+ if (near_thu < 0)
+ {
+/* adjust day to make it part of last year */
+ near_thu += YEAR_DAYS + IS_LEAP_YEAR( year - 1 );
+ }
+ else
+/* if nearest Thu after end of year, then it's in next year */
+ if (near_thu >= YEAR_DAYS + IS_LEAP_YEAR( year ))
+ {
+/* adjust day to make it part of next year */
+ near_thu -= YEAR_DAYS + IS_LEAP_YEAR( year );
+ }
+
+#if 0 /* DEBUG */
+ printf( "near thu %d week %d \n", near_thu, near_thu / WEEK_DAYS + 1);
+#endif /* 0 */
+
+ return near_thu / WEEK_DAYS + 1; /* 0-365 -> 0-52 -> 1-53 */
+
+} /* iso_week() */
+
+
static int
_add(const char *str, int upcase)
@@ -47,7 +172,7 @@ _add(const char *str, int upcase)
static int
_conv(int n, int digits, char pad)
{
- static char buf[10];
+ static char buf[12];
char *p = buf + sizeof(buf) - 2;
do {
@@ -70,6 +195,10 @@ _fmt(const char *format, const struct tm
if (*format == '%')
{
int pad = '0', space=' ';
+ int era = 0; /* locale era modifier */
+ int ordinal = 0; /* locale ordinal modifier */
+
+
if (format[1] == '_')
pad = space = ' ', format++;
if (format[1] == '-')
@@ -78,6 +207,10 @@ _fmt(const char *format, const struct tm
pad = space = '0', format++;
if (format[1] == '^')
upcase = 1, format++;
+ if (format[1] == 'E') /* if locale era modifier */
+ era = 1, format++;
+ if (format[1] == 'O') /* if locale ordinal modifier */
+ ordinal = 1, format++;
switch(*++format)
{
@@ -129,6 +262,16 @@ _fmt(const char *format, const struct tm
if (!_conv(t->tm_mday, 2, pad))
return 0;
continue;
+
+/*
+ * %F is equivalent to ``%Y-%m-%d'' (the ISO 8601 date format).
+ * [tm_year, tm_mon, tm_mday]
+ */
+ case 'F':
+ if (!_fmt( FMT_DATE_ISO, t, upcase))
+ return 0;
+ continue;
+
case 'H':
if (!_conv(t->tm_hour, 2, pad))
return 0;
@@ -179,6 +322,29 @@ _fmt(const char *format, const struct tm
if (!_conv(t->tm_sec, 2, pad))
return 0;
continue;
+
+/*
+ * + %s is replaced by the number of seconds since the epoch
+ * 1970-01-01 00:00:00 as a decimal number. [all]
+ */
+ case 's':
+ {
+/* copy user tm in case modified */
+ struct tm tm = *t;
+/* convert to seconds since epoch */
+ time_t secs = mktime( &tm );
+
+#if 0 /* DEBUG */
+ printf( "\n %u s \n", secs);
+#endif /* 0 */
+
+ if (secs == (time_t)-1) /* if invalid time, quit */
+ return 0;
+ if (!_conv( secs, 10, '\0')) /* if conversion failed, quit */
+ return 0;
+ continue;
+ }
+
case 'T':
if (!_fmt("%H:%M:%S", t, upcase))
return 0;
@@ -196,6 +362,37 @@ _fmt(const char *format, const struct tm
2, pad))
return 0;
continue;
+
+/*
+ * %V is replaced by the ISO 8601 week number (see above)
+ * as a decimal number (01-53). [tm_year, tm_wday, tm_yday]
+ */
+ case 'V':
+ if (!_conv( iso_week( YEAR( t->tm_year ), t->tm_yday, t->tm_wday),
+ 2, pad))
+ return 0;
+ continue;
+
+/*
+ * %g is replaced by the last 2 digits of the week-based year (see above)
+ * as a decimal number (00-99). [tm_year, tm_wday, tm_yday]
+ */
+ case 'g':
+ if (!_conv( iso_year( YEAR( t->tm_year ), t->tm_yday, t->tm_wday)
+ % CENTURY_YEARS, 2, pad))
+ return 0;
+ continue;
+
+/*
+ * %G is replaced by the week-based year (see above) as a
+ * decimal number (e.g., 1997). [tm_year, tm_wday, tm_yday]
+ */
+ case 'G':
+ if (!_conv( iso_year( YEAR( t->tm_year ), t->tm_yday, t->tm_wday),
+ 4, pad))
+ return 0;
+ continue;
+
case 'W':
if (!_conv((t->tm_yday + 7 -
(t->tm_wday ? (t->tm_wday - 1) : 6))
@@ -215,24 +412,31 @@ _fmt(const char *format, const struct tm
return 0;
continue;
case 'y':
- case 'g':
if (!_conv((t->tm_year + TM_YEAR_BASE) % 100, 2, pad))
return 0;
continue;
case 'Y':
- case 'G':
if (!_conv(t->tm_year + TM_YEAR_BASE, 4, pad))
return 0;
continue;
case 'z':
- if (!_add(t->__tm_gmtoff<0 ? "-" : "+", 0))
- return 0;
- if (!_conv(t->__tm_gmtoff<0 ? -t->__tm_gmtoff : t->__tm_gmtoff, 4, pad))
- return 0;
+/* if time zone available, do conversion */
+ if (t->tm_isdst >= 0 && t->__tm_zone && *t->__tm_zone)
+ {
+ int offset = abs( t->__tm_gmtoff ) / MIN_SECS;
+
+ if (!_add(t->__tm_gmtoff<0 ? "-" : "+", 0))
+ return 0;
+ if (!_conv( offset / HOUR_MINS * 100 + offset % HOUR_MINS,
+ 4, pad))
+ return 0;
+ }
continue;
case 'Z':
- if (!t->tm_zone || !_add(t->tm_zone, upcase))
- return 0;
+/* if time zone available, do conversion */
+ if (t->tm_isdst >= 0 && t->__tm_zone && *t->__tm_zone)
+ if (!_add(t->__tm_zone, upcase))
+ return 0;
continue;
case '%':
/*
Index: djgpp/src/libc/ansi/time/strftime.c
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/time/strftime.c,v
retrieving revision 1.6
diff -p -u -t -r1.6 strftime.c
--- djgpp/src/libc/ansi/time/strftime.c 8 Nov 2003 12:19:40 -0000 1.6
+++ djgpp/src/libc/ansi/time/strftime.c 20 Mar 2004 14:34:59 -0000
@@ -6,8 +6,52 @@
#include <string.h>
#include <time.h>
#include <ctype.h>
+#include <stdlib.h>
+#if 0 /* DEBUG */
+#include <stdio.h>
+#endif /* 0 */
-#define TM_YEAR_BASE 1900
+
+
+#define TM_YEAR_BASE 1900 /* tm year epoch base */
+#define LEAP_CENTURIES 4 /* centuries between leap centuries */
+#define CENTURY_YEARS 100 /* years per century */
+#define LEAP_CENTURY_YEARS (LEAP_CENTURIES * CENTURY_YEARS) /* years */
+#define LEAP_YEARS 4 /* years between leap years */
+#define YEAR_DAYS 365 /* days in common year */
+#define HOUR_MINS 60 /* minutes per hour */
+#define MIN_SECS 60 /* seconds per minute */
+
+
+#define FMT_DATE_ISO "%Y-%m-%d" /* ISO date format */
+
+
+/* convert tm year to Gregorian year */
+#define YEAR(tm_y) ((tm_y) + TM_YEAR_BASE)
+/* test if Gregorian year is a leap year */
+#define IS_LEAP_YEAR(y) (!((y) % LEAP_YEARS) && (((y) % CENTURY_YEARS) \
+ || !((y) % LEAP_CENTURY_YEARS)))
+
+
+
+/* tm wday values */
+typedef enum weekday
+{
+ SUNDAY,
+ MONDAY,
+ TUESDAY,
+ WEDNESDAY,
+ THURSDAY,
+ FRIDAY,
+ SATURDAY,
+ WEEK_DAYS
+
+} weekday; /* enum */
+
+
+
+
+/* locale dependent strings */
static const char *afmt[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
@@ -24,11 +68,92 @@ static const char *Bfmt[] = {
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December",
};
+
+
+/* locale dependent formats */
+
char __dj_date_format[10] = "%m/%d/%y";
char __dj_time_format[16] = "%H:%M:%S";
-static size_t gsize;
-static char *pt;
+
+/* internal global variables */
+
+static size_t gsize; /* user buffer space left */
+static char *pt; /* next char to use in buffer */
+
+
+
+/* calculate ISO year from Gregorian year and days of year and week */
+static int
+iso_year( int year, int doy, int dow)
+{
+ int ISOyear = year; /* ISO year may not be same as Gregorian */
+ int near_thu = doy + THURSDAY - (dow ? dow : WEEK_DAYS);
+ /* day in year of closest Thu to date */
+
+
+#if 0 /* DEBUG */
+ printf( "year %d doy %d dow %d thu %d near thu %d max %d \n",
+ year, doy, dow, THURSDAY, near_thu, YEAR_DAYS + IS_LEAP_YEAR( year ));
+#endif /* 0 */
+
+/* if nearest Thu before start of year, then it's in last year */
+ if (near_thu < 0)
+ {
+ --ISOyear;
+ }
+ else
+/* if nearest Thu after end of year, then it's in next year */
+ if (near_thu >= YEAR_DAYS + IS_LEAP_YEAR( year ))
+ {
+ ++ISOyear;
+ }
+
+ return ISOyear;
+
+} /* iso_year() */
+
+
+
+/* calculate ISO week from Gregorian year and days of year and week */
+static int
+iso_week( int year, int doy, int dow)
+{
+ int near_thu = doy + THURSDAY - (dow ? dow : WEEK_DAYS);
+ /* day in year of closest Thu to date */
+
+
+#if 0 /* DEBUG */
+ printf( "year %d doy %d dow %d thu %d near thu %d"
+ " max prev %d this %d \n",
+ year, doy, dow, THURSDAY, near_thu,
+ YEAR_DAYS + IS_LEAP_YEAR( year - 1 ),
+ YEAR_DAYS + IS_LEAP_YEAR( year ));
+#endif /* 0 */
+
+/* if nearest Thu before start of year, then it's in last year */
+ if (near_thu < 0)
+ {
+/* adjust day to make it part of last year */
+ near_thu += YEAR_DAYS + IS_LEAP_YEAR( year - 1 );
+ }
+ else
+/* if nearest Thu after end of year, then it's in next year */
+ if (near_thu >= YEAR_DAYS + IS_LEAP_YEAR( year ))
+ {
+/* adjust day to make it part of next year */
+ near_thu -= YEAR_DAYS + IS_LEAP_YEAR( year );
+ }
+
+#if 0 /* DEBUG */
+ printf( "near thu %d week %d \n", near_thu, near_thu / WEEK_DAYS + 1);
+#endif /* 0 */
+
+ return near_thu / WEEK_DAYS + 1; /* 0-365 -> 0-52 -> 1-53 */
+
+} /* iso_week() */
+
+
static int
_add(const char *str, int upcase)
@@ -47,7 +172,7 @@ _add(const char *str, int upcase)
static int
_conv(int n, int digits, char pad)
{
- static char buf[10];
+ static char buf[12];
char *p = buf + sizeof(buf) - 2;
do {
@@ -70,6 +195,10 @@ _fmt(const char *format, const struct tm
if (*format == '%')
{
int pad = '0', space=' ';
+ int era = 0; /* locale era modifier */
+ int ordinal = 0; /* locale ordinal modifier */
+
+
if (format[1] == '_')
pad = space = ' ', format++;
if (format[1] == '-')
@@ -78,6 +207,10 @@ _fmt(const char *format, const struct tm
pad = space = '0', format++;
if (format[1] == '^')
upcase = 1, format++;
+ if (format[1] == 'E') /* if locale era modifier */
+ era = 1, format++;
+ if (format[1] == 'O') /* if locale ordinal modifier */
+ ordinal = 1, format++;
switch(*++format)
{
@@ -129,6 +262,16 @@ _fmt(const char *format, const struct tm
if (!_conv(t->tm_mday, 2, pad))
return 0;
continue;
+
+/*
+ * %F is equivalent to ``%Y-%m-%d'' (the ISO 8601 date format).
+ * [tm_year, tm_mon, tm_mday]
+ */
+ case 'F':
+ if (!_fmt( FMT_DATE_ISO, t, upcase))
+ return 0;
+ continue;
+
case 'H':
if (!_conv(t->tm_hour, 2, pad))
return 0;
@@ -179,6 +322,29 @@ _fmt(const char *format, const struct tm
if (!_conv(t->tm_sec, 2, pad))
return 0;
continue;
+
+/*
+ * + %s is replaced by the number of seconds since the epoch
+ * 1970-01-01 00:00:00 as a decimal number. [all]
+ */
+ case 's':
+ {
+/* copy user tm in case modified */
+ struct tm tm = *t;
+/* convert to seconds since epoch */
+ time_t secs = mktime( &tm );
+
+#if 0 /* DEBUG */
+ printf( "\n %u s \n", secs);
+#endif /* 0 */
+
+ if (secs == (time_t)-1) /* if invalid time, quit */
+ return 0;
+ if (!_conv( secs, 10, '\0')) /* if conversion failed, quit */
+ return 0;
+ continue;
+ }
+
case 'T':
if (!_fmt("%H:%M:%S", t, upcase))
return 0;
@@ -196,6 +362,37 @@ _fmt(const char *format, const struct tm
2, pad))
return 0;
continue;
+
+/*
+ * %V is replaced by the ISO 8601 week number (see above)
+ * as a decimal number (01-53). [tm_year, tm_wday, tm_yday]
+ */
+ case 'V':
+ if (!_conv( iso_week( YEAR( t->tm_year ), t->tm_yday, t->tm_wday),
+ 2, pad))
+ return 0;
+ continue;
+
+/*
+ * %g is replaced by the last 2 digits of the week-based year (see above)
+ * as a decimal number (00-99). [tm_year, tm_wday, tm_yday]
+ */
+ case 'g':
+ if (!_conv( iso_year( YEAR( t->tm_year ), t->tm_yday, t->tm_wday)
+ % CENTURY_YEARS, 2, pad))
+ return 0;
+ continue;
+
+/*
+ * %G is replaced by the week-based year (see above) as a
+ * decimal number (e.g., 1997). [tm_year, tm_wday, tm_yday]
+ */
+ case 'G':
+ if (!_conv( iso_year( YEAR( t->tm_year ), t->tm_yday, t->tm_wday),
+ 4, pad))
+ return 0;
+ continue;
+
case 'W':
if (!_conv((t->tm_yday + 7 -
(t->tm_wday ? (t->tm_wday - 1) : 6))
@@ -215,24 +412,31 @@ _fmt(const char *format, const struct tm
return 0;
continue;
case 'y':
- case 'g':
if (!_conv((t->tm_year + TM_YEAR_BASE) % 100, 2, pad))
return 0;
continue;
case 'Y':
- case 'G':
if (!_conv(t->tm_year + TM_YEAR_BASE, 4, pad))
return 0;
continue;
case 'z':
- if (!_add(t->__tm_gmtoff<0 ? "-" : "+", 0))
- return 0;
- if (!_conv(t->__tm_gmtoff<0 ? -t->__tm_gmtoff : t->__tm_gmtoff, 4, pad))
- return 0;
+/* if time zone available, do conversion */
+ if (t->tm_isdst >= 0 && t->__tm_zone && *t->__tm_zone)
+ {
+ int offset = abs( t->__tm_gmtoff ) / MIN_SECS;
+
+ if (!_add(t->__tm_gmtoff<0 ? "-" : "+", 0))
+ return 0;
+ if (!_conv( offset / HOUR_MINS * 100 + offset % HOUR_MINS,
+ 4, pad))
+ return 0;
+ }
continue;
case 'Z':
- if (!t->tm_zone || !_add(t->tm_zone, upcase))
- return 0;
+/* if time zone available, do conversion */
+ if (t->tm_isdst >= 0 && t->__tm_zone && *t->__tm_zone)
+ if (!_add(t->__tm_zone, upcase))
+ return 0;
continue;
case '%':
/*
Index: djgpp/src/libc/ansi/time/strftime.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/time/strftime.txh,v
retrieving revision 1.6
diff -p -u -t -r1.6 strftime.txh
--- djgpp/src/libc/ansi/time/strftime.txh 8 Nov 2003 12:19:40 -0000 1.6
+++ djgpp/src/libc/ansi/time/strftime.txh 20 Mar 2004 14:34:59 -0000
@@ -5,145 +5,187 @@
@example
#include <time.h>
-size_t strftime(char *buf, size_t n, const char *format,
- const struct tm *time_info);
+size_t strftime(char * restrict string,
+ size_t maxsize,
+ const char * restrict format,
+ const struct tm * restrict time_info);
+
@end example
@subheading Description
-This function formats the time data in @var{time_info} according to the
-given @var{format} and stores it in @var{buf}, not exceeding @var{n}
-bytes.
-
-The format string is like @code{printf} in that any character other than
-@code{%} is added to the output string, and for each character following
-a @code{%} a pattern is added to the string as follows, with the
-examples as if the time was Friday, October 1, 1993, at 03:30:34 PM EDT:
+This function formats the calendar time data in @var{time_info}
+according to the given format string @var{format}
+and stores it in the output string @var{string},
+not exceeding @var{maxsize} bytes.
+
+The format string is like @code{sprintf} in that any character other
+than the flag character @code{%} is added to the output string, and
+depending on the conversion specifier character after the @code{%},
+characters are added to the output string, with the following examples
+shown as if the time was Friday, October 1, 1993, at 03:30:34 PM EDT:
@table @code
@item %A
-The full weekday name (@code{Friday})
+The full weekday name represented according to the
+current locale: (@code{Friday}) in the C locale
@item %a
-The abbreviated weekday name (@code{Fri})
+The abbreviated weekday name represented according to
+the current locale: (@code{Fri}) in the C locale
@item %B
-The full month name (@code{October})
+The full month name represented according to the
+current locale: (@code{October}) in the C locale
@item %b
@itemx %h
-The abbreviated month name (@code{Oct})
+The abbreviated month name represented according to
+the current locale: (@code{Oct}) in the C locale
@item %C
-Short for @code{%a %b %e %H:%M:%S %Y} (@code{Fri Oct 1 15:30:34 1993})
+The century of the year, zero padded to two digits (@code{19})
@item %c
-Short for @code{%m/%d/%y %H:%M:%S} (@code{10/01/93 15:30:34})
+The date and time represented according to the current locale: short for
+@code{%a %b %e %T %Y} (@code{Fri Oct 1 15:30:34 1993}) in the C locale
+
+@item %D
+
+The date in POSIX format; short for @code{%m/%d/%y} (@code{10/01/93})
+
+@item %d
+
+The day of the month (01-31), zero padded to two digits (@code{01})
@item %e
-The day of the month, blank padded to two characters (@code{ 2})
+The day of the month (1-31), blank padded to two characters (@code{ 1})
-@item %D
+@item %F
-Short for @code{%m/%d/%y} (@code{10/01/93})
+The date in ISO format; short for @code{%Y-%m-%d} (@code{1993-10-01})
-@item %d
+@item %G
-The day of the month, zero padded to two characters (@code{02})
+The ISO week based year, zero padded to four digits (@code{1993})
+
+@item %g
+
+The ISO week based year of the century (00-99),
+zero padded to two digits (@code{93})
@item %H
-The hour (0-24), zero padded to two characters (@code{15})
+The hour in the 24 hour clock (00-24), zero padded to two digits (@code{15})
@item %I
-The hour (1-12), zero padded to two characters (@code{03})
+The hour in the 12 hour clock (01-12), zero padded to two digits (@code{03})
@item %j
-The Julian day, zero padded to three characters (@code{275})
+The Julian day of the year (001-366), zero padded to three digits (@code{274})
@item %k
-The hour (0-24), space padded to two characters (@code{15})
+The hour in the 24 hour clock (0-24),
+space padded to two characters (@code{15})
@item %l
-The hour (1-12), space padded to two characters(@code{ 3})
+The hour in the 12 hour clock (1-12),
+space padded to two characters (@code{ 3})
@item %M
-The minutes, zero padded to two characters (@code{30})
+The minutes in the hour (00-59), zero padded to two digits (@code{30})
@item %m
-The month (1-12), zero padded to two characters (@code{10})
+The month in the year (01-12), zero padded to two digits (@code{10})
@item %n
-A newline (@code{\n})
+The newline control character (@code{\n})
@item %p
-AM or PM (@code{PM})
+The AM/PM indicator in the 12 hour clock represented according
+to the current locale: (@code{PM}) in the C locale
@item %R
-Short for @code{%H:%M} (@code{15:30})
+The time in the 24 hour clock, excluding seconds;
+short for @code{%H:%M} (@code{15:30})
@item %r
-Short for @code{%I:%M:%S %p} (@code{03:30:35 PM})
+The time in the 12 hour clock represented according to the current locale:
+short for @code{%I:%M:%S %p} (@code{03:30:35 PM}) in the C locale
@item %S
-The seconds, zero padded to two characters (@code{35})
+The seconds in the minute (00-60), zero padded to two digits (@code{35});
+the maximum value is 60 to allow leap seconds to be represented
+
+@item %s
+
+The number of seconds since 1970-01-01 00:00:00 UTC (@code{749503834})
@item %T
-Short for @code{%H:%M:%S} (@code{15:30:35})
+The time in the 24 hour clock, including seconds;
+short for @code{%H:%M:%S} (@code{15:30:35})
@item %t
-A tab (@code{\t})
+The horizontal tab control character (@code{\t})
@item %U
-The week of the year, with the first week defined by the first Sunday of
-the year, zero padded to two characters (@code{39})
+The Sunday based week of the year (00-53), with the first week defined
+by the first Sunday of the year, zero padded to two digits (@code{39})
@item %u
-The day of the week (1-7) (@code{6})
+The ISO Monday based day of the week (Mon 1-Sun 7) (@code{5})
+
+@item %V
+
+The ISO Monday based week of the year (01-53), with the first and last
+weeks defined by the first and last Thursdays of the year, zero padded
+to two digits (@code{39})
@item %W
-The week of the year, with the first week defined by the first Monday of
-the year, zero padded to two characters (@code{39})
+The Monday based week of the year (00-53), with the first week defined
+by the first Monday of the year, zero padded to two digits (@code{39})
@item %w
-The day of the week (0-6) (@code{5})
+The Sunday based day of the week (Sun 0-Sat 6) (@code{5})
@item %x
-Date represented according to the current locale.
+The date represented according to the current locale:
+short for @code{%D} (@code{10/01/93}) in the C locale
@item %X
-Time represented according to the current locale.
+The time in the 24 hour clock represented according to the current
+locale: short for @code{%T} (@code{15:30:34}) in the C locale
@item %y
-The year (00-99) of the century (@code{93})
+The year of the century (00-99), zero padded to two digits (@code{93})
@item %Y
@@ -151,11 +193,54 @@ The year, zero padded to four digits (@c
@item %Z
-The timezone abbreviation (@code{EDT})
+The timezone abbreviation represented according
+to the current locale (@code{EDT})
+
+@item %z
+
+The ISO timezone offset in [+-]HHMM format (@code{-0400})
@item %%
-A percent symbol (@code{%})
+The percent symbol character (@code{%})
+
+@end table
+
+@subheading Conversion Modifiers
+
+The following conversion modifier characters affect the conversion into
+the output string and must appear after the flag character (@code{%})
+and before the conversion specifier character.
+
+@table @code
+
+@item -
+
+Left justify the output with no padding characters,
+for example @code{%-d} gives @code{1}
+
+@item 0
+
+Right justify the output with zero digit padding,
+for example @code{%0d} gives @code{01}
+
+@item _
+
+Right justify the output with space character padding,
+for example @code{%_d} gives @code{ 1}
+
+@item ^
+
+Convert lowercase letters in the output to uppercase,
+for example @code{%^a} gives @code{FRI}
+
+@item E
+
+Locale alternate era output format; ignored in the C locale
+
+@item O
+
+Locale alternate digits output format; ignored in the C locale
@end table
@@ -165,7 +250,7 @@ The number of characters stored.
@subheading Portability
-@portability ansi, posix
+@portability ANSI, POSIX
@subheading Example
Index: djgpp/zoneinfo/src/strftime.c
===================================================================
RCS file: /cvs/djgpp/djgpp/zoneinfo/src/strftime.c,v
retrieving revision 1.2
diff -p -u -t -r1.2 strftime.c
--- djgpp/zoneinfo/src/strftime.c 28 May 1998 16:56:10 -0000 1.2
+++ djgpp/zoneinfo/src/strftime.c 20 Mar 2004 14:36:43 -0000
@@ -1,6 +1,6 @@
#ifndef lint
#ifndef NOID
-static char elsieid[] = "@(#)strftime.c 7.57";
+static char elsieid[] = "@(#)strftime.c 7.62";
/*
** Based on the UCB version with the ID appearing below.
** This is ANSIish only when "multibyte character == plain character".
@@ -80,24 +80,21 @@ static const struct lc_time_T C_time_loc
/*
** x_fmt
- ** Since the C language standard calls for
- ** "date, using locale's date format," anything goes.
+ ** C99 requires this format.
** Using just numbers (as here) makes Quakers happier;
** it's also compatible with SVR4.
- **
- ** XXX--might it be better to use the year-2000 friendly
- ** %Y-%m-%d
- ** here?
*/
"%m/%d/%y",
/*
** c_fmt
+ ** C99 requires this format.
+ ** Previously this code used "%D %X", but we now conform to C99.
** Note that
- ** "%a %b %d %H:%M:%S %Y"
+ ** "%a %b %d %H:%M:%S %Y"
** is used by Solaris 2.3.
*/
- "%D %X", /* %m/%d/%y %H:%M:%S */
+ "%a %b %e %T %Y",
/* am */
"AM",
@@ -236,20 +233,21 @@ label:
case 'E':
case 'O':
/*
- ** POSIX locale extensions, a la
- ** Arnold Robbins' strftime version 3.0.
+ ** C99 locale modifiers.
** The sequences
- ** %Ec %EC %Ex %Ey %EY
+ ** %Ec %EC %Ex %EX %Ey %EY
** %Od %oe %OH %OI %Om %OM
** %OS %Ou %OU %OV %Ow %OW %Oy
** are supposed to provide alternate
** representations.
- ** (ado, 1993-05-24)
*/
goto label;
case 'e':
pt = _conv(t->tm_mday, "%2d", pt, ptlim);
continue;
+ case 'F':
+ pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
+ continue;
case 'H':
pt = _conv(t->tm_hour, "%02d", pt, ptlim);
continue;
@@ -492,10 +490,66 @@ label:
pt = _add(t->TM_ZONE, pt, ptlim);
else
#endif /* defined TM_ZONE */
- if (t->tm_isdst == 0 || t->tm_isdst == 1) {
- pt = _add(tzname[t->tm_isdst],
+ if (t->tm_isdst >= 0)
+ pt = _add(tzname[t->tm_isdst != 0],
pt, ptlim);
- } else pt = _add("?", pt, ptlim);
+ /*
+ ** C99 says that %Z must be replaced by the
+ ** empty string if the time zone is not
+ ** determinable.
+ */
+ continue;
+ case 'z':
+ {
+ int diff;
+ char const * sign;
+
+ if (t->tm_isdst < 0)
+ continue;
+#ifdef TM_GMTOFF
+ diff = t->TM_GMTOFF;
+#else /* !defined TM_GMTOFF */
+ /*
+ ** C99 says that the UTC offset must
+ ** be computed by looking only at
+ ** tm_isdst. This requirement is
+ ** incorrect, since it means the code
+ ** must rely on magic (in this case
+ ** altzone and timezone), and the
+ ** magic might not have the correct
+ ** offset. Doing things correctly is
+ ** tricky and requires disobeying C99;
+ ** see GNU C strftime for details.
+ ** For now, punt and conform to the
+ ** standard, even though it's incorrect.
+ **
+ ** C99 says that %z must be replaced by the
+ ** empty string if the time zone is not
+ ** determinable, so output nothing if the
+ ** appropriate variables are not available.
+ */
+ if (t->tm_isdst == 0)
+#ifdef USG_COMPAT
+ diff = -timezone;
+#else /* defined USG_COMPAT */
+ continue;
+#endif /* !defined USG_COMPAT */
+ else
+#ifdef ALTZONE
+ diff = -altzone;
+#else /* !defined ALTZONE */
+ continue;
+#endif /* !defined ALTZONE */
+#endif /* !defined TM_GMTOFF */
+ if (diff < 0) {
+ sign = "-";
+ diff = -diff;
+ } else sign = "+";
+ pt = _add(sign, pt, ptlim);
+ diff /= 60;
+ pt = _conv((diff/60)*100 + diff%60,
+ "%04d", pt, ptlim);
+ }
continue;
case '+':
pt = _fmt(Locale->date_fmt, t, pt, ptlim,
@@ -503,10 +557,10 @@ label:
continue;
case '%':
/*
- * X311J/88-090 (4.12.3.5): if conversion char is
- * undefined, behavior is undefined. Print out the
- * character itself as printf(3) also does.
- */
+ ** X311J/88-090 (4.12.3.5): if conversion char is
+ ** undefined, behavior is undefined. Print out the
+ ** character itself as printf(3) also does.
+ */
default:
break;
}
Index: djgpp/tests/libc/ansi/time/makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/ansi/time/makefile,v
retrieving revision 1.3
diff -p -u -t -r1.3 makefile
--- djgpp/tests/libc/ansi/time/makefile 8 Nov 2003 12:19:41 -0000 1.3
+++ djgpp/tests/libc/ansi/time/makefile 20 Mar 2004 14:37:34 -0000
@@ -2,6 +2,8 @@
SRC += mktime.c
SRC += strftime.c
+SRC += strftiml.c
+SRC += strftimt.c
SRC += tzinfo.c
SRC += xstrftm.c
Index: djgpp/tests/libc/ansi/time/strftime.c
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/ansi/time/strftime.c,v
retrieving revision 1.3
diff -p -u -t -r1.3 strftime.c
--- djgpp/tests/libc/ansi/time/strftime.c 26 May 2002 16:12:46 -0000 1.3
+++ djgpp/tests/libc/ansi/time/strftime.c 20 Mar 2004 14:37:34 -0000
@@ -11,6 +11,86 @@
#include <time.h>
#include <libc/unconst.h>
+
+#undef CMP
+#define CMP(Fmt, Expected) n_fail += compare ((Fmt), tm, (Expected))
+
+
+typedef struct cmp
+{
+ char *fmt;
+ char *expected;
+
+} cmp;
+
+
+struct cmp comparisons[] =
+{
+ { "%-m", "1" }, /* GNU */
+ { "%A", "Friday" },
+ { "%^A", "FRIDAY" }, /* GNU */
+ { "%B", "January" },
+ { "%^B", "JANUARY" },
+ { "%C", "19" }, /* POSIX.2 */
+ { "%D", "01/09/70" }, /* POSIX.2 */
+ { "%E", "E" }, /* C99 */
+ { "%F", "1970-01-09"}, /* C99 */
+ { "%G", "1970" }, /* GNU */
+ { "%H", "13" },
+ { "%I", "01" },
+ { "%J", "J" },
+ { "%K", "K" },
+ { "%L", "L" },
+ { "%M", "06" },
+ { "%N", "N" },
+ { "%O", "O" }, /* C99 */
+ { "%P", "P" },
+ { "%Q", "Q" },
+ { "%R", "13:06" }, /* POSIX.2 */
+ { "%S", "07" },
+ { "%T", "13:06:07" }, /* POSIX.2 */
+ { "%U", "01" },
+ { "%V", "02" },
+ { "%W", "01" },
+ { "%X", "13:06:07" },
+ { "%Y", "1970" },
+ { "%Z", "GMT" },
+ { "%_m", " 1" }, /* GNU */
+ { "%a", "Fri" },
+ { "%^a", "FRI" },
+ { "%b", "Jan" },
+ { "%^b", "JAN" },
+ { "%c", "Fri Jan 9 13:06:07 1970" },
+ { "%^c", "FRI JAN 9 13:06:07 1970" },
+ { "%d", "09" },
+ { "%e", " 9" }, /* POSIX.2 */
+ { "%f", "f" }, /* ISO */
+ { "%g", "70" }, /* GNU */
+ { "%h", "Jan" }, /* POSIX.2 */
+ { "%^h", "JAN" },
+ { "%i", "i" },
+ { "%j", "009" },
+ { "%k", "13" }, /* GNU */
+ { "%l", " 1" }, /* GNU */
+ { "%m", "01" },
+ { "%n", "\n" }, /* POSIX.2 */
+ { "%o", "o" },
+ { "%p", "PM" },
+ { "%q", "q" },
+ { "%r", "01:06:07 PM"}, /* POSIX.2 */
+ { "%s", "738367" }, /* GNU */
+ { "%t", "\t" }, /* POSIX.2 */
+ { "%u", "5" }, /* POSIX.2 */
+ { "%v", "v" },
+ { "%w", "5" },
+ { "%x", "01/09/70" },
+ { "%y", "70" },
+ { "%z", "+0000" }, /* GNU */
+ { "%%", "%" },
+
+}; /* comparisons[] */
+
+
static int
compare (const char *fmt, const struct tm *tm, const char *expected)
{
@@ -19,7 +99,8 @@ compare (const char *fmt, const struct t
if (strcmp (buf, expected))
{
#if 1
- printf ("fmt: \"%s\", expected \"%s\", got \"%s\"\n",
+ printf ("\n fmt: \"%-3s\" expected: \"%s\" \n"
+ " got: \"%s\" \n",
fmt, expected, buf);
#endif
return 1;
@@ -27,68 +108,24 @@ compare (const char *fmt, const struct t
return 0;
}
+
int
main (void)
{
- int n_fail = 0;
- struct tm *tm;
- time_t t = 738367; /* Fri Jan 9 13:06:07 1970 */
- tm = gmtime (&t);
+ struct cmp * c;
+ int n_fail = 0;
+ time_t t = 738367; /* Fri Jan 9 13:06:07 1970 */
+ struct tm * tm = gmtime (&t);
+
/* This is necessary to make strftime give consistent zone strings and
e.g., seconds since the epoch (%s). */
putenv (unconst("TZ=GMT0", char *));
-#undef CMP
-#define CMP(Fmt, Expected) n_fail += compare ((Fmt), tm, (Expected))
-
- CMP ("%-m", "1"); /* GNU */
- CMP ("%A", "Friday");
- CMP ("%^A", "FRIDAY"); /* The ^ is a GNU extension. */
- CMP ("%B", "January");
- CMP ("%^B", "JANUARY");
- CMP ("%C", "19"); /* POSIX.2 */
- CMP ("%D", "01/09/70"); /* POSIX.2 */
- CMP ("%G", "1970"); /* GNU */
- CMP ("%H", "13");
- CMP ("%I", "01");
- CMP ("%M", "06");
- CMP ("%M", "06");
- CMP ("%R", "13:06"); /* POSIX.2 */
- CMP ("%S", "07");
- CMP ("%T", "13:06:07"); /* POSIX.2 */
- CMP ("%U", "01");
- CMP ("%V", "02");
- CMP ("%W", "01");
- CMP ("%X", "13:06:07");
- CMP ("%Y", "1970");
- CMP ("%Z", "GMT");
- CMP ("%_m", " 1"); /* GNU */
- CMP ("%a", "Fri");
- CMP ("%^a", "FRI");
- CMP ("%b", "Jan");
- CMP ("%^b", "JAN");
- CMP ("%c", "Fri Jan 9 13:06:07 1970");
- CMP ("%^c", "FRI JAN 9 13:06:07 1970");
- CMP ("%d", "09");
- CMP ("%e", " 9"); /* POSIX.2 */
- CMP ("%g", "70"); /* GNU */
- CMP ("%h", "Jan"); /* POSIX.2 */
- CMP ("%^h", "JAN");
- CMP ("%j", "009");
- CMP ("%k", "13"); /* GNU */
- CMP ("%l", " 1"); /* GNU */
- CMP ("%m", "01");
- CMP ("%n", "\n"); /* POSIX.2 */
- CMP ("%p", "PM");
- CMP ("%r", "01:06:07 PM"); /* POSIX.2 */
- CMP ("%s", "738367"); /* GNU */
- CMP ("%t", "\t"); /* POSIX.2 */
- CMP ("%u", "5"); /* POSIX.2 */
- CMP ("%w", "5");
- CMP ("%x", "01/09/70");
- CMP ("%y", "70");
- CMP ("%z", "+0000"); /* GNU */
+ for (c = comparisons; c < comparisons + sizeof comparisons / sizeof *comparisons; ++ c)
+ {
+ CMP( c->fmt, c->expected);
+ }
exit (n_fail ? 1 : 0);
}
Index: djgpp/tests/libc/ansi/time/xstrftm.c
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/ansi/time/xstrftm.c,v
retrieving revision 1.1
diff -p -u -t -r1.1 xstrftm.c
--- djgpp/tests/libc/ansi/time/xstrftm.c 8 Nov 2003 12:19:41 -0000 1.1
+++ djgpp/tests/libc/ansi/time/xstrftm.c 20 Mar 2004 14:37:34 -0000
@@ -1,19 +1,38 @@
+#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
-extern char __dj_date_format[10];
-extern char __dj_time_format[16];
+extern char __dj_date_format[];
+extern char __dj_time_format[];
int main(int ac, char *av[])
{
char buf[99];
time_t t = time(NULL);
+ char *locale;
+/* default C locale */
+ locale = setlocale( LC_TIME, NULL );
+ strftime(buf, sizeof(buf), "%x %X", gmtime(&t));
+ printf("%s locale %s\n", locale, buf);
+
+/* current locale */
+ locale = setlocale( LC_TIME, "" );
+ strftime(buf, sizeof(buf), "%x %X", gmtime(&t));
+ printf("%s locale %s\n", locale, buf);
+
+/* custom locale */
+ locale = "custom";
strcpy(__dj_date_format, "%d|%m|%Y");
strcpy(__dj_time_format, "[%H|%M|%S]");
+ strftime(buf, sizeof(buf), "%x %X", gmtime(&t));
+ printf("%s locale %s\n", locale, buf);
+/* default C locale */
+ locale = setlocale( LC_TIME, "C" );
strftime(buf, sizeof(buf), "%x %X", gmtime(&t));
- printf("%s\n", buf);
+ printf("%s locale %s\n", locale, buf);
+
return 0;
}
--- /dev/null 2004-03-20 07:38:32.000000000 -0700
+++ djgpp/tests/libc/ansi/time/strftiml.c 2004-02-18 22:32:40.000000000 -0700
@@ -0,0 +1,38 @@
+/* t.c - basic time conversion calls */
+
+
+#include <stdio.h>
+#include <time.h>
+
+
+
+int main( void )
+{
+ char *p;
+ struct tm *tm_now = NULL;
+ time_t t_now = 0;
+ int n;
+ char fmts[] = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ%";
+ char fmt[] = "%%\t'%%'\n";
+ char msg[1024];
+
+
+ t_now = time( NULL ); /* get current time */
+ tm_now = localtime( &t_now ); /* get local time */
+ t_now = mktime( tm_now ); /* convert back to time */
+
+ p = ctime( &t_now ); /* convert time_t to string */
+ printf( "ctime '%s'\n", p);
+ p = asctime( tm_now ); /* convert struct tm to string */
+ printf( "asctime '%s'\n", p);
+ for (p = fmts; p < fmts + sizeof( fmts ); ++p)
+ {
+ n = sprintf( fmt, "%c\t'%%%c'\n", *p, *p);
+ n = strftime( msg, sizeof( msg ), fmt, tm_now);
+ n = printf( "%s", msg);
+ }
+
+ return 0;
+
+} /* main() */
+
--- /dev/null 2004-03-20 07:38:39.000000000 -0700
+++ djgpp/tests/libc/ansi/time/strftimt.c 2004-02-24 02:30:52.000000000 -0700
@@ -0,0 +1,192 @@
+/* @(#)tstrftm.c 1.0 18 Feb 2004 BWI */
+
+
+
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+
+
+static const struct
+{
+ const char * locale;
+ const char * tz;
+ const char * format;
+ const char * text;
+ struct tm tm;
+} tests[] =
+/* locale tz format text */
+/* sec min hr day mon year wdy ydy dst */
+/* -1 -1970 0-6 0-* -=+ */
+{
+/* 1 */ { "C", "Universal", "%u %Y-%m-%d %H:%M:%S", "7 1999-01-31 21:01:10",
+ { 10, 1, 21, 31, 0, 99, 0, 30,-1 }},
+/* 2 */ { "C", "Universal", "%u %C%y.%b.%e %I.%M %p","7 1999.Feb.28 09.01 PM",
+ { 10, 1, 21, 28, 1, 99, 0, 58,-1 }},
+/* 3 */ { "C", "Universal", "%w %y/%h %j %k-%M-%S", "2 00/Feb 060 16-30-46",
+ { 46, 30, 16, 29, 1, 100, 2, 59,-1 }},
+/* 4 */ { "C", "Universal", "%a %y %B %e %l:%M %p", "Sat 00 January 1 12:34 PM",
+ { 12, 34, 12, 1, 0, 100, 6, 0,-1 }},
+/* 5 */ { "C", "Universal", "%A %y-%m-%d %T","Friday 00-03-03 17:41:01",
+ { 1, 41, 17, 3, 2, 100, 5, 62,-1 }},
+/* 6 */ { "C", "Universal", "%W %F %R", "36 1999-09-09 21:01",
+ { 10, 1, 21, 9, 8, 99, 4, 251,-1 }},
+/* 7 */ { "C", "Universal", "%U %D %X", "18 05/02/99 12:34:12",
+ { 12, 34, 12, 2, 4, 99, 1, 121,-1 }},
+/* 8 */ { "C", "Universal", "%x %t %r", "05/21/01 \t 09:01:10 PM",
+ { 10, 1, 21, 21, 4, 101, 1, 140,-1 }},
+/* 9 */ { "C", "Universal", "%c %n", "Mon May 21 16:30:46 2001 \n",
+ { 46, 30, 16, 21, 4, 101, 1, 140,-1 }},
+/* 10 */ { "C", "Universal", "%F %T %Z", "2000-01-05 17:41:01 UTC",
+ { 1, 41, 17, 5, 0, 100, 3, 4,-1 }},
+/* 11 */ { "C", "Universal", "%F %T %z", "2000-01-05 17:41:01 +0000",
+ { 1, 41, 17, 5, 0, 100, 3, 4,-1 }},
+/* 12 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "1975-12-29 1976-W01-1 1975-w52-1 1975-u52-1",
+ { 0, 0, 0, 29, 11, 75, 0, 362,-1 }},
+/* 13 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "1977-01-02 1976-W53-7 1977-w00-7 1977-u01-0",
+ { 0, 0, 0, 2, 0, 77, 0, 1,-1 }},
+/* 14 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "1999-01-02 1998-W53-6 1999-w00-6 1999-u00-6",
+ { 0, 0, 0, 2, 0, 99, 5, 1,-1 }},
+/* 15 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "1999-12-27 1999-W52-1 1999-w52-1 1999-u52-1",
+ { 0, 0, 0, 27, 11, 99, 0, 360,-1 }},
+/* 16 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2000-01-02 1999-W52-7 2000-w00-7 2000-u01-0",
+ { 0, 0, 0, 2, 0, 100, 0, 1,-1 }},
+/* 17 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2003-12-28 2003-W52-7 2003-w51-7 2003-u52-0",
+ { 0, 0, 0, 28, 11, 103, 0, 361,-1 }},
+/* 18 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2003-12-29 2004-W01-1 2003-w52-1 2003-u52-1",
+ { 0, 0, 0, 29, 11, 103, 0, 362,-1 }},
+/* 19 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2003-12-31 2004-W01-3 2003-w52-3 2003-u52-3",
+ { 0, 0, 0, 31, 11, 103, 2, 364,-1 }},
+/* 20 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2004-01-01 2004-W01-4 2004-w00-4 2004-u00-4",
+ { 0, 0, 0, 1, 0, 104, 3, 0,-1 }},
+/* 21 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2004-01-04 2004-W01-7 2004-w00-7 2004-u01-0",
+ { 0, 0, 0, 4, 0, 104, 0, 3,-1 }},
+/* 22 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2004-01-05 2004-W02-1 2004-w01-1 2004-u01-1",
+ { 0, 0, 0, 5, 0, 104, 0, 4,-1 }},
+/* 23 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2004-12-26 2004-W52-7 2004-w51-7 2004-u52-0",
+ { 0, 0, 0, 26, 11, 104, 0, 360,-1 }},
+/* 24 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2004-12-27 2004-W53-1 2004-w52-1 2004-u52-1",
+ { 0, 0, 0, 27, 11, 104, 0, 361,-1 }},
+/* 25 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2004-12-31 2004-W53-5 2004-w52-5 2004-u52-5",
+ { 0, 0, 0, 31, 11, 104, 4, 365,-1 }},
+/* 26 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2005-01-01 2004-W53-6 2005-w00-6 2005-u00-6",
+ { 0, 0, 0, 1, 0, 105, 5, 0,-1 }},
+/* 27 */ { "C", "Universal", "%F %G-W%V-%u %Y-w%W-%u %Y-u%U-%w", "2005-01-03 2005-W01-1 2005-w01-1 2005-u01-1",
+ { 0, 0, 0, 3, 0, 105, 0, 2,-1 }},
+/* 28 */ { "C", "Universal", "%F %g-W%V-%u %y-w%W-%u %y-u%U-%w", "2012-01-01 11-W52-7 12-w00-7 12-u01-0",
+ { 0, 0, 0, 1, 0, 112, 0, 0,-1 }},
+/* 29 */ { "C", "Universal", "%F %g-W%V-%u %y-w%W-%u %y-u%U-%w", "2012-02-29 12-W09-3 12-w09-3 12-u09-3",
+ { 0, 0, 0, 29, 01, 112, 2, 59,-1 }},
+/* 30 */ { "C", "Universal", "%F %g-W%V-%u %y-w%W-%u %y-u%U-%w", "2012-12-31 13-W01-1 12-w53-1 12-u53-1",
+ { 0, 0, 0, 31, 11, 112, 0, 365,-1 }},
+/* 31 */ { "C", "EST5EDT", "%c %Z %s W%V w%W u%U", "Fri Oct 1 15:30:34 1993 EDT 749503834 W39 w39 u39",
+ { 34, 30, 15, 1, 9, 93, 5, 273,-1 }},
+/* 32 */ { "C", "Europe/Berlin","%d.%m.%Y %T %Z","01.08.2000 05:06:07 CEST",
+ { 7, 6, 5, 1, 7, 100, 0, 0,-1 }},
+/* 33 */ { "C", "Europe/Berlin","%d.%m.%Y %T %z","01.08.2000 05:06:07 +0200",
+ { 7, 6, 5, 1, 7, 100, 0, 0,-1 }},
+#if 0 /* not yet supported */
+/* 34 */ { "ja_JP.EUC-JP","Universal", "%Y %U %a", "2001 20 \xb7\xee",
+ { 0, 0, 0, 21, 4, 101, 1, 140,-1 }},
+/* 35 */ { "ja_JP.EUC-JP","Universal", "%Y %W %a", "2001 21 \xb7\xee",
+ { 0, 0, 0, 21, 4, 101, 1, 140,-1 }},
+/* 36 */ { "ja_JP.EUC-JP","Asia/Tokyo", "%Y %W %a", "2001 21 \xb7\xee",
+ { 0, 0, 0, 21, 4, 101, 1, 140,-1 }},
+
+#endif /* 0 */
+};
+
+
+
+int
+main (int argc, char *argv[])
+{
+ time_t tt;
+ size_t t;
+ size_t len = 0;
+ int rc = 0;
+ int errs = 0;
+ struct tm tm = { 0 };
+ char text[BUFSIZ] = "";
+
+ for (t = 0; t < sizeof tests / sizeof *tests; ++t)
+ {
+ errno = 0;
+
+ if (setlocale( LC_TIME, tests[t].locale) == NULL)
+ {
+ if (errno) perror( "setlocale()" );
+ printf( "test %3d error %6s set locale '%s' category '%s'(%d) \n",
+ t + 1, "NULL", tests[t].locale, "LC_TIME", LC_TIME);
+ ++errs;
+ continue;
+ }
+
+ if ((rc = setenv( "TZ", tests[t].tz, 1)))
+ {
+ if (errno) perror( "setenv()" );
+ printf( "test %3d error %6d set environment '%s' '%s' \n",
+ t + 1, rc, "TZ", tests[t].tz);
+ ++errs;
+ continue;
+ }
+
+ tzset();
+
+ tm = tests[t].tm;
+
+ if ((tt = mktime( &tm )) == -1)
+ {
+ if (errno) perror( "mktime()" );
+ printf( "test %3d error %6d make time "
+ "%04d-%02d-%02d %02d:%02d:%02d j%03d w%01d dst %d \n",
+ t + 1, tt,
+ tests[t].tm.tm_year + 1900,
+ tests[t].tm.tm_mon + 1,
+ tests[t].tm.tm_mday,
+ tests[t].tm.tm_hour,
+ tests[t].tm.tm_min,
+ tests[t].tm.tm_sec,
+ tests[t].tm.tm_yday + 1,
+ tests[t].tm.tm_wday,
+ tests[t].tm.tm_isdst);
+ ++errs;
+ continue;
+ }
+
+ if (!(len = strftime( text, sizeof text, tests[t].format, &tm)))
+ {
+ if (errno) perror( "strftime()" );
+ printf( "test %3d error %6d time format '%s' \n",
+ t + 1, len, tests[t].format);
+ ++errs;
+ continue;
+ }
+
+ if (strlen( tests[t].text ) != strlen( text )
+ || strcmp( tests[t].text, text))
+ {
+ printf( "\n test %3d error %6s expected '%s' \n"
+ " actual '%s' \n",
+ t + 1, "FAILED", tests[t].text, text);
+ ++errs;
+ continue;
+ }
+ }
+
+ if (errs)
+ {
+ printf( "tests %2d errors %5d passed %d \n",
+ sizeof tests / sizeof *tests,
+ errs,
+ sizeof tests / sizeof *tests - errs);
+ }
+
+ return errs;
+
+} /* main() */
+
- Raw text -