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