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