changeset 1185:669750cfad84

[project @ 1995-03-30 04:56:50 by jwe] Initial revision
author jwe
date Thu, 30 Mar 1995 04:56:50 +0000
parents 1def0d0014bd
children d94bcafabac2
files src/strftime.c
diffstat 1 files changed, 469 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/strftime.c	Thu Mar 30 04:56:50 1995 +0000
@@ -0,0 +1,469 @@
+/* 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	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)
+   %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> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.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>
+#else
+#include <time.h>
+#endif
+
+#ifndef STDC_HEADERS
+time_t mktime ();
+#endif
+
+#if defined(HAVE_TZNAME)
+extern char *tzname[2];
+#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;
+}
+
+/* Add a 3 digit number to STRING, padding if specified.
+   Return the number of characters added, up to MAX. */
+
+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;
+}
+
+/* Like strncpy except return the number of characters copied. */
+
+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;
+{
+  /* 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;
+}
+
+/* Return the week in the year of the time in TM, with the weeks
+   starting on Sundays. */
+
+static int
+sun_week (tm)
+     struct tm *tm;
+{
+  int dl;
+
+  /* Set `dl' to the day in the year of the last day of the week previous
+     to the one containing the day specified in TM.  If the day specified
+     in TM is in the first week of the year, `dl' will be negative or 0.
+     Otherwise, calculate the number of complete weeks before our week
+     (dl / 7) and add any partial week at the start of the year (dl % 7). */
+  dl = tm->tm_yday - tm->tm_wday;
+  return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
+}
+
+/* Return the week in the year of the time in TM, with the weeks
+   starting on Mondays. */
+
+static int
+mon_week (tm)
+     struct tm *tm;
+{
+  int dl, wday;
+
+  if (tm->tm_wday == 0)
+    wday = 6;
+  else
+    wday = tm->tm_wday - 1;
+  dl = tm->tm_yday - wday;
+  return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
+}
+
+#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
+char *
+zone_name (tp)
+     struct tm *tp;
+{
+  char *timezone ();
+  struct timeval tv;
+  struct timezone tz;
+
+  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. */
+
+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. */
+
+  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;
+
+	  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':
+#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
+#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 '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;
+	    }
+	}
+    }
+  add_char (0);
+  return length - 1;
+}