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_GMTOFF 1
27 # define HAVE_TM_ZONE 1
28 # define STDC_HEADERS 1
29 # include <ansidecl.h>
30 # 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
82 #define TYPE_SIGNED(t) ((t) -1 < 0)
84 /* Bound on length of the string representing an integer value of type t.
85 Subtract one for the sign bit if t is signed;
86 302 / 1000 is log10 (2) rounded up;
87 add one for integer division truncation;
88 add one more for a minus sign if t is signed. */
89 #define INT_STRLEN_BOUND(t) \
90 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
92 #define TM_YEAR_BASE 1900
95 static unsigned int week
__P ((const struct tm
*const, int, int));
111 #define cpy(n, s) add ((n), memcpy((PTR) p, (PTR) (s), (n)))
114 /* Yield the difference between *A and *B,
115 measured in seconds, ignoring leap seconds. */
116 static int tm_diff
__P ((const struct tm
*, const struct tm
*));
122 int ay
= a
->tm_year
+ TM_YEAR_BASE
- 1;
123 int by
= b
->tm_year
+ TM_YEAR_BASE
- 1;
124 /* Divide years by 100, rounding towards minus infinity. */
125 int ac
= ay
/ 100 - (ay
% 100 < 0);
126 int bc
= by
/ 100 - (by
% 100 < 0);
127 int intervening_leap_days
=
128 ((ay
>> 2) - (by
>> 2)) - (ac
- bc
) + ((ac
>> 2) - (bc
>> 2));
130 int days
= (365 * years
+ intervening_leap_days
131 + (a
->tm_yday
- b
->tm_yday
));
132 return (60 * (60 * (24 * days
+ (a
->tm_hour
- b
->tm_hour
))
133 + (a
->tm_min
- b
->tm_min
))
134 + (a
->tm_sec
- b
->tm_sec
));
136 #endif /* ! HAVE_TM_GMTOFF */
140 /* Return the week in the year specified by TP,
141 with weeks starting on STARTING_DAY. */
146 week (tp
, starting_day
, max_preceding
)
147 const struct tm
*const tp
;
153 wday
= tp
->tm_wday
- starting_day
;
157 /* Set DL to the day in the year of the first day of the week
158 containing the day specified in TP. */
159 dl
= tp
->tm_yday
- wday
;
161 /* For the computation following ISO 8601:1988 we set the number of
162 the week containing January 1st to 1 if this week has more than
163 MAX_PRECEDING days in the new year. For ISO 8601 this number is
164 3, for the other representation it is 7 (i.e., not to be
166 base
= ((dl
+ 7) % 7) > max_preceding
? 1 : 0;
168 /* If DL is negative we compute the result as 0 unless we have to
169 compute it according ISO 8601. In this case we have to return 53
170 or 1 if the week containing January 1st has less than 4 days in
171 the new year or not. If DL is not negative we calculate the
172 number of complete weeks for our week (DL / 7) plus 1 (because
173 only for DL < 0 we are in week 0/53 and plus the number of the
174 first week computed in the last step. */
175 return dl
< 0 ? (dl
< -max_preceding
? 53 : base
)
180 static char const weekday_name
[][10] =
182 "Sunday", "Monday", "Tuesday", "Wednesday",
183 "Thursday", "Friday", "Saturday"
185 static char const month_name
[][10] =
187 "January", "February", "March", "April", "May", "June",
188 "July", "August", "September", "October", "November", "December"
192 /* Write information from TP into S according to the format
193 string FORMAT, writing no more that MAXSIZE characters
194 (including the terminating '\0') and returning number of
195 characters written. If S is NULL, nothing will be written
196 anywhere, so to determine how many characters would be
197 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
199 strftime (s
, maxsize
, format
, tp
)
203 register const struct tm
*tp
;
205 int hour12
= tp
->tm_hour
;
207 const char *const a_wkday
= _NL_CURRENT (LC_TIME
, ABDAY_1
+ tp
->tm_wday
);
208 const char *const f_wkday
= _NL_CURRENT (LC_TIME
, DAY_1
+ tp
->tm_wday
);
209 const char *const a_month
= _NL_CURRENT (LC_TIME
, ABMON_1
+ tp
->tm_mon
);
210 const char *const f_month
= _NL_CURRENT (LC_TIME
, MON_1
+ tp
->tm_mon
);
211 const char *const ampm
= _NL_CURRENT (LC_TIME
,
212 hour12
> 11 ? PM_STR
: AM_STR
);
213 size_t aw_len
= strlen(a_wkday
);
214 size_t am_len
= strlen(a_month
);
215 size_t ap_len
= strlen (ampm
);
217 const char * const*alt_digits
= &_NL_CURRENT (LC_TIME
, ALT_DIGITS
);
218 int nr_alt_digits
= (_NL_CURRENT (LC_TIME
, ALT_DIGITS
+ 1) - *alt_digits
);
220 const char *const f_wkday
= weekday_name
[tp
->tm_wday
];
221 const char *const f_month
= month_name
[tp
->tm_mon
];
222 const char *const a_wkday
= f_wkday
;
223 const char *const a_month
= f_month
;
224 const char *const ampm
= "AMPM" + 2 * (hour12
> 11);
229 size_t wkday_len
= strlen (f_wkday
);
230 size_t month_len
= strlen (f_month
);
231 const unsigned int y_week0
= week (tp
, 0, 7);
232 const unsigned int y_week1
= week (tp
, 1, 7);
233 const unsigned int y_week2
= week (tp
, 1, 3);
236 register size_t i
= 0;
237 register char *p
= s
;
238 register const char *f
;
242 zone
= (const char *) tp
->tm_zone
;
245 if (!(zone
&& *zone
) && tp
->tm_isdst
>= 0)
246 zone
= tzname
[tp
->tm_isdst
];
248 if (!(zone
&& *zone
))
251 zonelen
= strlen (zone
);
256 if (hour12
== 0) hour12
= 12;
258 for (f
= format
; *f
!= '\0'; ++f
)
260 enum { pad_zero
, pad_space
, pad_none
} pad
; /* Padding for number. */
261 unsigned int digits
; /* Max digits for numeric format. */
262 unsigned int number_value
; /* Numeric value to be printed. */
263 int negative_number
; /* 1 if the number is negative. */
264 const char *subfmt
= "";
265 enum { none
, alternate
, era
} modifier
;
267 char buf
[1 + (sizeof (int) < sizeof (time_t)
268 ? INT_STRLEN_BOUND (time_t)
269 : INT_STRLEN_BOUND (int))];
274 /* Non-ASCII, may be a multibyte. */
275 int len
= mblen (f
, strlen (f
));
290 /* Check for flags that can modify a number format. */
307 /* Check for modifiers. */
316 modifier
= alternate
;
323 /* Now do the specified format. */
326 #define DO_NUMBER(d, v) \
327 digits = d; number_value = v; goto do_number
328 #define DO_NUMBER_SPACEPAD(d, v) \
329 digits = d; number_value = v; goto do_number_spacepad
331 case '\0': /* GNU extension: % at end of format. */
335 if (modifier
!= none
)
341 if (modifier
!= none
)
343 cpy (aw_len
, a_wkday
);
347 if (modifier
!= none
)
349 cpy (wkday_len
, f_wkday
);
353 case 'h': /* GNU extension. */
354 if (modifier
!= none
)
356 cpy (am_len
, a_month
);
360 if (modifier
!= none
)
362 cpy (month_len
, f_month
);
366 if (modifier
== alternate
)
370 subfmt
= _NL_CURRENT (LC_TIME
, ERA_D_T_FMT
);
372 subfmt
= _NL_CURRENT (LC_TIME
, D_T_FMT
);
374 subfmt
= "%a %b %e %H:%M:%S %Z %Y";
379 size_t len
= strftime (p
, maxsize
- i
, subfmt
, tp
);
380 if (len
== 0 && *subfmt
)
387 if (modifier
== alternate
)
390 /* XXX I'm not sure about this. --drepper@gnu */
391 if (modifier
== era
&&
392 *(subfmt
= _NL_CURRENT (LC_TIME
, ERA
)) != '\0')
395 DO_NUMBER (2, (1900 + tp
->tm_year
) / 100);
398 if (modifier
== alternate
)
402 subfmt
= _NL_CURRENT (LC_TIME
, ERA_D_FMT
);
404 subfmt
= _NL_CURRENT (LC_TIME
, D_FMT
);
408 case 'D': /* GNU extension. */
416 DO_NUMBER (2, tp
->tm_mday
);
418 case 'e': /* GNU extension: %d, but blank-padded. */
422 DO_NUMBER_SPACEPAD (2, tp
->tm_mday
);
424 /* All numeric formats set DIGITS and NUMBER_VALUE and then
425 jump to one of these two labels. */
428 /* Force `_' flag. */
432 /* Format the number according to the MODIFIER flag. */
435 if (modifier
== alternate
&& 0 <= number_value
436 && number_value
< (unsigned int) nr_alt_digits
)
438 /* ALT_DIGITS is the first entry in an array with
439 alternative digit symbols. */
440 size_t digitlen
= strlen (*(alt_digits
+ number_value
));
443 cpy (digitlen
, *(alt_digits
+ number_value
));
444 goto done_with_number
;
448 unsigned int u
= number_value
;
450 bufp
= buf
+ sizeof (buf
);
451 negative_number
= number_value
< 0;
457 *--bufp
= u
% 10 + '0';
458 while ((u
/= 10) != 0);
461 do_number_sign_and_padding
:
467 int padding
= digits
- (buf
+ sizeof (buf
) - bufp
);
469 if (pad
== pad_space
)
471 while (0 < padding
--)
476 bufp
+= negative_number
;
477 while (0 < padding
--)
484 cpy (buf
+ sizeof (buf
) - bufp
, bufp
);
496 DO_NUMBER (2, tp
->tm_hour
);
502 DO_NUMBER (2, hour12
);
504 case 'k': /* GNU extension. */
508 DO_NUMBER_SPACEPAD (2, tp
->tm_hour
);
510 case 'l': /* GNU extension. */
514 DO_NUMBER_SPACEPAD (2, hour12
);
520 DO_NUMBER (3, 1 + tp
->tm_yday
);
526 DO_NUMBER (2, tp
->tm_min
);
532 DO_NUMBER (2, tp
->tm_mon
+ 1);
534 case 'n': /* GNU extension. */
542 case 'R': /* GNU extension. */
546 case 'r': /* GNU extension. */
547 subfmt
= "%I:%M:%S %p";
554 DO_NUMBER (2, tp
->tm_sec
);
556 case 's': /* GNU extension. */
559 time_t t
= mktime (<m
);
561 /* Generate string value for T using time_t arithmetic;
562 this works even if sizeof (long) < sizeof (time_t). */
564 bufp
= buf
+ sizeof (buf
);
565 negative_number
= t
< 0;
576 /* Adjust if division truncates to minus infinity. */
577 if (0 < -1 % 10 && d
< 0)
589 goto do_number_sign_and_padding
;
593 if (modifier
== alternate
)
597 subfmt
= _NL_CURRENT (LC_TIME
, ERA_T_FMT
);
599 subfmt
= _NL_CURRENT (LC_TIME
, T_FMT
);
603 case 'T': /* GNU extension. */
607 case 't': /* GNU extension. */
615 DO_NUMBER (2, y_week0
);
621 DO_NUMBER (2, y_week2
);
627 DO_NUMBER (2, y_week1
);
633 DO_NUMBER (2, tp
->tm_wday
);
638 && *(subfmt
= _NL_CURRENT (LC_TIME
, ERA_YEAR
)) != '\0')
642 if (modifier
== alternate
)
645 DO_NUMBER (4, 1900 + tp
->tm_year
);
650 && *(subfmt
= _NL_CURRENT (LC_TIME
, ERA_YEAR
)) != '\0')
653 DO_NUMBER (2, tp
->tm_year
% 100);
659 case 'z': /* GNU extension. */
660 if (tp
->tm_isdst
< 0)
666 diff
= tp
->tm_gmtoff
;
670 time_t lt
= mktime (<m
);
672 if (lt
== (time_t) -1)
674 /* mktime returns -1 for errors, but -1 is also a
675 valid time_t value. Check whether an error really
678 localtime_r (<
, &tm
);
680 if ((ltm
.tm_sec
^ tm
.tm_sec
)
681 | (ltm
.tm_min
^ tm
.tm_min
)
682 | (ltm
.tm_hour
^ tm
.tm_hour
)
683 | (ltm
.tm_mday
^ tm
.tm_mday
)
684 | (ltm
.tm_mon
^ tm
.tm_mon
)
685 | (ltm
.tm_year
^ tm
.tm_year
))
689 if (! gmtime_r (<
, >m
))
692 diff
= tm_diff (<m
, >m
);
706 DO_NUMBER (4, (diff
/ 60) * 100 + diff
% 60);
712 if (pad
== pad_space
)
714 else if (pad
== pad_zero
)
719 else if (modifier
== alternate
)