]> git.ipfire.org Git - thirdparty/glibc.git/blame - time/strftime.c
Sun Mar 5 19:40:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
[thirdparty/glibc.git] / time / strftime.c
CommitLineData
28f540f4
RM
1/* Extensions for GNU date that are still missing here:
2 -
3 _
4*/
5
933e73fa 6/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
28f540f4
RM
7This file is part of the GNU C Library.
8
9The GNU C Library is free software; you can redistribute it and/or
10modify it under the terms of the GNU Library General Public License as
11published by the Free Software Foundation; either version 2 of the
12License, or (at your option) any later version.
13
14The GNU C Library is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17Library General Public License for more details.
18
19You should have received a copy of the GNU Library General Public
20License along with the GNU C Library; see the file COPYING.LIB. If
21not, write to the Free Software Foundation, Inc., 675 Mass Ave,
22Cambridge, MA 02139, USA. */
23
24#include <ansidecl.h>
933e73fa 25#include "../locale/localeinfo.h"
28f540f4
RM
26#include <ctype.h>
27#include <limits.h>
28#include <stddef.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33
34#ifndef HAVE_GNU_LD
35#define __tzname tzname
36#define __daylight daylight
37#define __timezone timezone
38#endif
39
40
41#define add(n, f) \
42 do \
43 { \
44 i += (n); \
45 if (i >= maxsize) \
46 return 0; \
47 else \
48 if (p != NULL) \
49 { \
50 f; \
51 p += (n); \
52 } \
53 } while (0)
54#define cpy(n, s) add((n), memcpy((PTR) p, (PTR) (s), (n)))
55#define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
56
57/* Return the week in the year specified by TP,
58 with weeks starting on STARTING_DAY. */
59#ifdef __GNUC__
60inline
61#endif
62static unsigned int
63DEFUN(week, (tp, starting_day),
64 CONST struct tm *CONST tp AND int starting_day)
65{
66 int wday, dl;
67
68 wday = tp->tm_wday - starting_day;
69 if (wday < 0)
70 wday += 7;
71
72 /* Set DL to the day in the year of the last day of the week previous to the
73 one containing the day specified in TP. If DL is negative or zero, the
74 day specified in TP is in the first week of the year. Otherwise,
75 calculate the number of complete weeks before our week (DL / 7) and
76 add any partial week at the start of the year (DL % 7). */
77 dl = tp->tm_yday - wday;
78 return dl <= 0 ? 0 : ((dl / 7) + ((dl % 7) == 0 ? 0 : 1));
79}
80
81
82/* Write information from TP into S according to the format
83 string FORMAT, writing no more that MAXSIZE characters
84 (including the terminating '\0') and returning number of
85 characters written. If S is NULL, nothing will be written
86 anywhere, so to determine how many characters would be
87 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
88size_t
89DEFUN(strftime, (s, maxsize, format, tp),
90 char *s AND size_t maxsize AND
91 CONST char *format AND register CONST struct tm *tp)
92{
933e73fa
RM
93 CONST char *CONST a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
94 CONST char *CONST f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
95 CONST char *CONST a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
96 CONST char *CONST f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
28f540f4
RM
97 size_t aw_len = strlen(a_wkday);
98 size_t am_len = strlen(a_month);
99 size_t wkday_len = strlen(f_wkday);
100 size_t month_len = strlen(f_month);
101 int hour12 = tp->tm_hour;
933e73fa
RM
102 CONST char *CONST ampm = _NL_CURRENT (LC_TIME,
103 hour12 > 12 ? PM_STR : AM_STR);
104 size_t ap_len = strlen (ampm);
105 CONST unsigned int y_week0 = week (tp, 0);
106 CONST unsigned int y_week1 = week (tp, 1);
28f540f4
RM
107 CONST char *zone;
108 size_t zonelen;
109 register size_t i = 0;
110 register char *p = s;
111 register CONST char *f;
112
113 if (tp->tm_isdst < 0)
114 {
115 zone = "";
116 zonelen = 0;
117 }
118 else
119 {
120 zone = __tzname[tp->tm_isdst];
121 zonelen = strlen(zone);
122 }
123
124 if (hour12 > 12)
125 hour12 -= 12;
126 else
127 if (hour12 == 0) hour12 = 12;
128
129 for (f = format; *f != '\0'; ++f)
130 {
131 CONST char *subfmt;
132
133 if (!isascii(*f))
134 {
135 /* Non-ASCII, may be a multibyte. */
136 int len = mblen(f, strlen(f));
137 if (len > 0)
138 {
139 cpy(len, f);
140 continue;
141 }
142 }
143
144 if (*f != '%')
145 {
146 add(1, *p = *f);
147 continue;
148 }
149
150 ++f;
151 switch (*f)
152 {
153 case '\0':
154 case '%':
155 add(1, *p = *f);
156 break;
157
158 case 'a':
159 cpy(aw_len, a_wkday);
160 break;
161
162 case 'A':
163 cpy(wkday_len, f_wkday);
164 break;
165
166 case 'b':
167 case 'h': /* GNU extension. */
168 cpy(am_len, a_month);
169 break;
170
171 case 'B':
172 cpy(month_len, f_month);
173 break;
174
175 case 'c':
933e73fa 176 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
28f540f4
RM
177 subformat:
178 {
179 size_t len = strftime (p, maxsize - i, subfmt, tp);
180 add(len, );
181 }
182 break;
183
184 case 'C':
185 fmt (2, (p, "%.2d", (1900 + tp->tm_year) / 100));
186 break;
187
188 case 'D': /* GNU extension. */
189 subfmt = "%m/%d/%y";
190 goto subformat;
191
192 case 'd':
193 fmt(2, (p, "%.2d", tp->tm_mday));
194 break;
195
196 case 'e': /* GNU extension: %d, but blank-padded. */
197 fmt(2, (p, "%2d", tp->tm_mday));
198 break;
199
200 case 'H':
201 fmt(2, (p, "%.2d", tp->tm_hour));
202 break;
203
204 case 'I':
205 fmt(2, (p, "%.2d", hour12));
206 break;
207
208 case 'k': /* GNU extension. */
209 fmt(2, (p, "%2d", tp->tm_hour));
210 break;
211
212 case 'l': /* GNU extension. */
213 fmt(2, (p, "%2d", hour12));
214 break;
215
216 case 'j':
217 fmt(3, (p, "%.3d", 1 + tp->tm_yday));
218 break;
219
220 case 'M':
221 fmt(2, (p, "%.2d", tp->tm_min));
222 break;
223
224 case 'm':
225 fmt(2, (p, "%.2d", tp->tm_mon + 1));
226 break;
227
228 case 'n': /* GNU extension. */
229 add (1, *p = '\n');
230 break;
231
232 case 'p':
233 cpy(ap_len, ampm);
234 break;
235
236 case 'R': /* GNU extension. */
237 subfmt = "%H:%M";
238 goto subformat;
239
240 case 'r': /* GNU extension. */
241 subfmt = "%I:%M:%S %p";
242 goto subformat;
243
244 case 'S':
245 fmt(2, (p, "%.2d", tp->tm_sec));
246 break;
247
248 case 'T': /* GNU extenstion. */
249 subfmt = "%H:%M:%S";
250 goto subformat;
251
252 case 't': /* GNU extenstion. */
253 add (1, *p = '\t');
254 break;
255
256 case 'U':
257 fmt(2, (p, "%.2u", y_week0));
258 break;
259
260 case 'W':
261 fmt(2, (p, "%.2u", y_week1));
262 break;
263
264 case 'w':
265 fmt(2, (p, "%.2d", tp->tm_wday));
266 break;
267
268 case 'X':
933e73fa 269 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
28f540f4
RM
270 goto subformat;
271
272 case 'x':
933e73fa 273 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
28f540f4
RM
274 goto subformat;
275
276 case 'Y':
277 fmt(4, (p, "%.4d", 1900 + tp->tm_year));
278 break;
279
280 case 'y':
281 fmt(2, (p, "%.2d", tp->tm_year));
282 break;
283
284 case 'Z':
285 cpy(zonelen, zone);
286 break;
287
288 default:
289 /* Bad format. */
290 break;
291 }
292 }
293
294 if (p != NULL)
295 *p = '\0';
296 return i;
297}