diff options
Diffstat (limited to 'crypto/o_time.c')
-rw-r--r-- | crypto/o_time.c | 151 |
1 files changed, 150 insertions, 1 deletions
diff --git a/crypto/o_time.c b/crypto/o_time.c index 607556b0a6..eecbdd19f0 100644 --- a/crypto/o_time.c +++ b/crypto/o_time.c @@ -2,6 +2,9 @@ /* Written by Richard Levitte (richard@levitte.org) for the OpenSSL * project 2001. */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project 2008. + */ /* ==================================================================== * Copyright (c) 2001 The OpenSSL Project. All rights reserved. * @@ -214,4 +217,150 @@ struct tm *OPENSSL_gmtime(const time_t *timer, struct tm *result) } #endif return ts; - } + } + +/* Take a tm structure and add an offset to it. This avoids any OS issues + * with restricted date types and overflows which cause the year 2038 + * problem. + */ + +#define SECS_PER_DAY (24 * 60 * 60) + +static long date_to_julian(int y, int m, int d); +static void julian_to_date(long jd, int *y, int *m, int *d); + +int OPENSSL_gmtime_adj(struct tm *tm, int off_day, long offset_sec) + { + int offset_hms, offset_day; + long time_jd; + int time_year, time_month, time_day; + /* split offset into days and day seconds */ + offset_day = offset_sec / SECS_PER_DAY; + /* Avoid sign issues with % operator */ + offset_hms = offset_sec - (offset_day * SECS_PER_DAY); + offset_day += off_day; + /* Add current time seconds to offset */ + offset_hms += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; + /* Adjust day seconds if overflow */ + if (offset_hms >= SECS_PER_DAY) + { + offset_day++; + offset_hms -= SECS_PER_DAY; + } + else if (offset_hms < 0) + { + offset_day--; + offset_hms += SECS_PER_DAY; + } + + /* Convert date of time structure into a Julian day number. + */ + + time_year = tm->tm_year + 1900; + time_month = tm->tm_mon + 1; + time_day = tm->tm_mday; + + time_jd = date_to_julian(time_year, time_month, time_day); + + /* Work out Julian day of new date */ + time_jd += offset_day; + + if (time_jd < 0) + return 0; + + /* Convert Julian day back to date */ + + julian_to_date(time_jd, &time_year, &time_month, &time_day); + + if (time_year < 1900 || time_year > 9999) + return 0; + + /* Update tm structure */ + + tm->tm_year = time_year - 1900; + tm->tm_mon = time_month - 1; + tm->tm_mday = time_day; + + tm->tm_hour = offset_hms / 3600; + tm->tm_min = (offset_hms / 60) % 60; + tm->tm_sec = offset_hms % 60; + + return 1; + +} + +/* Convert date to and from julian day + * Uses Fliegel & Van Flandern algorithm + */ +static long date_to_julian(int y, int m, int d) +{ + return (1461 * (y + 4800 + (m - 14) / 12)) / 4 + + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 - + (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + + d - 32075; +} + +static void julian_to_date(long jd, int *y, int *m, int *d) + { + long L = jd + 68569; + long n = (4 * L) / 146097; + long i, j; + + L = L - (146097 * n + 3) / 4; + i = (4000 * (L + 1)) / 1461001; + L = L - (1461 * i) / 4 + 31; + j = (80 * L) / 2447; + *d = L - (2447 * j) / 80; + L = j / 11; + *m = j + 2 - (12 * L); + *y = 100 * (n - 49) + i + L; + } + +#ifdef OPENSSL_TIME_TEST + +#include <stdio.h> + +/* Time checking test code. Check times are identical for a wide range of + * offsets. This should be run on a machine with 64 bit time_t or it will + * trigger the very errors the routines fix. + */ + +int main(int argc, char **argv) + { + long offset; + for (offset = 0; offset < 1000000; offset++) + { + check_time(offset); + check_time(-offset); + check_time(offset * 1000); + check_time(-offset * 1000); + } + } + +int check_time(long offset) + { + struct tm tm1, tm2; + time_t t1, t2; + time(&t1); + t2 = t1 + offset; + OPENSSL_gmtime(&t2, &tm2); + OPENSSL_gmtime(&t1, &tm1); + OPENSSL_gmtime_adj(&tm1, 0, offset); + if ((tm1.tm_year == tm2.tm_year) && + (tm1.tm_mon == tm2.tm_mon) && + (tm1.tm_mday == tm2.tm_mday) && + (tm1.tm_hour == tm2.tm_hour) && + (tm1.tm_min == tm2.tm_min) && + (tm1.tm_sec == tm2.tm_sec)) + return 1; + fprintf(stderr, "TIME ERROR!!\n"); + fprintf(stderr, "Time1: %d/%d/%d, %d:%02d:%02d\n", + tm2.tm_mday, tm2.tm_mon + 1, tm2.tm_year + 1900, + tm2.tm_hour, tm2.tm_min, tm2.tm_sec); + fprintf(stderr, "Time2: %d/%d/%d, %d:%02d:%02d\n", + tm1.tm_mday, tm1.tm_mon + 1, tm1.tm_year + 1900, + tm1.tm_hour, tm1.tm_min, tm1.tm_sec); + return 0; + } + +#endif |