1 /* $OpenBSD: o_time.c,v 1.16 2021/10/27 09:50:56 beck Exp $ */ 2 /* Written by Richard Levitte (richard@levitte.org) for the OpenSSL 3 * project 2001. 4 */ 5 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 6 * project 2008. 7 */ 8 /* ==================================================================== 9 * Copyright (c) 2001 The OpenSSL Project. All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in 20 * the documentation and/or other materials provided with the 21 * distribution. 22 * 23 * 3. All advertising materials mentioning features or use of this 24 * software must display the following acknowledgment: 25 * "This product includes software developed by the OpenSSL Project 26 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 27 * 28 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 29 * endorse or promote products derived from this software without 30 * prior written permission. For written permission, please contact 31 * licensing@OpenSSL.org. 32 * 33 * 5. Products derived from this software may not be called "OpenSSL" 34 * nor may "OpenSSL" appear in their names without prior written 35 * permission of the OpenSSL Project. 36 * 37 * 6. Redistributions of any form whatsoever must retain the following 38 * acknowledgment: 39 * "This product includes software developed by the OpenSSL Project 40 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 41 * 42 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 43 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 45 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 46 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 47 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 48 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 49 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 51 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 52 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 53 * OF THE POSSIBILITY OF SUCH DAMAGE. 54 * ==================================================================== 55 * 56 * This product includes cryptographic software written by Eric Young 57 * (eay@cryptsoft.com). This product includes software written by Tim 58 * Hudson (tjh@cryptsoft.com). 59 * 60 */ 61 62 #include <string.h> 63 64 #include "o_time.h" 65 66 /* Take a tm structure and add an offset to it. This avoids any OS issues 67 * with restricted date types and overflows which cause the year 2038 68 * problem. 69 */ 70 71 #define SECS_PER_DAY (24 * 60 * 60) 72 73 static long date_to_julian(int y, int m, int d); 74 static void julian_to_date(long jd, int *y, int *m, int *d); 75 static int julian_adj(const struct tm *tm, int off_day, long offset_sec, 76 long *pday, int *psec); 77 78 int 79 OPENSSL_gmtime_adj(struct tm *tm, int off_day, long offset_sec) 80 { 81 int offset_hms, offset_day; 82 long time_jd; 83 int time_year, time_month, time_day; 84 /* split offset into days and day seconds */ 85 offset_day = offset_sec / SECS_PER_DAY; 86 /* Avoid sign issues with % operator */ 87 offset_hms = offset_sec - (offset_day * SECS_PER_DAY); 88 offset_day += off_day; 89 /* Add current time seconds to offset */ 90 offset_hms += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; 91 /* Adjust day seconds if overflow */ 92 if (offset_hms >= SECS_PER_DAY) { 93 offset_day++; 94 offset_hms -= SECS_PER_DAY; 95 } else if (offset_hms < 0) { 96 offset_day--; 97 offset_hms += SECS_PER_DAY; 98 } 99 100 /* Convert date of time structure into a Julian day number. 101 */ 102 103 time_year = tm->tm_year + 1900; 104 time_month = tm->tm_mon + 1; 105 time_day = tm->tm_mday; 106 107 time_jd = date_to_julian(time_year, time_month, time_day); 108 109 /* Work out Julian day of new date */ 110 time_jd += offset_day; 111 112 if (time_jd < 0) 113 return 0; 114 115 /* Convert Julian day back to date */ 116 117 julian_to_date(time_jd, &time_year, &time_month, &time_day); 118 119 if (time_year < 1900 || time_year > 9999) 120 return 0; 121 122 /* Update tm structure */ 123 124 tm->tm_year = time_year - 1900; 125 tm->tm_mon = time_month - 1; 126 tm->tm_mday = time_day; 127 128 tm->tm_hour = offset_hms / 3600; 129 tm->tm_min = (offset_hms / 60) % 60; 130 tm->tm_sec = offset_hms % 60; 131 132 return 1; 133 134 } 135 136 int 137 OPENSSL_gmtime_diff(int *pday, int *psec, const struct tm *from, 138 const struct tm *to) 139 { 140 int from_sec, to_sec, diff_sec; 141 long from_jd, to_jd, diff_day; 142 143 if (!julian_adj(from, 0, 0, &from_jd, &from_sec)) 144 return 0; 145 if (!julian_adj(to, 0, 0, &to_jd, &to_sec)) 146 return 0; 147 diff_day = to_jd - from_jd; 148 diff_sec = to_sec - from_sec; 149 /* Adjust differences so both positive or both negative */ 150 if (diff_day > 0 && diff_sec < 0) { 151 diff_day--; 152 diff_sec += SECS_PER_DAY; 153 } 154 if (diff_day < 0 && diff_sec > 0) { 155 diff_day++; 156 diff_sec -= SECS_PER_DAY; 157 } 158 159 if (pday) 160 *pday = (int)diff_day; 161 if (psec) 162 *psec = diff_sec; 163 164 return 1; 165 166 } 167 168 /* Convert tm structure and offset into julian day and seconds */ 169 static int 170 julian_adj(const struct tm *tm, int off_day, long offset_sec, long *pday, 171 int *psec) 172 { 173 int time_year, time_month, time_day; 174 long offset_day, time_jd; 175 int offset_hms; 176 177 /* split offset into days and day seconds */ 178 offset_day = offset_sec / SECS_PER_DAY; 179 /* Avoid sign issues with % operator */ 180 offset_hms = offset_sec - (offset_day * SECS_PER_DAY); 181 offset_day += off_day; 182 /* Add current time seconds to offset */ 183 offset_hms += tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; 184 /* Adjust day seconds if overflow */ 185 if (offset_hms >= SECS_PER_DAY) { 186 offset_day++; 187 offset_hms -= SECS_PER_DAY; 188 } else if (offset_hms < 0) { 189 offset_day--; 190 offset_hms += SECS_PER_DAY; 191 } 192 193 /* 194 * Convert date of time structure into a Julian day number. 195 */ 196 197 time_year = tm->tm_year + 1900; 198 time_month = tm->tm_mon + 1; 199 time_day = tm->tm_mday; 200 201 time_jd = date_to_julian(time_year, time_month, time_day); 202 203 /* Work out Julian day of new date */ 204 time_jd += offset_day; 205 206 if (time_jd < 0) 207 return 0; 208 209 *pday = time_jd; 210 *psec = offset_hms; 211 212 return 1; 213 } 214 215 /* Convert date to and from julian day 216 * Uses Fliegel & Van Flandern algorithm 217 */ 218 static long 219 date_to_julian(int y, int m, int d) 220 { 221 return (1461 * (y + 4800 + (m - 14) / 12)) / 4 + 222 (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 - 223 (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + 224 d - 32075; 225 } 226 227 static void 228 julian_to_date(long jd, int *y, int *m, int *d) 229 { 230 long L = jd + 68569; 231 long n = (4 * L) / 146097; 232 long i, j; 233 234 L = L - (146097 * n + 3) / 4; 235 i = (4000 * (L + 1)) / 1461001; 236 L = L - (1461 * i) / 4 + 31; 237 j = (80 * L) / 2447; 238 *d = L - (2447 * j) / 80; 239 L = j / 11; 240 *m = j + 2 - (12 * L); 241 *y = 100 * (n - 49) + i + L; 242 } 243