Home | History | Annotate | Line # | Download | only in time
      1 /*	$NetBSD: asctime.c,v 1.32 2025/01/23 22:44:22 christos Exp $	*/
      2 
      3 /* asctime a la ISO C.  */
      4 
      5 /*
      6 ** This file is in the public domain, so clarified as of
      7 ** 1996-06-05 by Arthur David Olson.
      8 */
      9 
     10 /*
     11 ** Avoid the temptation to punt entirely to strftime;
     12 ** strftime can behave badly when tm components are out of range, and
     13 ** the output of strftime is supposed to be locale specific
     14 ** whereas the output of asctime is supposed to be constant.
     15 */
     16 
     17 #include <sys/cdefs.h>
     18 #if defined(LIBC_SCCS) && !defined(lint)
     19 #if 0
     20 static char	elsieid[] = "@(#)asctime.c	8.5";
     21 #else
     22 __RCSID("$NetBSD: asctime.c,v 1.32 2025/01/23 22:44:22 christos Exp $");
     23 #endif
     24 #endif /* LIBC_SCCS and not lint */
     25 
     26 /*LINTLIBRARY*/
     27 
     28 #include "namespace.h"
     29 #include "private.h"
     30 #include <stdio.h>
     31 
     32 /* Publish asctime_r and ctime_r only when supporting older POSIX.  */
     33 #if SUPPORT_POSIX2008 || defined(__NetBSD__)
     34 # define asctime_static
     35 #else
     36 # define asctime_static static
     37 # undef asctime_r
     38 # undef ctime_r
     39 # define asctime_r static_asctime_r
     40 # define ctime_r static_ctime_r
     41 #endif
     42 
     43 #ifndef __LIBC12_SOURCE__
     44 
     45 #ifdef __weak_alias
     46 __weak_alias(asctime_r,_asctime_r)
     47 #endif
     48 
     49 enum { STD_ASCTIME_BUF_SIZE = 26 };
     50 
     51 
     52 /*
     53 ** Big enough for something such as
     54 ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648     -2147483648\n
     55 ** (two three-character abbreviations, five strings denoting integers,
     56 ** seven explicit spaces, two explicit colons, a newline,
     57 ** and a trailing NUL byte).
     58 ** The values above are for systems where an int is 32 bits and are provided
     59 ** as an example; the size expression below is a bound for the system at
     60 ** hand.
     61 */
     62 static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
     63 
     64 /* On pre-C99 platforms, a snprintf substitute good enough for us.  */
     65 #if !HAVE_SNPRINTF
     66 # include <stdarg.h>
     67 ATTRIBUTE_FORMAT((printf, 3, 4)) static int
     68 my_snprintf(char *s, size_t size, char const *format, ...)
     69 {
     70   int n;
     71   va_list args;
     72   char stackbuf[sizeof buf_asctime];
     73   va_start(args, format);
     74   n = vsprintf(stackbuf, format, args);
     75   va_end (args);
     76   if (0 <= n && n < size)
     77     memcpy (s, stackbuf, n + 1);
     78   return n;
     79 }
     80 # undef snprintf
     81 # define snprintf my_snprintf
     82 #endif
     83 
     84 asctime_static
     85 char *
     86 asctime_r(struct tm const *restrict timeptr, char *restrict buf)
     87 {
     88 	static const char	wday_name[][4] = {
     89 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     90 	};
     91 	static const char	mon_name[][4] = {
     92 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
     93 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     94 	};
     95 	const char *	wn;
     96 	const char *	mn;
     97 	int year, mday, hour, min, sec;
     98 	long long_TM_YEAR_BASE = TM_YEAR_BASE;
     99 	size_t bufsize = (buf == buf_asctime
    100 			  ? sizeof buf_asctime : STD_ASCTIME_BUF_SIZE);
    101 
    102 	if (timeptr == NULL) {
    103 		strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
    104 		/* Set errno now, since strcpy might change it in
    105 		   POSIX.1-2017 and earlier.  */
    106 		errno = EINVAL;
    107 		return buf;
    108 	}
    109 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
    110 		wn = "???";
    111 	else	wn = wday_name[timeptr->tm_wday];
    112 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
    113 		mn = "???";
    114 	else	mn = mon_name[timeptr->tm_mon];
    115 
    116 	year = timeptr->tm_year;
    117 	mday = timeptr->tm_mday;
    118 	hour = timeptr->tm_hour;
    119 	min = timeptr->tm_min;
    120 	sec = timeptr->tm_sec;
    121 
    122 	/* Vintage programs are coded for years that are always four bytes long
    123 	   and may assume that the newline always lands in the same place.
    124 	   For years that are less than four bytes, pad the output with
    125 	   leading zeroes to get the newline in the traditional place.
    126 	   For years longer than four bytes, put extra spaces before the year
    127 	   so that vintage code trying to overwrite the newline
    128 	   won't overwrite a digit within a year and truncate the year,
    129 	   using the principle that no output is better than wrong output.
    130 	   This conforms to ISO C and POSIX standards, which say behavior
    131 	   is undefined when the year is less than 1000 or greater than 9999.
    132 
    133 	   Also, avoid overflow when formatting tm_year + TM_YEAR_BASE.  */
    134 
    135 	if ((size_t)(year <= INT_MAX - TM_YEAR_BASE
    136 	     ? snprintf (buf, bufsize,
    137 			 ((-999 - TM_YEAR_BASE <= year
    138 			   && year <= 9999 - TM_YEAR_BASE)
    139 			  ? "%s %s%3d %.2d:%.2d:%.2d %04ld\n"
    140 			  : "%s %s%3d %.2d:%.2d:%.2d     %ld\n"),
    141 			 wn, mn, mday, hour, min, sec,
    142 			 year + long_TM_YEAR_BASE)
    143 	     : snprintf (buf, bufsize,
    144 			 "%s %s%3d %.2d:%.2d:%.2d     %d%d\n",
    145 			 wn, mn, mday, hour, min, sec,
    146 			 year / 10 + TM_YEAR_BASE / 10,
    147 			 year % 10))
    148 	    < bufsize)
    149 		return buf;
    150 	else {
    151 		errno = EOVERFLOW;
    152 		return NULL;
    153 	}
    154 }
    155 
    156 char *
    157 asctime(const struct tm *timeptr)
    158 {
    159 	return asctime_r(timeptr, buf_asctime);
    160 }
    161 #endif /* !__LIBC12_SOURCE__ */
    162 
    163 
    164 char *
    165 ctime_rz(timezone_t sp, const time_t *timep, char *buf)
    166 {
    167   struct tm mytm;
    168   struct tm *tmp = localtime_rz(sp, timep, &mytm);
    169   return tmp ? asctime_r(tmp, buf) : NULL;
    170 }
    171 
    172 asctime_static
    173 char *
    174 ctime_r(const time_t *timep, char *buf)
    175 {
    176   struct tm mytm;
    177   struct tm *tmp = localtime_r(timep, &mytm);
    178   return tmp ? asctime_r(tmp, buf) : NULL;
    179 }
    180 
    181 char *
    182 ctime(const time_t *timep)
    183 {
    184   /* Do not call localtime_r, as C23 requires ctime to initialize the
    185      static storage that localtime updates.  */
    186   struct tm *tmp = localtime(timep);
    187   return tmp ? asctime(tmp) : NULL;
    188 }
    189