Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / sh / strftime.c
index aa3fc85..0ababc1 100644 (file)
@@ -38,6 +38,7 @@
  * Updated September, 2000
  * Updated December, 2001
  * Updated January, 2011
+ * Updated April, 2012
  *
  * Fixes from ado@elsie.nci.nih.gov,
  * February 1991, May 1992
@@ -75,6 +76,7 @@
 #define VMS_EXT                1       /* include %v for VMS date format */
 #define HPUX_EXT       1       /* non-conflicting stuff in HP-UX date */
 #define POSIX_SEMANTICS        1       /* call tzset() if TZ changes */
+#define POSIX_2008     1       /* flag and fw for C, F, G, Y formats */
 
 #undef strchr  /* avoid AIX weirdness */
 
@@ -96,27 +98,25 @@ static int iso8601wknum(const struct tm *timeptr);
 
 #define range(low, item, hi)   max(low, min(item, hi))
 
-#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
+/* Whew! This stuff is a mess. */
+#if !defined(OS2) && !defined(MSDOS) && !defined(__CYGWIN__) && defined(HAVE_TZNAME)
 extern char *tzname[2];
 extern int daylight;
 #if defined(SOLARIS) || defined(mips) || defined (M_UNIX)
 extern long int timezone, altzone;
 #else
-#  if defined (HPUX)
+#  if defined (HPUX) || defined(__hpux)
 extern long int timezone;
 #  else
+#    if !defined(__CYGWIN__)
 extern int timezone, altzone;
-#  endif /* !HPUX */
-#endif /* !SOLARIS && !mips && !M_UNIX */
+#    endif
+#  endif
+#endif
 #endif
 
 #undef min     /* just in case */
 
-/* format for %+ -- currently unused */
-#ifndef NATIONAL_FORMAT
-#define NATIONAL_FORMAT "%a %b %e %H:%M:%S %Z %Y"
-#endif
-
 /* min --- return minimum of two numbers */
 
 static inline int
@@ -135,6 +135,34 @@ max(int a, int b)
        return (a > b ? a : b);
 }
 
+#ifdef POSIX_2008
+/* iso_8601_2000_year --- format a year per ISO 8601:2000 as in 1003.1 */
+
+static void
+iso_8601_2000_year(char *buf, int year, size_t fw)
+{
+       int extra;
+       char sign = '\0';
+
+       if (year >= -9999 && year <= 9999) {
+               sprintf(buf, "%0*d", (int) fw, year);
+               return;
+       }
+
+       /* now things get weird */
+       if (year > 9999) {
+               sign = '+';
+       } else {
+               sign = '-';
+               year = -year;
+       }
+
+       extra = year / 10000;
+       year %= 10000;
+       sprintf(buf, "%c_%04d_%d", sign, extra, year);
+}
+#endif /* POSIX_2008 */
+
 /* strftime --- produce formatted time */
 
 size_t
@@ -155,12 +183,19 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
 #ifndef HAVE_TM_ZONE
 #ifndef HAVE_TM_NAME
 #ifndef HAVE_TZNAME
+#ifndef __CYGWIN__
        extern char *timezone();
        struct timeval tv;
        struct timezone zone;
+#endif /* __CYGWIN__ */
 #endif /* HAVE_TZNAME */
 #endif /* HAVE_TM_NAME */
 #endif /* HAVE_TM_ZONE */
+#ifdef POSIX_2008
+       int pad;
+       size_t fw;
+       char flag;
+#endif /* POSIX_2008 */
 
        /* various tables, useful in North America */
        static const char *days_a[] = {
@@ -234,6 +269,40 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
                        *s++ = *format;
                        continue;
                }
+#ifdef POSIX_2008
+               pad = '\0';
+               fw = 0;
+               flag = '\0';
+               switch (*++format) {
+               case '+':
+                       flag = '+';
+                       /* fall through */
+               case '0':
+                       pad = '0';
+                       format++;
+                       break;
+
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       break;
+
+               default:
+                       format--;
+                       goto again;
+               }
+               for (; isdigit(*format); format++) {
+                       fw = fw * 10 + (*format - '0');
+               }
+               format--;
+#endif /* POSIX_2008 */
+
        again:
                switch (*++format) {
                case '\0':
@@ -285,8 +354,19 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
                        break;
 
                case 'C':
+#ifdef POSIX_2008
+                       if (pad != '\0' && fw > 0) {
+                               size_t min_fw = (flag ? 3 : 2);
+
+                               fw = max(fw, min_fw);
+                               sprintf(tbuf, flag
+                                               ? "%+0*ld"
+                                               : "%0*ld", (int) fw,
+                                               (timeptr->tm_year + 1900L) / 100);
+                       } else
+#endif /* POSIX_2008 */
                century:
-                       sprintf(tbuf, "%02ld", (timeptr->tm_year + 1900L) / 100);
+                               sprintf(tbuf, "%02ld", (timeptr->tm_year + 1900L) / 100);
                        break;
 
                case 'd':       /* day of the month, 01 - 31 */
@@ -307,7 +387,30 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
                        goto again;
 
                case 'F':       /* ISO 8601 date representation */
+               {
+#ifdef POSIX_2008
+                       /*
+                        * Field width for %F is for the whole thing.
+                        * It must be at least 10.
+                        */
+                       char m_d[10];
+                       strftime(m_d, sizeof m_d, "-%m-%d", timeptr);
+                       size_t min_fw = 10;
+
+                       if (pad != '\0' && fw > 0) {
+                               fw = max(fw, min_fw);
+                       } else {
+                               fw = min_fw;
+                       }
+
+                       fw -= 6;        /* -XX-XX at end are invariant */
+
+                       iso_8601_2000_year(tbuf, timeptr->tm_year + 1900, fw);
+                       strcat(tbuf, m_d);
+#else
                        strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr);
+#endif /* POSIX_2008 */
+               }
                        break;
 
                case 'g':
@@ -329,8 +432,20 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
                        else
                                y = 1900L + timeptr->tm_year;
 
-                       if (*format == 'G')
-                               sprintf(tbuf, "%ld", y);
+                       if (*format == 'G') {
+#ifdef POSIX_2008
+                               if (pad != '\0' && fw > 0) {
+                                       size_t min_fw = 4;
+
+                                       fw = max(fw, min_fw);
+                                       sprintf(tbuf, flag
+                                                       ? "%+0*ld"
+                                                       : "%0*ld", (int) fw,
+                                                       y);
+                               } else
+#endif /* POSIX_2008 */
+                                       sprintf(tbuf, "%ld", y);
+                       }
                        else
                                sprintf(tbuf, "%02ld", y % 100);
                        break;
@@ -455,7 +570,17 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
                        break;
 
                case 'Y':       /* year with century */
-               fullyear:
+#ifdef POSIX_2008
+                       if (pad != '\0' && fw > 0) {
+                               size_t min_fw = 4;
+
+                               fw = max(fw, min_fw);
+                               sprintf(tbuf, flag
+                                               ? "%+0*ld"
+                                               : "%0*ld", (int) fw,
+                                               1900L + timeptr->tm_year);
+                       } else
+#endif /* POSIX_2008 */
                        sprintf(tbuf, "%ld", 1900L + timeptr->tm_year);
                        break;
 
@@ -496,12 +621,12 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
                         * Systems with tzname[] probably have timezone as
                         * secs west of GMT.  Convert to mins east of GMT.
                         */
-#  ifdef HPUX
+#  if defined(__hpux) || defined (HPUX) || defined(__CYGWIN__)
                        off = -timezone / 60;
 #  else
                        /* ADR: 4 August 2001, fixed this per gazelle@interaccess.com */
                        off = -(daylight ? altzone : timezone) / 60;
-#  endif /* !HPUX */
+#  endif
 #else /* !HAVE_TZNAME */
                        gettimeofday(& tv, & zone);
                        off = -zone.tz_minuteswest;