• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2022, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 // Time conversion to/from POSIX time_t and struct tm, with no support
16 // for time zones other than UTC
17 
18 #include <openssl/time.h>
19 
20 #include <assert.h>
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #include "internal.h"
27 
28 #define SECS_PER_HOUR (60 * 60)
29 #define SECS_PER_DAY (24 * SECS_PER_HOUR)
30 
31 
32 // Is a year/month/day combination valid, in the range from year 0000
33 // to 9999?
is_valid_date(int year,int month,int day)34 static int is_valid_date(int year, int month, int day) {
35   if (day < 1 || month < 1 || year < 0 || year > 9999) {
36     return 0;
37   }
38   switch (month) {
39     case 1:
40     case 3:
41     case 5:
42     case 7:
43     case 8:
44     case 10:
45     case 12:
46       return day > 0 && day <= 31;
47     case 4:
48     case 6:
49     case 9:
50     case 11:
51       return day > 0 && day <= 30;
52     case 2:
53       if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
54         return day > 0 && day <= 29;
55       } else {
56         return day > 0 && day <= 28;
57       }
58     default:
59       return 0;
60   }
61 }
62 
63 // Is a time valid? Leap seconds of 60 are not considered valid, as
64 // the POSIX time in seconds does not include them.
is_valid_time(int hours,int minutes,int seconds)65 static int is_valid_time(int hours, int minutes, int seconds) {
66   if (hours < 0 || minutes < 0 || seconds < 0 || hours > 23 || minutes > 59 ||
67       seconds > 59) {
68     return 0;
69   }
70   return 1;
71 }
72 
73 // Is a int64 time representing a time within our expected range?
is_valid_epoch_time(int64_t time)74 static int is_valid_epoch_time(int64_t time) {
75   // 0000-01-01 00:00:00 UTC to 9999-12-31 23:59:59 UTC
76   return (int64_t)-62167219200 <= time && time <= (int64_t)253402300799;
77 }
78 
79 // Inspired by algorithms presented in
80 // https://howardhinnant.github.io/date_algorithms.html
81 // (Public Domain)
posix_time_from_utc(int year,int month,int day,int hours,int minutes,int seconds,int64_t * out_time)82 static int posix_time_from_utc(int year, int month, int day, int hours,
83                                int minutes, int seconds, int64_t *out_time) {
84   if (!is_valid_date(year, month, day) ||
85       !is_valid_time(hours, minutes, seconds)) {
86     return 0;
87   }
88   if (month <= 2) {
89     year--;  // Start years on Mar 1, so leap days always finish a year.
90   }
91   // At this point year will be in the range -1 and 9999.
92   assert(-1 <= year && year <= 9999);
93   int64_t era = (year >= 0 ? year : year - 399) / 400;
94   int64_t year_of_era = year - era * 400;
95   int64_t day_of_year =
96       (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + day - 1;
97   int64_t day_of_era =
98       year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
99   int64_t posix_days = era * 146097 + day_of_era - 719468;
100   *out_time = posix_days * SECS_PER_DAY + hours * SECS_PER_HOUR + minutes * 60 +
101               seconds;
102   return 1;
103 }
104 
105 // Inspired by algorithms presented in
106 // https://howardhinnant.github.io/date_algorithms.html
107 // (Public Domain)
utc_from_posix_time(int64_t time,int * out_year,int * out_month,int * out_day,int * out_hours,int * out_minutes,int * out_seconds)108 static int utc_from_posix_time(int64_t time, int *out_year, int *out_month,
109                                int *out_day, int *out_hours, int *out_minutes,
110                                int *out_seconds) {
111   if (!is_valid_epoch_time(time)) {
112     return 0;
113   }
114   int64_t days = time / SECS_PER_DAY;
115   int64_t leftover_seconds = time % SECS_PER_DAY;
116   if (leftover_seconds < 0) {
117     days--;
118     leftover_seconds += SECS_PER_DAY;
119   }
120   days += 719468;  // Shift to starting epoch of Mar 1 0000.
121   // At this point, days will be in the range -61 and 3652364.
122   assert(-61 <= days && days <= 3652364);
123   int64_t era = (days > 0 ? days : days - 146096) / 146097;
124   int64_t day_of_era = days - era * 146097;
125   int64_t year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 -
126                          day_of_era / 146096) /
127                         365;
128   *out_year = (int)(year_of_era + era * 400);  // Year starting on Mar 1.
129   int64_t day_of_year =
130       day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
131   int64_t month_of_year = (5 * day_of_year + 2) / 153;
132   *out_month =
133       (int)(month_of_year < 10 ? month_of_year + 3 : month_of_year - 9);
134   if (*out_month <= 2) {
135     (*out_year)++;  // Adjust year back to Jan 1 start of year.
136   }
137   *out_day = (int)(day_of_year - (153 * month_of_year + 2) / 5 + 1);
138   *out_hours = (int)(leftover_seconds / SECS_PER_HOUR);
139   leftover_seconds %= SECS_PER_HOUR;
140   *out_minutes = (int)(leftover_seconds / 60);
141   *out_seconds = (int)(leftover_seconds % 60);
142   return 1;
143 }
144 
OPENSSL_tm_to_posix(const struct tm * tm,int64_t * out)145 int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out) {
146   return posix_time_from_utc(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
147                              tm->tm_hour, tm->tm_min, tm->tm_sec, out);
148 }
149 
OPENSSL_posix_to_tm(int64_t time,struct tm * out_tm)150 int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm) {
151   memset(out_tm, 0, sizeof(struct tm));
152   if (!utc_from_posix_time(time, &out_tm->tm_year, &out_tm->tm_mon,
153                            &out_tm->tm_mday, &out_tm->tm_hour, &out_tm->tm_min,
154                            &out_tm->tm_sec)) {
155     return 0;
156   }
157   out_tm->tm_year -= 1900;
158   out_tm->tm_mon -= 1;
159 
160   return 1;
161 }
162 
OPENSSL_timegm(const struct tm * tm,time_t * out)163 int OPENSSL_timegm(const struct tm *tm, time_t *out) {
164   static_assert(
165       sizeof(time_t) == sizeof(int32_t) || sizeof(time_t) == sizeof(int64_t),
166       "time_t is broken");
167   int64_t posix_time;
168   if (!OPENSSL_tm_to_posix(tm, &posix_time)) {
169     return 0;
170   }
171   if (sizeof(time_t) == sizeof(int32_t) &&
172       (posix_time > INT32_MAX || posix_time < INT32_MIN)) {
173     return 0;
174   }
175   *out = (time_t)posix_time;
176   return 1;
177 }
178 
OPENSSL_gmtime(const time_t * time,struct tm * out_tm)179 struct tm *OPENSSL_gmtime(const time_t *time, struct tm *out_tm) {
180   static_assert(
181       sizeof(time_t) == sizeof(int32_t) || sizeof(time_t) == sizeof(int64_t),
182       "time_t is broken");
183   int64_t posix_time = *time;
184   if (!OPENSSL_posix_to_tm(posix_time, out_tm)) {
185     return NULL;
186   }
187   return out_tm;
188 }
189 
OPENSSL_gmtime_adj(struct tm * tm,int off_day,long offset_sec)190 int OPENSSL_gmtime_adj(struct tm *tm, int off_day, long offset_sec) {
191   int64_t posix_time;
192   if (!posix_time_from_utc(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
193                            tm->tm_hour, tm->tm_min, tm->tm_sec, &posix_time)) {
194     return 0;
195   }
196   if (!utc_from_posix_time(
197           posix_time + (int64_t)off_day * SECS_PER_DAY + offset_sec,
198           &tm->tm_year, &tm->tm_mon, &tm->tm_mday, &tm->tm_hour, &tm->tm_min,
199           &tm->tm_sec)) {
200     return 0;
201   }
202   tm->tm_year -= 1900;
203   tm->tm_mon -= 1;
204 
205   return 1;
206 }
207 
OPENSSL_gmtime_diff(int * out_days,int * out_secs,const struct tm * from,const struct tm * to)208 int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from,
209                         const struct tm *to) {
210   int64_t time_to;
211   if (!posix_time_from_utc(to->tm_year + 1900, to->tm_mon + 1, to->tm_mday,
212                            to->tm_hour, to->tm_min, to->tm_sec, &time_to)) {
213     return 0;
214   }
215   int64_t time_from;
216   if (!posix_time_from_utc(from->tm_year + 1900, from->tm_mon + 1,
217                            from->tm_mday, from->tm_hour, from->tm_min,
218                            from->tm_sec, &time_from)) {
219     return 0;
220   }
221   int64_t timediff = time_to - time_from;
222   int64_t daydiff = timediff / SECS_PER_DAY;
223   timediff %= SECS_PER_DAY;
224   if (daydiff > INT_MAX || daydiff < INT_MIN) {
225     return 0;
226   }
227   *out_secs = (int)timediff;
228   *out_days = (int)daydiff;
229   return 1;
230 }
231