delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2004/10/24/20:44:04

X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f
Date: Sun, 24 Oct 2004 18:43:49 -0600
From: Brian Inglis <Brian DOT Inglis AT SystematicSw DOT ab DOT ca>
Subject: Re: C99 strftime and Related Changes
To: djgpp-workers <djgpp-workers AT delorie DOT com>
Message-id: <puion0tjsn0bl0sjtipt4koi5qn3l2ckcd@4ax.com>
Organization: Systematic Software
MIME-version: 1.0
X-Mailer: Forte Agent 1.93/32.576 English (American)
X-MIME-Autoconverted: from quoted-printable to 8bit by delorie.com id i9P0i3wD013256
Reply-To: djgpp-workers AT delorie DOT com

On Thu, 18 Mar 2004 03:07:04 -0700, Brian Inglis
<Brian DOT Inglis AT SystematicSw DOT ab DOT ca> wrote:

>13 files, 1748 lines, 57294 bytes

Here they are inline, then.


CONTENTS C99 Time Related Changes


liblocal.02/src/ansi/locale/setlocal.pat to
liblocal.02/src/ansi/locale/setlocal.c

liblocal.02/src/ansi/time/strftime.pat to
liblocal.02/src/ansi/time/strftime.c
djgpp/src/libc/ansi/time/strftime.pat to
djgpp/src/libc/ansi/time/strftime.c

djgpp/src/libc/ansi/time/strftxh.pat to
djgpp/src/libc/ansi/time/strftime.txh

djgpp/tests/libc/ansi/time/makefile.pat to
djgpp/tests/libc/ansi/time/makefile

djgpp/tests/libc/ansi/time/strftime.pat to
djgpp/tests/libc/ansi/time/strftime.c

djgpp/tests/libc/ansi/time/xstrftm.pat to
djgpp/tests/libc/ansi/time/xstrftm.c

djgpp/tests/libc/ansi/time/strftiml.pat to
djgpp/tests/libc/ansi/time/strftiml.c NEW

djgpp/tests/libc/ansi/time/strftimt.pat to
djgpp/tests/libc/ansi/time/strftimt.  NEW

CHANGES C99 Time Related Changes


liblocal.02/src/ansi/locale/setlocal.c

fix problem resetting standard locale and returning updated locale
name


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

--- setlocal.c	2002-07-17 02:16:36.000000000 -0600
+++ 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[])
--- strftime.c	2002-06-08 04:18:06.000000000 -0600
+++ 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: 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
--- strftime.c	8 Nov 2003 12:19:40 -0000	1.6
+++ strftime.c	17 Mar 2004 06:36:05 -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: strftime.txh
===================================================================
RCS file: /cvs/djgpp/djgpp/src/libc/ansi/time/strftime.txh,v
retrieving revision 1.6
diff -u -t -F^@[[:alpha:]] -r1.6 strftime.txh
--- strftime.txh	8 Nov 2003 12:19:40 -0000	1.6
+++ strftime.txh	17 Mar 2004 06:39:48 -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 @@
 
 @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 @@
 
 @subheading Portability
 
-@portability ansi, posix
+@portability ANSI, POSIX
 
 @subheading Example
 
Index: strftime.c
===================================================================
RCS file: /cvs/djgpp/djgpp/zoneinfo/src/strftime.c,v
retrieving revision 1.2
diff -p -u -t -r1.2 strftime.c
--- strftime.c	28 May 1998 16:56:10 -0000	1.2
+++ strftime.c	17 Mar 2004 06:43:26 -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: makefile
===================================================================
RCS file: /cvs/djgpp/djgpp/tests/libc/ansi/time/makefile,v
retrieving revision 1.3
diff -u -t -r1.3 makefile
--- makefile	8 Nov 2003 12:19:41 -0000	1.3
+++ makefile	17 Mar 2004 06:57:18 -0000
@@ -4,6 +4,8 @@
 SRC += strftime.c
 SRC += tzinfo.c
 SRC += xstrftm.c
+SRC += strftimt.c
+SRC += strftiml.c
 
 include $(TOP)/../makefile.inc
 
Index: 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
--- strftime.c	26 May 2002 16:12:46 -0000	1.3
+++ strftime.c	17 Mar 2004 06:48:41 -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: 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
--- xstrftm.c	8 Nov 2003 12:19:41 -0000	1.1
+++ xstrftm.c	17 Mar 2004 06:49:25 -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;
 }
--- strftiml.c	2004-01-01 00:00:00.000000000 -0700
+++ strftiml.c	2004-02-18 22:32:40.000000000 -0700
@@ -0,0 +1,38 @@
+/* strftiml.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() */
+
--- strftimt.c	2004-01-01 00:00:00.000000000 -0700
+++ strftimt.c	2004-02-24 02:30:52.000000000 -0700
@@ -0,0 +1,192 @@
+/* @(#)strftimt.c        1.1     24 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() */
+

-- 
Thanks. Take care, Brian Inglis

Business:  +1(403)547-8816 	Brian DOT Inglis AT SystematicSW DOT ab DOT ca
Residence: +1(403)239-6520 	BWInglis AT Shaw DOT ca
Cellular:  +1(403)708-7006 	Brian_Inglis AT CompuServe DOT com
Facsimile: +1(403)547-8816 	Brian_Inglis AT CSi DOT com

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019