diff options
author | Roland McGrath <roland@gnu.org> | 1994-05-11 01:03:43 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 1994-05-11 01:03:43 +0000 |
commit | fa5e923bdf1631b04bb1baeb740bf63f37944ae8 (patch) | |
tree | 3c3cb3886d5493ac12341667eb08affb52d07b80 /time | |
parent | 04cea0abeb6e7783360e1d098a4a97fac72094e8 (diff) |
entered into RCS
Diffstat (limited to 'time')
-rw-r--r-- | time/difftime.c | 42 | ||||
-rw-r--r-- | time/zdump.c | 85 |
2 files changed, 95 insertions, 32 deletions
diff --git a/time/difftime.c b/time/difftime.c index acbb2b51fc..e33e1b6b36 100644 --- a/time/difftime.c +++ b/time/difftime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1994 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -25,5 +25,43 @@ __CONSTVALUE double DEFUN(difftime, (time1, time0), time_t time1 AND time_t time0) { - return (double) (time1 - time0); + /* Algorithm courtesy Paul Eggert (eggert@twinsun.com). */ + + time_t delta, hibit; + + if (sizeof (time_t) < sizeof (double)) + return (double) time1 - (double) time0; + if (sizeof (time_t) < sizeof (LONG_DOUBLE)) + return (LONG_DOUBLE) time1 - (LONG_DOUBLE) time0; + + if (time1 < time0) + return - difftime (time0, time1); + + /* As much as possible, avoid loss of precision by computing the + difference before converting to double. */ + delta = time1 - time0; + if (delta >= 0) + return delta; + + /* Repair delta overflow. */ + hibit = 1; + while ((hibit <<= 1) > 0) + continue; + + /* The following expression rounds twice, which means the result may not + be the closest to the true answer. For example, suppose time_t is + 64-bit signed int, long_double is IEEE 754 double with default + rounding, time1 = 9223372036854775807 and time0 = -1536. Then the + true difference is 9223372036854777343, which rounds to + 9223372036854777856 with a total error of 513. But delta overflows to + -9223372036854774273, which rounds to -9223372036854774784, and + correcting this by subtracting 2 * (long_double) hibit (i.e. by adding + 2**64 = 18446744073709551616) yields 9223372036854776832, which rounds + to 9223372036854775808 with a total error of 1535 instead. This + problem occurs only with very large differences. It's too painful to + fix this portably. We are not alone in this problem; many C compilers + round twice when converting large unsigned types to small floating + types, so if time_t is unsigned the "return delta" above has the same + double-rounding problem. */ + return delta - 2 * (LONG_DOUBLE) hibit; } diff --git a/time/zdump.c b/time/zdump.c index ada214dd2c..1e44d4a797 100644 --- a/time/zdump.c +++ b/time/zdump.c @@ -1,10 +1,6 @@ -#ifdef LIBC -#include <ansidecl.h> -#endif - #ifndef lint #ifndef NOID -static char elsieid[] = "@(#)zdump.c 7.2"; +static char elsieid[] = "@(#)zdump.c 7.10"; #endif /* !defined NOID */ #endif /* !defined lint */ @@ -43,8 +39,12 @@ static char elsieid[] = "@(#)zdump.c 7.2"; #define SECSPERMIN 60 #endif /* !defined SECSPERMIN */ +#ifndef MINSPERHOUR +#define MINSPERHOUR 60 +#endif /* !defined MINSPERHOUR */ + #ifndef SECSPERHOUR -#define SECSPERHOUR 3600 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #endif /* !defined SECSPERHOUR */ #ifndef HOURSPERDAY @@ -55,10 +55,18 @@ static char elsieid[] = "@(#)zdump.c 7.2"; #define EPOCH_YEAR 1970 #endif /* !defined EPOCH_YEAR */ +#ifndef TM_YEAR_BASE +#define TM_YEAR_BASE 1900 +#endif /* !defined TM_YEAR_BASE */ + #ifndef DAYSPERNYEAR #define DAYSPERNYEAR 365 #endif /* !defined DAYSPERNYEAR */ +#ifndef isleap +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) +#endif /* !defined isleap */ + extern char ** environ; extern int getopt(); extern char * optarg; @@ -74,7 +82,7 @@ extern void perror(); static char * abbr(); static long delta(); -static void hunt(); +static time_t hunt(); static int longest; static char * progname; static void show(); @@ -102,26 +110,28 @@ char * argv[]; vflag = 1; else cutoff = optarg; if (c != EOF || - (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { - (void) fprintf(stderr, - "%s: usage is %s [ -v ] [ -c cutoff ] zonename ...\n", - argv[0], argv[0]); - (void) exit(EXIT_FAILURE); + (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { + (void) fprintf(stderr, +"%s: usage is %s [ -v ] [ -c cutoff ] zonename ...\n", + argv[0], argv[0]); + (void) exit(EXIT_FAILURE); } - if (cutoff != NULL) + if (cutoff != NULL) { + int y; + cutyear = atoi(cutoff); - /* - ** VERY approximate. - */ - cuttime = (long) (cutyear - EPOCH_YEAR) * - SECSPERHOUR * HOURSPERDAY * DAYSPERNYEAR; + cuttime = 0; + for (y = EPOCH_YEAR; y < cutyear; ++y) + cuttime += DAYSPERNYEAR + isleap(y); + cuttime *= SECSPERHOUR * HOURSPERDAY; + } (void) time(&now); longest = 0; for (i = optind; i < argc; ++i) if (strlen(argv[i]) > longest) longest = strlen(argv[i]); for (hibit = 1; (hibit << 1) != 0; hibit <<= 1) - ; + continue; for (i = optind; i < argc; ++i) { register char ** saveenv; static char buf[MAX_STRING_LENGTH]; @@ -167,7 +177,8 @@ char * argv[]; if (delta(&newtm, &tm) != (newt - t) || newtm.tm_isdst != tm.tm_isdst || strcmp(abbr(&newtm), buf) != 0) { - hunt(argv[i], t, newt); + newt = hunt(argv[i], t, newt); + newtm = *localtime(&newt); (void) strncpy(buf, abbr(&newtm), (sizeof buf) - 1); } @@ -195,10 +206,10 @@ char * argv[]; /* gcc -Wall pacifier */ for ( ; ; ) - ; + continue; } -static void +static time_t hunt(name, lot, hit) char * name; time_t lot; @@ -227,21 +238,34 @@ time_t hit; } show(name, lot, TRUE); show(name, hit, TRUE); + return hit; } +/* +** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta. +*/ + static long delta(newp, oldp) struct tm * newp; struct tm * oldp; { long result; + int tmy; - result = newp->tm_hour - oldp->tm_hour; - if (result < 0) - result += HOURSPERDAY; - result *= SECSPERHOUR; - result += (newp->tm_min - oldp->tm_min) * SECSPERMIN; - return result + newp->tm_sec - oldp->tm_sec; + if (newp->tm_year < oldp->tm_year) + return -delta(oldp, newp); + result = 0; + for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) + result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE); + result += newp->tm_yday - oldp->tm_yday; + result *= HOURSPERDAY; + result += newp->tm_hour - oldp->tm_hour; + result *= MINSPERHOUR; + result += newp->tm_min - oldp->tm_min; + result *= SECSPERMIN; + result += newp->tm_sec - oldp->tm_sec; + return result; } static void @@ -274,9 +298,10 @@ abbr(tmp) struct tm * tmp; { register char * result; + static char nada[1]; if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) - return ""; + return nada; result = tzname[tmp->tm_isdst]; - return (result == NULL) ? "" : result; + return (result == NULL) ? nada : result; } |