1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA. */
24 # define HAVE_LIMITS_H 1
26 # define HAVE_TM_ZONE 1
27 # define STDC_HEADERS 1
28 # include <ansidecl.h>
29 # include "../locale/localeinfo.h"
33 #include <sys/types.h> /* Some systems define `time_t' here. */
35 #ifdef TIME_WITH_SYS_TIME
36 # include <sys/time.h>
39 # ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
59 # define memcpy(d, s, n) bcopy (s, d, n)
63 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
64 #define __P(args) args
78 static unsigned int week
__P((const struct tm
*const, int, int));
94 #define cpy(n, s) add((n), memcpy((PTR) p, (PTR) (s), (n)))
97 #define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
99 #define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
104 /* Return the week in the year specified by TP,
105 with weeks starting on STARTING_DAY. */
110 week (tp
, starting_day
, max_preceding
)
111 const struct tm
*const tp
;
117 wday
= tp
->tm_wday
- starting_day
;
121 /* Set DL to the day in the year of the first day of the week
122 containing the day specified in TP. */
123 dl
= tp
->tm_yday
- wday
;
125 /* For the computation following ISO 8601:1988 we set the number of
126 the week containing January 1st to 1 if this week has more than
127 MAX_PRECEDING days in the new year. For ISO 8601 this number is
128 3, for the other representation it is 7 (i.e., not to be
130 base
= ((dl
+ 7) % 7) > max_preceding
? 1 : 0;
132 /* If DL is negative we compute the result as 0 unless we have to
133 compute it according ISO 8601. In this case we have to return 53
134 or 1 if the week containing January 1st has less than 4 days in
135 the new year or not. If DL is not negative we calculate the
136 number of complete weeks for our week (DL / 7) plus 1 (because
137 only for DL < 0 we are in week 0/53 and plus the number of the
138 first week computed in the last step. */
139 return dl
< 0 ? (dl
< -max_preceding
? 53 : base
)
144 static char const weekday_name
[][10] =
146 "Sunday", "Monday", "Tuesday", "Wednesday",
147 "Thursday", "Friday", "Saturday"
149 static char const month_name
[][10] =
151 "January", "February", "March", "April", "May", "June",
152 "July", "August", "September", "October", "November", "December"
156 /* Write information from TP into S according to the format
157 string FORMAT, writing no more that MAXSIZE characters
158 (including the terminating '\0') and returning number of
159 characters written. If S is NULL, nothing will be written
160 anywhere, so to determine how many characters would be
161 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
163 strftime (s
, maxsize
, format
, tp
)
167 register const struct tm
*tp
;
169 int hour12
= tp
->tm_hour
;
171 const char *const a_wkday
= _NL_CURRENT (LC_TIME
, ABDAY_1
+ tp
->tm_wday
);
172 const char *const f_wkday
= _NL_CURRENT (LC_TIME
, DAY_1
+ tp
->tm_wday
);
173 const char *const a_month
= _NL_CURRENT (LC_TIME
, ABMON_1
+ tp
->tm_mon
);
174 const char *const f_month
= _NL_CURRENT (LC_TIME
, MON_1
+ tp
->tm_mon
);
175 const char *const ampm
= _NL_CURRENT (LC_TIME
,
176 hour12
> 11 ? PM_STR
: AM_STR
);
177 size_t aw_len
= strlen(a_wkday
);
178 size_t am_len
= strlen(a_month
);
179 size_t ap_len
= strlen (ampm
);
181 const char *const f_wkday
= weekday_name
[tp
->tm_wday
];
182 const char *const f_month
= month_name
[tp
->tm_mon
];
183 const char *const a_wkday
= f_wkday
;
184 const char *const a_month
= f_month
;
185 const char *const ampm
= "AMPM" + 2 * (hour12
> 11);
190 size_t wkday_len
= strlen(f_wkday
);
191 size_t month_len
= strlen(f_month
);
192 const unsigned int y_week0
= week (tp
, 0, 7);
193 const unsigned int y_week1
= week (tp
, 1, 7);
194 const unsigned int y_week2
= week (tp
, 1, 3);
197 register size_t i
= 0;
198 register char *p
= s
;
199 register const char *f
;
202 /* Initialize the buffer we will use for the sprintf format for numbers. */
207 zone
= (const char *) tp
->tm_zone
;
210 if (!(zone
&& *zone
) && tp
->tm_isdst
>= 0)
211 zone
= tzname
[tp
->tm_isdst
];
213 if (!(zone
&& *zone
))
216 zonelen
= strlen (zone
);
221 if (hour12
== 0) hour12
= 12;
223 for (f
= format
; *f
!= '\0'; ++f
)
225 enum { pad_zero
, pad_space
, pad_none
} pad
; /* Padding for number. */
226 unsigned int maxdigits
; /* Max digits for numeric format. */
227 unsigned int number_value
; /* Numeric value to be printed. */
233 /* Non-ASCII, may be a multibyte. */
234 int len
= mblen(f
, strlen(f
));
249 /* Check for flags that can modify a number format. */
266 /* Now do the specified format. */
275 cpy(aw_len
, a_wkday
);
279 cpy(wkday_len
, f_wkday
);
283 case 'h': /* GNU extension. */
284 cpy(am_len
, a_month
);
288 cpy(month_len
, f_month
);
293 subfmt
= _NL_CURRENT (LC_TIME
, D_T_FMT
);
295 subfmt
= "%a %b %d %H:%M:%S %Z %Y";
299 size_t len
= strftime (p
, maxsize
- i
, subfmt
, tp
);
300 if (len
== 0 && *subfmt
)
306 #define DO_NUMBER(digits, value) \
307 maxdigits = digits; number_value = value; goto do_number
308 #define DO_NUMBER_NOPAD(digits, value) \
309 maxdigits = digits; number_value = value; goto do_number_nopad
312 DO_NUMBER (2, (1900 + tp
->tm_year
) / 100);
316 subfmt
= _NL_CURRENT (LC_TIME
, D_FMT
);
320 case 'D': /* GNU extension. */
325 DO_NUMBER (2, tp
->tm_mday
);
327 case 'e': /* GNU extension: %d, but blank-padded. */
328 DO_NUMBER_NOPAD (2, tp
->tm_mday
);
330 /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
331 jump to one of these two labels. */
334 /* Force `-' flag. */
339 /* Format the number according to the PAD flag. */
341 register char *nf
= &number_fmt
[1];
349 *nf
++ = '0' + maxdigits
;
356 add (maxdigits
, printed
= sprintf (p
, number_fmt
, number_value
));
358 add (maxdigits
, sprintf (p
, number_fmt
, number_value
);
359 printed
= strlen (p
));
367 DO_NUMBER (2, tp
->tm_hour
);
370 DO_NUMBER (2, hour12
);
372 case 'k': /* GNU extension. */
373 DO_NUMBER_NOPAD (2, tp
->tm_hour
);
375 case 'l': /* GNU extension. */
376 DO_NUMBER_NOPAD (2, hour12
);
379 DO_NUMBER (3, 1 + tp
->tm_yday
);
382 DO_NUMBER (2, tp
->tm_min
);
385 DO_NUMBER (2, tp
->tm_mon
+ 1);
387 case 'n': /* GNU extension. */
395 case 'R': /* GNU extension. */
399 case 'r': /* GNU extension. */
400 subfmt
= "%I:%M:%S %p";
404 DO_NUMBER (2, tp
->tm_sec
);
408 subfmt
= _NL_CURRENT (LC_TIME
, T_FMT
);
412 case 'T': /* GNU extenstion. */
416 case 't': /* GNU extenstion. */
421 DO_NUMBER (2, y_week0
);
424 DO_NUMBER (2, y_week2
);
427 DO_NUMBER (2, y_week1
);
430 DO_NUMBER (2, tp
->tm_wday
);
433 DO_NUMBER (4, 1900 + tp
->tm_year
);
436 DO_NUMBER (2, tp
->tm_year
% 100);