1/* Copyright (C) 1993-2002 Free Software Foundation, Inc. 2 This file is part of the GNU C Library. 3 Contributed by Paul Eggert (eggert@twinsun.com). 4 5 The GNU C Library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public License as 7 published by the Free Software Foundation; either version 2 of the 8 License, or (at your option) any later version. 9 10 The GNU C Library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with the GNU C Library; see the file COPYING.LIB. If not, 17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 Boston, MA 02111-1307, USA. */ 19 20/* Define this to have a standalone program to test this implementation of 21 mktime. */ 22/* #define DEBUG 1 */ 23 24#ifdef HAVE_CONFIG_H 25#include <config.h> 26#endif 27 28#ifdef _LIBC 29# define HAVE_LIMITS_H 1 30# define HAVE_LOCALTIME_R 1 31# define STDC_HEADERS 1 32#endif 33 34/* Assume that leap seconds are possible, unless told otherwise. 35 If the host has a `zic' command with a `-L leapsecondfilename' option, 36 then it supports leap seconds; otherwise it probably doesn't. */ 37#ifndef LEAP_SECONDS_POSSIBLE 38#define LEAP_SECONDS_POSSIBLE 1 39#endif 40 41#ifndef VMS 42#include <sys/types.h> /* Some systems define `time_t' here. */ 43#else 44#include <stddef.h> 45#endif 46#include <time.h> 47 48#if HAVE_LIMITS_H 49#include <limits.h> 50#endif 51 52#if DEBUG 53#include <stdio.h> 54#if STDC_HEADERS 55#include <stdlib.h> 56#endif 57/* Make it work even if the system's libc has its own mktime routine. */ 58#define mktime my_mktime 59#endif /* DEBUG */ 60 61#ifndef __P 62#if defined (__GNUC__) || (defined (__STDC__) && __STDC__) 63#define __P(args) args 64#else 65#define __P(args) () 66#endif /* GCC. */ 67#endif /* Not __P. */ 68 69#ifndef CHAR_BIT 70#define CHAR_BIT 8 71#endif 72 73#ifndef INT_MIN 74#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1)) 75#endif 76#ifndef INT_MAX 77#define INT_MAX (~0 - INT_MIN) 78#endif 79 80#ifndef TIME_T_MIN 81#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \ 82 : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)) 83#endif 84#ifndef TIME_T_MAX 85#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN) 86#endif 87 88#define TM_YEAR_BASE 1900 89#define EPOCH_YEAR 1970 90 91#ifndef __isleap 92/* Nonzero if YEAR is a leap year (every 4 years, 93 except every 100th isn't, and every 400th is). */ 94#define __isleap(year) \ 95 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) 96#endif 97 98/* How many days come before each month (0-12). */ 99const unsigned short int __mon_yday[2][13] = 100 { 101 /* Normal years. */ 102 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 103 /* Leap years. */ 104 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 105 }; 106 107static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *)); 108time_t __mktime_internal __P ((struct tm *, 109 struct tm *(*) (const time_t *, struct tm *), 110 time_t *)); 111 112 113static struct tm *my_localtime_r __P ((const time_t *, struct tm *)); 114static struct tm * 115my_localtime_r (t, tp) 116 const time_t *t; 117 struct tm *tp; 118{ 119 struct tm *l = localtime (t); 120 if (! l) 121 return 0; 122 *tp = *l; 123 return tp; 124} 125 126 127/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP), 128 measured in seconds, ignoring leap seconds. 129 YEAR uses the same numbering as TM->tm_year. 130 All values are in range, except possibly YEAR. 131 If overflow occurs, yield the low order bits of the correct answer. */ 132static time_t 133ydhms_tm_diff (year, yday, hour, min, sec, tp) 134 int year, yday, hour, min, sec; 135 const struct tm *tp; 136{ 137 /* Compute intervening leap days correctly even if year is negative. 138 Take care to avoid int overflow. time_t overflow is OK, since 139 only the low order bits of the correct time_t answer are needed. 140 Don't convert to time_t until after all divisions are done, since 141 time_t might be unsigned. */ 142 int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3); 143 int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3); 144 int a100 = a4 / 25 - (a4 % 25 < 0); 145 int b100 = b4 / 25 - (b4 % 25 < 0); 146 int a400 = a100 >> 2; 147 int b400 = b100 >> 2; 148 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); 149 time_t years = year - (time_t) tp->tm_year; 150 time_t days = (365 * years + intervening_leap_days 151 + (yday - tp->tm_yday)); 152 return (60 * (60 * (24 * days + (hour - tp->tm_hour)) 153 + (min - tp->tm_min)) 154 + (sec - tp->tm_sec)); 155} 156 157 158static time_t localtime_offset; 159 160/* Convert *TP to a time_t value. */ 161time_t 162mktime (tp) 163 struct tm *tp; 164{ 165#ifdef _LIBC 166 /* POSIX.1 8.1.1 requires that whenever mktime() is called, the 167 time zone names contained in the external variable `tzname' shall 168 be set as if the tzset() function had been called. */ 169 __tzset (); 170#endif 171 172 return __mktime_internal (tp, my_localtime_r, &localtime_offset); 173} 174 175/* Convert *TP to a time_t value, inverting 176 the monotonic and mostly-unit-linear conversion function CONVERT. 177 Use *OFFSET to keep track of a guess at the offset of the result, 178 compared to what the result would be for UTC without leap seconds. 179 If *OFFSET's guess is correct, only one CONVERT call is needed. */ 180time_t 181__mktime_internal (tp, convert, offset) 182 struct tm *tp; 183 struct tm *(*convert) __P ((const time_t *, struct tm *)); 184 time_t *offset; 185{ 186 time_t t, dt, t0; 187 struct tm tm; 188 189 /* The maximum number of probes (calls to CONVERT) should be enough 190 to handle any combinations of time zone rule changes, solar time, 191 and leap seconds. Posix.1 prohibits leap seconds, but some hosts 192 have them anyway. */ 193 int remaining_probes = 4; 194 195 /* Time requested. Copy it in case CONVERT modifies *TP; this can 196 occur if TP is localtime's returned value and CONVERT is localtime. */ 197 int sec = tp->tm_sec; 198 int min = tp->tm_min; 199 int hour = tp->tm_hour; 200 int mday = tp->tm_mday; 201 int mon = tp->tm_mon; 202 int year_requested = tp->tm_year; 203 int isdst = tp->tm_isdst; 204 205 /* Ensure that mon is in range, and set year accordingly. */ 206 int mon_remainder = mon % 12; 207 int negative_mon_remainder = mon_remainder < 0; 208 int mon_years = mon / 12 - negative_mon_remainder; 209 int year = year_requested + mon_years; 210 211 /* The other values need not be in range: 212 the remaining code handles minor overflows correctly, 213 assuming int and time_t arithmetic wraps around. 214 Major overflows are caught at the end. */ 215 216 /* Calculate day of year from year, month, and day of month. 217 The result need not be in range. */ 218 int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)] 219 [mon_remainder + 12 * negative_mon_remainder]) 220 + mday - 1); 221 222#if LEAP_SECONDS_POSSIBLE 223 /* Handle out-of-range seconds specially, 224 since ydhms_tm_diff assumes every minute has 60 seconds. */ 225 int sec_requested = sec; 226 if (sec < 0) 227 sec = 0; 228 if (59 < sec) 229 sec = 59; 230#endif 231 232 /* Invert CONVERT by probing. First assume the same offset as last time. 233 Then repeatedly use the error to improve the guess. */ 234 235 tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE; 236 tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 237 t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm); 238 239 for (t = t0 + *offset; 240 (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm))); 241 t += dt) 242 if (--remaining_probes == 0) 243 return -1; 244 245 /* Check whether tm.tm_isdst has the requested value, if any. */ 246 if (0 <= isdst && 0 <= tm.tm_isdst) 247 { 248 int dst_diff = (isdst != 0) - (tm.tm_isdst != 0); 249 if (dst_diff) 250 { 251 /* Move two hours in the direction indicated by the disagreement, 252 probe some more, and switch to a new time if found. 253 The largest known fallback due to daylight savings is two hours: 254 once, in Newfoundland, 1988-10-30 02:00 -> 00:00. */ 255 time_t ot = t - 2 * 60 * 60 * dst_diff; 256 while (--remaining_probes != 0) 257 { 258 struct tm otm; 259 if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec, 260 (*convert) (&ot, &otm)))) 261 { 262 t = ot; 263 tm = otm; 264 break; 265 } 266 if ((ot += dt) == t) 267 break; /* Avoid a redundant probe. */ 268 } 269 } 270 } 271 272 *offset = t - t0; 273 274#if LEAP_SECONDS_POSSIBLE 275 if (sec_requested != tm.tm_sec) 276 { 277 /* Adjust time to reflect the tm_sec requested, not the normalized value. 278 Also, repair any damage from a false match due to a leap second. */ 279 t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60); 280 (*convert) (&t, &tm); 281 } 282#endif 283 284 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) 285 { 286 /* time_t isn't large enough to rule out overflows in ydhms_tm_diff, 287 so check for major overflows. A gross check suffices, 288 since if t has overflowed, it is off by a multiple of 289 TIME_T_MAX - TIME_T_MIN + 1. So ignore any component of 290 the difference that is bounded by a small value. */ 291 292 double dyear = (double) year_requested + mon_years - tm.tm_year; 293 double dday = 366 * dyear + mday; 294 double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested; 295 296 if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec)) 297 return -1; 298 } 299 300 *tp = tm; 301 return t; 302} 303 304#ifdef weak_alias 305weak_alias (mktime, timelocal) 306#endif 307 308#if DEBUG 309 310static int 311not_equal_tm (a, b) 312 struct tm *a; 313 struct tm *b; 314{ 315 return ((a->tm_sec ^ b->tm_sec) 316 | (a->tm_min ^ b->tm_min) 317 | (a->tm_hour ^ b->tm_hour) 318 | (a->tm_mday ^ b->tm_mday) 319 | (a->tm_mon ^ b->tm_mon) 320 | (a->tm_year ^ b->tm_year) 321 | (a->tm_mday ^ b->tm_mday) 322 | (a->tm_yday ^ b->tm_yday) 323 | (a->tm_isdst ^ b->tm_isdst)); 324} 325 326static void 327print_tm (tp) 328 struct tm *tp; 329{ 330 printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d", 331 tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday, 332 tp->tm_hour, tp->tm_min, tp->tm_sec, 333 tp->tm_yday, tp->tm_wday, tp->tm_isdst); 334} 335 336static int 337check_result (tk, tmk, tl, tml) 338 time_t tk; 339 struct tm tmk; 340 time_t tl; 341 struct tm tml; 342{ 343 if (tk != tl || not_equal_tm (&tmk, &tml)) 344 { 345 printf ("mktime ("); 346 print_tm (&tmk); 347 printf (")\nyields ("); 348 print_tm (&tml); 349 printf (") == %ld, should be %ld\n", (long) tl, (long) tk); 350 return 1; 351 } 352 353 return 0; 354} 355 356int 357main (argc, argv) 358 int argc; 359 char **argv; 360{ 361 int status = 0; 362 struct tm tm, tmk, tml; 363 time_t tk, tl; 364 char trailer; 365 366 if ((argc == 3 || argc == 4) 367 && (sscanf (argv[1], "%d-%d-%d%c", 368 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer) 369 == 3) 370 && (sscanf (argv[2], "%d:%d:%d%c", 371 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer) 372 == 3)) 373 { 374 tm.tm_year -= TM_YEAR_BASE; 375 tm.tm_mon--; 376 tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]); 377 tmk = tm; 378 tl = mktime (&tmk); 379 tml = *localtime (&tl); 380 printf ("mktime returns %ld == ", (long) tl); 381 print_tm (&tmk); 382 printf ("\n"); 383 status = check_result (tl, tmk, tl, tml); 384 } 385 else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0)) 386 { 387 time_t from = atol (argv[1]); 388 time_t by = atol (argv[2]); 389 time_t to = atol (argv[3]); 390 391 if (argc == 4) 392 for (tl = from; tl <= to; tl += by) 393 { 394 tml = *localtime (&tl); 395 tmk = tml; 396 tk = mktime (&tmk); 397 status |= check_result (tk, tmk, tl, tml); 398 } 399 else 400 for (tl = from; tl <= to; tl += by) 401 { 402 /* Null benchmark. */ 403 tml = *localtime (&tl); 404 tmk = tml; 405 tk = tl; 406 status |= check_result (tk, tmk, tl, tml); 407 } 408 } 409 else 410 printf ("Usage:\ 411\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\ 412\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\ 413\t%s FROM BY TO - # Do not test those values (for benchmark).\n", 414 argv[0], argv[0], argv[0]); 415 416 return status; 417} 418 419#endif /* DEBUG */ 420 421/* 422Local Variables: 423compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime" 424End: 425*/ 426