summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/strftime.c902
1 files changed, 398 insertions, 504 deletions
diff --git a/lib/strftime.c b/lib/strftime.c
index 2b8f3600d..16a9bb2b4 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -1,559 +1,453 @@
-/* strftime - custom formatting of date and/or time
- Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-/* Note: this version of strftime lacks locale support,
- but it is standalone.
-
- Performs `%' substitutions similar to those in printf. Except
- where noted, substituted fields have a fixed size; numeric fields are
- padded if necessary. Padding is with zeros by default; for fields
- that display a single number, padding can be changed or inhibited by
- following the `%' with one of the modifiers described below. Unknown
- field specifiers are copied as normal characters. All other
- characters are copied to the output without change.
-
- Supports a superset of the ANSI C field specifiers.
-
- Literal character fields:
- % %
- n newline
- t tab
-
- Numeric modifiers (a nonstandard extension):
- - do not pad the field
- _ pad the field with spaces
-
- Time fields:
- %H hour (00..23)
- %I hour (01..12)
- %k hour ( 0..23)
- %l hour ( 1..12)
- %M minute (00..59)
- %p locale's AM or PM
- %r time, 12-hour (hh:mm:ss [AP]M)
- %R time, 24-hour (hh:mm)
- %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension)
- %S second (00..61)
- %T time, 24-hour (hh:mm:ss)
- %X locale's time representation (%H:%M:%S)
- %z RFC-822 style numeric timezone (-0500) (a nonstandard extension)
- %Z time zone (EDT), or nothing if no time zone is determinable
-
- Date fields:
- %a locale's abbreviated weekday name (Sun..Sat)
- %A locale's full weekday name, variable length (Sunday..Saturday)
- %b locale's abbreviated month name (Jan..Dec)
- %B locale's full month name, variable length (January..December)
- %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
- %C century (00..99)
- %d day of month (01..31)
- %e day of month ( 1..31)
- %D date (mm/dd/yy)
- %h same as %b
- %j day of year (001..366)
- %m month (01..12)
- %U week number of year with Sunday as first day of week (00..53)
- %V FIXME
- %w day of week (0..6)
- %W week number of year with Monday as first day of week (00..53)
- %x locale's date representation (mm/dd/yy)
- %y last two digits of year (00..99)
- %Y year (1970...)
-
- David MacKenzie <djm@gnu.ai.mit.edu> */
+/* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
+
+NOTE: The canonical source of this file is maintained with the GNU C Library.
+Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+USA. */
#ifdef HAVE_CONFIG_H
-#include <config.h>
+# include <config.h>
+#endif
+
+#ifdef _LIBC
+# define HAVE_LIMITS_H 1
+# define HAVE_MBLEN 1
+# define HAVE_TM_ZONE 1
+# define STDC_HEADERS 1
+# include <ansidecl.h>
+# include "../locale/localeinfo.h"
#endif
#include <stdio.h>
-#include <sys/types.h>
-#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
-#include <sys/time.h>
+#include <sys/types.h> /* Some systems define `time_t' here. */
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
#else
-#include <time.h>
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
#endif
-#ifndef STDC_HEADERS
-time_t mktime ();
+#if HAVE_MBLEN
+# include <ctype.h>
#endif
-#if defined(HAVE_TZNAME)
-extern char *tzname[2];
+#if HAVE_LIMITS_H
+# include <limits.h>
#endif
-/* Types of padding for numbers in date and time. */
-enum padding
-{
- none, blank, zero
-};
-
-static char const* const days[] =
-{
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
-};
-
-static char const * const months[] =
-{
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
-};
-
-/* Add character C to STRING and increment LENGTH,
- unless LENGTH would exceed MAX. */
-
-#define add_char(c) \
- do \
- { \
- if (length + 1 <= max) \
- string[length++] = (c); \
- } \
- while (0)
-
-/* Add a 2 digit number to STRING, padding if specified.
- Return the number of characters added, up to MAX. */
-
-static int
-add_num2 (string, num, max, pad)
- char *string;
- int num;
- int max;
- enum padding pad;
-{
- int top = num / 10;
- int length = 0;
-
- if (top == 0 && pad == blank)
- add_char (' ');
- else if (top != 0 || pad == zero)
- add_char (top + '0');
- add_char (num % 10 + '0');
- return length;
-}
+#if STDC_HEADERS
+# include <stddef.h>
+# include <stdlib.h>
+# include <string.h>
+#else
+# define memcpy(d, s, n) bcopy (s, d, n)
+#endif
-/* Add a 3 digit number to STRING, padding if specified.
- Return the number of characters added, up to MAX. */
+#ifndef __P
+#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+#define __P(args) args
+#else
+#define __P(args) ()
+#endif /* GCC. */
+#endif /* Not __P. */
-static int
-add_num3 (string, num, max, pad)
- char *string;
- int num;
- int max;
- enum padding pad;
-{
- int top = num / 100;
- int mid = (num - top * 100) / 10;
- int length = 0;
-
- if (top == 0 && pad == blank)
- add_char (' ');
- else if (top != 0 || pad == zero)
- add_char (top + '0');
- if (mid == 0 && top == 0 && pad == blank)
- add_char (' ');
- else if (mid != 0 || top != 0 || pad == zero)
- add_char (mid + '0');
- add_char (num % 10 + '0');
- return length;
-}
+#ifndef PTR
+#ifdef __STDC__
+#define PTR void *
+#else
+#define PTR char *
+#endif
+#endif
-/* Like strncpy except return the number of characters copied. */
+static unsigned int week __P((const struct tm *const, int, int));
+
+
+#define add(n, f) \
+ do \
+ { \
+ i += (n); \
+ if (i >= maxsize) \
+ return 0; \
+ else \
+ if (p) \
+ { \
+ f; \
+ p += (n); \
+ } \
+ } while (0)
+#define cpy(n, s) add((n), memcpy((PTR) p, (PTR) (s), (n)))
+
+#ifdef _LIBC
+#define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
+#else
+#define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
+#endif
-static int
-add_str (to, from, max)
- char *to;
- const char *from;
- int max;
-{
- int i;
- for (i = 0; from[i] && i <= max; ++i)
- to[i] = from[i];
- return i;
-}
-static int
-add_num_time_t (string, max, num)
- char *string;
- int max;
- time_t num;
+/* Return the week in the year specified by TP,
+ with weeks starting on STARTING_DAY. */
+#ifdef __GNUC__
+inline
+#endif
+static unsigned int
+week (tp, starting_day, max_preceding)
+ const struct tm *const tp;
+ int starting_day;
+ int max_preceding;
{
- /* This buffer is large enough to hold the character representation
- (including the trailing NUL) of any unsigned decimal quantity
- whose binary representation fits in 128 bits. */
- char buf[40];
- int length;
-
- if (sizeof (num) > 16)
- abort ();
- sprintf (buf, "%lu", (unsigned long) num);
- length = add_str (string, buf, max);
- return length;
+ int wday, dl, base;
+
+ wday = tp->tm_wday - starting_day;
+ if (wday < 0)
+ wday += 7;
+
+ /* Set DL to the day in the year of the first day of the week
+ containing the day specified in TP. */
+ dl = tp->tm_yday - wday;
+
+ /* For the computation following ISO 8601:1988 we set the number of
+ the week containing January 1st to 1 if this week has more than
+ MAX_PRECEDING days in the new year. For ISO 8601 this number is
+ 3, for the other representation it is 7 (i.e., not to be
+ fulfilled). */
+ base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
+
+ /* If DL is negative we compute the result as 0 unless we have to
+ compute it according ISO 8601. In this case we have to return 53
+ or 1 if the week containing January 1st has less than 4 days in
+ the new year or not. If DL is not negative we calculate the
+ number of complete weeks for our week (DL / 7) plus 1 (because
+ only for DL < 0 we are in week 0/53 and plus the number of the
+ first week computed in the last step. */
+ return dl < 0 ? (dl < -max_preceding ? 53 : base)
+ : base + 1 + dl / 7;
}
-/* Convert MINUTES_EAST into a string suitable for use as the RFC-822
- timezone indicator. Write no more than MAX bytes into STRING.
- Return the number of bytes written into STRING. */
+#ifndef _NL_CURRENT
+static char const weekday_name[][10] =
+ {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ };
+static char const month_name[][10] =
+ {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ };
+#endif
-static int
-add_num_tz (string, max, minutes_east)
- char *string;
- int max;
- int minutes_east;
+/* Write information from TP into S according to the format
+ string FORMAT, writing no more that MAXSIZE characters
+ (including the terminating '\0') and returning number of
+ characters written. If S is NULL, nothing will be written
+ anywhere, so to determine how many characters would be
+ written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
+size_t
+strftime (s, maxsize, format, tp)
+ char *s;
+ size_t maxsize;
+ const char *format;
+ register const struct tm *tp;
{
- int length;
+ int hour12 = tp->tm_hour;
+#ifdef _NL_CURRENT
+ const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
+ const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
+ const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
+ const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
+ const char *const ampm = _NL_CURRENT (LC_TIME,
+ hour12 > 11 ? PM_STR : AM_STR);
+ size_t aw_len = strlen(a_wkday);
+ size_t am_len = strlen(a_month);
+ size_t ap_len = strlen (ampm);
+#else
+ const char *const f_wkday = weekday_name[tp->tm_wday];
+ const char *const f_month = month_name[tp->tm_mon];
+ const char *const a_wkday = f_wkday;
+ const char *const a_month = f_month;
+ const char *const ampm = "AMPM" + 2 * (hour12 > 11);
+ size_t aw_len = 3;
+ size_t am_len = 3;
+ size_t ap_len = 2;
+#endif
+ size_t wkday_len = strlen(f_wkday);
+ size_t month_len = strlen(f_month);
+ const unsigned int y_week0 = week (tp, 0, 7);
+ const unsigned int y_week1 = week (tp, 1, 7);
+ const unsigned int y_week2 = week (tp, 1, 3);
+ const char *zone;
+ size_t zonelen;
+ register size_t i = 0;
+ register char *p = s;
+ register const char *f;
+ char number_fmt[5];
+
+ /* Initialize the buffer we will use for the sprintf format for numbers. */
+ number_fmt[0] = '%';
+
+ zone = 0;
+#if HAVE_TM_ZONE
+ zone = (const char *) tp->tm_zone;
+#endif
+#if HAVE_TZNAME
+ if (!(zone && *zone) && tp->tm_isdst >= 0)
+ zone = tzname[tp->tm_isdst];
+#endif
+ if (!(zone && *zone))
+ zone = "???";
- if (max < 1)
- return 0;
+ zonelen = strlen (zone);
- if (minutes_east < 0)
- {
- *string = '-';
- minutes_east = -minutes_east;
- }
+ if (hour12 > 12)
+ hour12 -= 12;
else
- *string = '+';
+ if (hour12 == 0) hour12 = 12;
- length = 1 + add_num2 (&string[1], (minutes_east / 60) % 24, max - 1, zero);
- length += add_num2 (&string[length], minutes_east % 60, max - length, zero);
+ for (f = format; *f != '\0'; ++f)
+ {
+ enum { pad_zero, pad_space, pad_none } pad; /* Padding for number. */
+ unsigned int maxdigits; /* Max digits for numeric format. */
+ unsigned int number_value; /* Numeric value to be printed. */
+ const char *subfmt;
- return length;
-}
+#if HAVE_MBLEN
+ if (!isascii(*f))
+ {
+ /* Non-ASCII, may be a multibyte. */
+ int len = mblen(f, strlen(f));
+ if (len > 0)
+ {
+ cpy(len, f);
+ continue;
+ }
+ }
+#endif
-/* Implement %U. Return the week in the year of the time in TM,
- with the weeks starting on Sundays. */
+ if (*f != '%')
+ {
+ add(1, *p = *f);
+ continue;
+ }
-static int
-sun_week (tm)
- const struct tm *tm;
-{
- int dl;
+ /* Check for flags that can modify a number format. */
+ ++f;
+ switch (*f)
+ {
+ case '_':
+ pad = pad_space;
+ ++f;
+ break;
+ case '-':
+ pad = pad_none;
+ ++f;
+ break;
+ default:
+ pad = pad_zero;
+ break;
+ }
- /* %U Week of the year (Sunday as the first day of the week) as a decimal
- number [00-53]. All days in a new year preceding the first Sunday are
- considered to be in week 0. */
+ /* Now do the specified format. */
+ switch (*f)
+ {
+ case '\0':
+ case '%':
+ add(1, *p = *f);
+ break;
+
+ case 'a':
+ cpy(aw_len, a_wkday);
+ break;
+
+ case 'A':
+ cpy(wkday_len, f_wkday);
+ break;
+
+ case 'b':
+ case 'h': /* GNU extension. */
+ cpy(am_len, a_month);
+ break;
+
+ case 'B':
+ cpy(month_len, f_month);
+ break;
+
+ case 'c':
+#ifdef _NL_CURRENT
+ subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
+#else
+ subfmt = "%a %b %d %H:%M:%S %Z %Y";
+#endif
+ subformat:
+ {
+ size_t len = strftime (p, maxsize - i, subfmt, tp);
+ if (len == 0 && *subfmt)
+ return 0;
+ add(len, );
+ }
+ break;
+
+#define DO_NUMBER(digits, value) \
+ maxdigits = digits; number_value = value; goto do_number
+#define DO_NUMBER_NOPAD(digits, value) \
+ maxdigits = digits; number_value = value; goto do_number_nopad
+
+ case 'C':
+ DO_NUMBER (2, (1900 + tp->tm_year) / 100);
+
+ case 'x':
+#ifdef _NL_CURRENT
+ subfmt = _NL_CURRENT (LC_TIME, D_FMT);
+ goto subformat;
+#endif
+ /* Fall through. */
+ case 'D': /* GNU extension. */
+ subfmt = "%m/%d/%y";
+ goto subformat;
- dl = tm->tm_yday - tm->tm_wday;
- return dl < 0 ? 0 : dl / 7 + 1;
-}
+ case 'd':
+ DO_NUMBER (2, tp->tm_mday);
-/* Implement %V. Similar to mon_week (%W), but there is no 0'th week --
- they're numbered [01-53]. And if the week containing January 1 has
- four or more days in the new year, then it is considered week 1;
- otherwise, it is week 53 of the previous year, and the next week is
- week 1. (See the ISO 8601: 1988 standard.) */
+ case 'e': /* GNU extension: %d, but blank-padded. */
+ DO_NUMBER_NOPAD (2, tp->tm_mday);
-static int
-mon_week_ISO (tm)
- const struct tm *tm;
-{
- int dl, n_days_before_first_monday;
- int week_num;
+ /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
+ jump to one of these two labels. */
- n_days_before_first_monday = (tm->tm_yday + 7 - tm->tm_wday + 1) % 7;
- dl = tm->tm_yday - n_days_before_first_monday;
- week_num = dl < 0 ? 0 : dl / 7 + 1;
- if (n_days_before_first_monday >= 4)
- {
- week_num = (week_num + 1) % 54;
- if (week_num == 0)
- week_num = 1;
- }
- if (week_num == 0)
- week_num = 53;
+ do_number_nopad:
+ /* Force `-' flag. */
+ pad = pad_none;
- return week_num;
-}
+ do_number:
+ {
+ /* Format the number according to the PAD flag. */
-/* Implement %W. Return the week in the year of the time in TM,
- with the weeks starting on Mondays. */
+ register char *nf = &number_fmt[1];
+ int printed;
-static int
-mon_week (tm)
- const struct tm *tm;
-{
- int dl, n_days_before_first_monday;
+ switch (pad)
+ {
+ case pad_zero:
+ *nf++ = '0';
+ case pad_space:
+ *nf++ = '0' + maxdigits;
+ case pad_none:
+ *nf++ = 'u';
+ *nf = '\0';
+ }
- n_days_before_first_monday = (tm->tm_yday + 7 - tm->tm_wday + 1) % 7;
- dl = tm->tm_yday - n_days_before_first_monday;
- return dl < 0 ? 0 : dl / 7 + 1;
-}
+#ifdef _LIBC
+ add (maxdigits, printed = sprintf (p, number_fmt, number_value));
+#else
+ add (maxdigits, sprintf (p, number_fmt, number_value);
+ printed = strlen (p));
+#endif
-#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
-char *
-zone_name (tp)
- struct tm *tp;
-{
- char *timezone ();
- struct timeval tv;
- struct timezone tz;
+ break;
+ }
- gettimeofday (&tv, &tz);
- return timezone (tz.tz_minuteswest, tp->tm_isdst);
-}
-#endif
-/* Format the time given in TM according to FORMAT, and put the
- results in STRING.
- Return the number of characters (not including terminating null)
- that were put into STRING, or 0 if the length would have
- exceeded MAX. */
+ case 'H':
+ DO_NUMBER (2, tp->tm_hour);
-size_t
-strftime (string, max, format, tm)
- char *string;
- size_t max;
- const char *format;
- const struct tm *tm;
-{
- enum padding pad; /* Type of padding to apply. */
- size_t length = 0; /* Characters put in STRING so far. */
+ case 'I':
+ DO_NUMBER (2, hour12);
- for (; *format && length < max; ++format)
- {
- if (*format != '%')
- add_char (*format);
- else
- {
- ++format;
- /* Modifiers: */
- if (*format == '-')
- {
- pad = none;
- ++format;
- }
- else if (*format == '_')
- {
- pad = blank;
- ++format;
- }
- else
- pad = zero;
+ case 'k': /* GNU extension. */
+ DO_NUMBER_NOPAD (2, tp->tm_hour);
- switch (*format)
- {
- /* Literal character fields: */
- case 0:
- case '%':
- add_char ('%');
- break;
- case 'n':
- add_char ('\n');
- break;
- case 't':
- add_char ('\t');
- break;
- default:
- add_char (*format);
- break;
-
- /* Time fields: */
- case 'H':
- case 'k':
- length +=
- add_num2 (&string[length], tm->tm_hour, max - length,
- *format == 'H' ? pad : blank);
- break;
- case 'I':
- case 'l':
- {
- int hour12;
-
- if (tm->tm_hour == 0)
- hour12 = 12;
- else if (tm->tm_hour > 12)
- hour12 = tm->tm_hour - 12;
- else
- hour12 = tm->tm_hour;
- length +=
- add_num2 (&string[length], hour12, max - length,
- *format == 'I' ? pad : blank);
- }
- break;
- case 'M':
- length +=
- add_num2 (&string[length], tm->tm_min, max - length, pad);
- break;
- case 'p':
- if (tm->tm_hour < 12)
- add_char ('A');
- else
- add_char ('P');
- add_char ('M');
- break;
- case 'r':
- length +=
- strftime (&string[length], max - length, "%I:%M:%S %p", tm);
- break;
- case 'R':
- length +=
- strftime (&string[length], max - length, "%H:%M", tm);
- break;
-
- case 's':
- {
- struct tm writable_tm;
- writable_tm = *tm;
- length += add_num_time_t (&string[length], max - length,
- mktime (&writable_tm));
- }
- break;
-
- case 'S':
- length +=
- add_num2 (&string[length], tm->tm_sec, max - length, pad);
- break;
- case 'T':
- length +=
- strftime (&string[length], max - length, "%H:%M:%S", tm);
- break;
- case 'X':
- length +=
- strftime (&string[length], max - length, "%H:%M:%S", tm);
- break;
- case 'z':
- {
- time_t t;
- struct tm tml, tmg;
- int diff;
+ case 'l': /* GNU extension. */
+ DO_NUMBER_NOPAD (2, hour12);
- tml = *tm;
- t = mktime (&tml);
- tml = *localtime (&t); /* Canonicalize the local time */
- tmg = *gmtime (&t);
+ case 'j':
+ DO_NUMBER (3, 1 + tp->tm_yday);
- /* Compute the difference */
+ case 'M':
+ DO_NUMBER (2, tp->tm_min);
- diff = tml.tm_min - tmg.tm_min;
- diff += 60 * (tml.tm_hour - tmg.tm_hour);
+ case 'm':
+ DO_NUMBER (2, tp->tm_mon + 1);
- if (tml.tm_mon != tmg.tm_mon)
- {
- /* We assume no timezone differs from UTC by more than
- +- 23 hours. This should be safe. */
- if (tmg.tm_mday == 1)
- tml.tm_mday = 0;
- else /* tml.tm_mday == 1 */
- tmg.tm_mday = 0;
- }
+ case 'n': /* GNU extension. */
+ add (1, *p = '\n');
+ break;
- diff += 1440 * (tml.tm_mday - tmg.tm_mday);
+ case 'p':
+ cpy(ap_len, ampm);
+ break;
- length += add_num_tz (&string[length], max - length, diff);
- }
- break;
- case 'Z':
-#ifdef HAVE_TM_ZONE
- length += add_str (&string[length], tm->tm_zone, max - length);
-#else
-#ifdef HAVE_TZNAME
- if (tm->tm_isdst && tzname[1] && *tzname[1])
- length += add_str (&string[length], tzname[1], max - length);
- else
- length += add_str (&string[length], tzname[0], max - length);
-#else
- length += add_str (&string[length], zone_name (tm), max - length);
-#endif
+ case 'R': /* GNU extension. */
+ subfmt = "%H:%M";
+ goto subformat;
+
+ case 'r': /* GNU extension. */
+ subfmt = "%I:%M:%S %p";
+ goto subformat;
+
+ case 'S':
+ DO_NUMBER (2, tp->tm_sec);
+
+ case 'X':
+#ifdef _NL_CURRENT
+ subfmt = _NL_CURRENT (LC_TIME, T_FMT);
+ goto subformat;
#endif
- break;
-
- /* Date fields: */
- case 'a':
- add_char (days[tm->tm_wday][0]);
- add_char (days[tm->tm_wday][1]);
- add_char (days[tm->tm_wday][2]);
- break;
- case 'A':
- length +=
- add_str (&string[length], days[tm->tm_wday], max - length);
- break;
- case 'b':
- case 'h':
- add_char (months[tm->tm_mon][0]);
- add_char (months[tm->tm_mon][1]);
- add_char (months[tm->tm_mon][2]);
- break;
- case 'B':
- length +=
- add_str (&string[length], months[tm->tm_mon], max - length);
- break;
- case 'c':
- length +=
- strftime (&string[length], max - length,
- "%a %b %d %H:%M:%S %Z %Y", tm);
- break;
- case 'C':
- length +=
- add_num2 (&string[length], (tm->tm_year + 1900) / 100,
- max - length, pad);
- break;
- case 'd':
- length +=
- add_num2 (&string[length], tm->tm_mday, max - length, pad);
- break;
- case 'e':
- length +=
- add_num2 (&string[length], tm->tm_mday, max - length, blank);
- break;
- case 'D':
- length +=
- strftime (&string[length], max - length, "%m/%d/%y", tm);
- break;
- case 'j':
- length +=
- add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
- break;
- case 'm':
- length +=
- add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
- break;
- case 'U':
- length +=
- add_num2 (&string[length], sun_week (tm), max - length, pad);
- break;
- case 'V':
- length += add_num2 (&string[length], mon_week_ISO (tm),
- max - length, pad);
- break;
- case 'w':
- add_char (tm->tm_wday + '0');
- break;
- case 'W':
- length +=
- add_num2 (&string[length], mon_week (tm), max - length, pad);
- break;
- case 'x':
- length +=
- strftime (&string[length], max - length, "%m/%d/%y", tm);
- break;
- case 'y':
- length +=
- add_num2 (&string[length], tm->tm_year % 100,
- max - length, pad);
- break;
- case 'Y':
- add_char ((tm->tm_year + 1900) / 1000 + '0');
- length +=
- add_num3 (&string[length],
- (1900 + tm->tm_year) % 1000, max - length, zero);
- break;
- }
+ /* Fall through. */
+ case 'T': /* GNU extenstion. */
+ subfmt = "%H:%M:%S";
+ goto subformat;
+
+ case 't': /* GNU extenstion. */
+ add (1, *p = '\t');
+ break;
+
+ case 'U':
+ DO_NUMBER (2, y_week0);
+
+ case 'V':
+ DO_NUMBER (2, y_week2);
+
+ case 'W':
+ DO_NUMBER (2, y_week1);
+
+ case 'w':
+ DO_NUMBER (2, tp->tm_wday);
+
+ case 'Y':
+ DO_NUMBER (4, 1900 + tp->tm_year);
+
+ case 'y':
+ DO_NUMBER (2, tp->tm_year % 100);
+
+ case 'Z':
+ cpy(zonelen, zone);
+ break;
+
+ default:
+ /* Bad format. */
+ break;
}
}
- add_char (0);
- return length - 1;
+
+ if (p)
+ *p = '\0';
+ return i;
}