xref: /aosp_15_r20/external/boringssl/src/crypto/asn1/posix_time.c (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
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/posix_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 (INT64_C(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(int64_t year,int64_t month,int64_t day)34 static int is_valid_date(int64_t year, int64_t month, int64_t 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(int64_t hours,int64_t minutes,int64_t seconds)65 static int is_valid_time(int64_t hours, int64_t minutes, int64_t 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 // 0000-01-01 00:00:00 UTC
74 #define MIN_POSIX_TIME INT64_C(-62167219200)
75 // 9999-12-31 23:59:59 UTC
76 #define MAX_POSIX_TIME INT64_C(253402300799)
77 
78 // Is an int64 time within our expected range?
is_valid_posix_time(int64_t time)79 static int is_valid_posix_time(int64_t time) {
80   return MIN_POSIX_TIME <= time && time <= MAX_POSIX_TIME;
81 }
82 
83 // Inspired by algorithms presented in
84 // https://howardhinnant.github.io/date_algorithms.html
85 // (Public Domain)
posix_time_from_utc(int64_t year,int64_t month,int64_t day,int64_t hours,int64_t minutes,int64_t seconds,int64_t * out_time)86 static int posix_time_from_utc(int64_t year, int64_t month, int64_t day,
87                                int64_t hours, int64_t minutes, int64_t seconds,
88                                int64_t *out_time) {
89   if (!is_valid_date(year, month, day) ||
90       !is_valid_time(hours, minutes, seconds)) {
91     return 0;
92   }
93   if (month <= 2) {
94     year--;  // Start years on Mar 1, so leap days always finish a year.
95   }
96   // At this point year will be in the range -1 and 9999.
97   assert(-1 <= year && year <= 9999);
98   int64_t era = (year >= 0 ? year : year - 399) / 400;
99   int64_t year_of_era = year - era * 400;
100   int64_t day_of_year =
101       (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + day - 1;
102   int64_t day_of_era =
103       year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
104   int64_t posix_days = era * 146097 + day_of_era - 719468;
105   *out_time = posix_days * SECS_PER_DAY + hours * SECS_PER_HOUR + minutes * 60 +
106               seconds;
107   return 1;
108 }
109 
110 // Inspired by algorithms presented in
111 // https://howardhinnant.github.io/date_algorithms.html
112 // (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)113 static int utc_from_posix_time(int64_t time, int *out_year, int *out_month,
114                                int *out_day, int *out_hours, int *out_minutes,
115                                int *out_seconds) {
116   if (!is_valid_posix_time(time)) {
117     return 0;
118   }
119   int64_t days = time / SECS_PER_DAY;
120   int64_t leftover_seconds = time % SECS_PER_DAY;
121   if (leftover_seconds < 0) {
122     days--;
123     leftover_seconds += SECS_PER_DAY;
124   }
125   days += 719468;  // Shift to starting epoch of Mar 1 0000.
126   // At this point, days will be in the range -61 and 3652364.
127   assert(-61 <= days && days <= 3652364);
128   int64_t era = (days > 0 ? days : days - 146096) / 146097;
129   int64_t day_of_era = days - era * 146097;
130   int64_t year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 -
131                          day_of_era / 146096) /
132                         365;
133   *out_year = (int)(year_of_era + era * 400);  // Year starting on Mar 1.
134   int64_t day_of_year =
135       day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
136   int64_t month_of_year = (5 * day_of_year + 2) / 153;
137   *out_month =
138       (int)(month_of_year < 10 ? month_of_year + 3 : month_of_year - 9);
139   if (*out_month <= 2) {
140     (*out_year)++;  // Adjust year back to Jan 1 start of year.
141   }
142   *out_day = (int)(day_of_year - (153 * month_of_year + 2) / 5 + 1);
143   *out_hours = (int)(leftover_seconds / SECS_PER_HOUR);
144   leftover_seconds %= SECS_PER_HOUR;
145   *out_minutes = (int)(leftover_seconds / 60);
146   *out_seconds = (int)(leftover_seconds % 60);
147   return 1;
148 }
149 
OPENSSL_tm_to_posix(const struct tm * tm,int64_t * out)150 int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out) {
151   return posix_time_from_utc(tm->tm_year + INT64_C(1900),
152                              tm->tm_mon + INT64_C(1), tm->tm_mday, tm->tm_hour,
153                              tm->tm_min, tm->tm_sec, out);
154 }
155 
OPENSSL_posix_to_tm(int64_t time,struct tm * out_tm)156 int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm) {
157   struct tm tmp_tm = {0};
158   if (!utc_from_posix_time(time, &tmp_tm.tm_year, &tmp_tm.tm_mon,
159                            &tmp_tm.tm_mday, &tmp_tm.tm_hour, &tmp_tm.tm_min,
160                            &tmp_tm.tm_sec)) {
161     return 0;
162   }
163   tmp_tm.tm_year -= 1900;
164   tmp_tm.tm_mon -= 1;
165   *out_tm = tmp_tm;
166 
167   return 1;
168 }
169 
OPENSSL_timegm(const struct tm * tm,time_t * out)170 int OPENSSL_timegm(const struct tm *tm, time_t *out) {
171   static_assert(
172       sizeof(time_t) == sizeof(int32_t) || sizeof(time_t) == sizeof(int64_t),
173       "time_t is broken");
174   int64_t posix_time;
175   if (!OPENSSL_tm_to_posix(tm, &posix_time)) {
176     return 0;
177   }
178   if (sizeof(time_t) == sizeof(int32_t) &&
179       (posix_time > INT32_MAX || posix_time < INT32_MIN)) {
180     return 0;
181   }
182   *out = (time_t)posix_time;
183   return 1;
184 }
185 
OPENSSL_gmtime(const time_t * time,struct tm * out_tm)186 struct tm *OPENSSL_gmtime(const time_t *time, struct tm *out_tm) {
187   static_assert(
188       sizeof(time_t) == sizeof(int32_t) || sizeof(time_t) == sizeof(int64_t),
189       "time_t is broken");
190   int64_t posix_time = *time;
191   if (!OPENSSL_posix_to_tm(posix_time, out_tm)) {
192     return NULL;
193   }
194   return out_tm;
195 }
196 
OPENSSL_gmtime_adj(struct tm * tm,int offset_day,int64_t offset_sec)197 int OPENSSL_gmtime_adj(struct tm *tm, int offset_day, int64_t offset_sec) {
198   int64_t posix_time;
199   if (!OPENSSL_tm_to_posix(tm, &posix_time)) {
200     return 0;
201   }
202   static_assert(INT_MAX <= INT64_MAX / SECS_PER_DAY,
203                 "day offset in seconds cannot overflow");
204   static_assert(MAX_POSIX_TIME <= INT64_MAX - INT_MAX * SECS_PER_DAY,
205                 "addition cannot overflow");
206   static_assert(MIN_POSIX_TIME >= INT64_MIN - INT_MIN * SECS_PER_DAY,
207                 "addition cannot underflow");
208   posix_time += offset_day * SECS_PER_DAY;
209   if (posix_time > 0 && offset_sec > INT64_MAX - posix_time) {
210     return 0;
211   }
212   if (posix_time < 0 && offset_sec < INT64_MIN - posix_time) {
213     return 0;
214   }
215   posix_time += offset_sec;
216 
217   if (!OPENSSL_posix_to_tm(posix_time, tm)) {
218     return 0;
219   }
220 
221   return 1;
222 }
223 
OPENSSL_gmtime_diff(int * out_days,int * out_secs,const struct tm * from,const struct tm * to)224 int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from,
225                         const struct tm *to) {
226   int64_t time_to, time_from;
227   if (!OPENSSL_tm_to_posix(to, &time_to) ||
228       !OPENSSL_tm_to_posix(from, &time_from)) {
229     return 0;
230   }
231   // Times are in range, so these calculations can not overflow.
232   static_assert(SECS_PER_DAY <= INT_MAX, "seconds per day does not fit in int");
233   static_assert((MAX_POSIX_TIME - MIN_POSIX_TIME) / SECS_PER_DAY <= INT_MAX,
234                 "range of valid POSIX times, in days, does not fit in int");
235   int64_t timediff = time_to - time_from;
236   int64_t daydiff = timediff / SECS_PER_DAY;
237   timediff %= SECS_PER_DAY;
238   *out_secs = (int)timediff;
239   *out_days = (int)daydiff;
240   return 1;
241 }
242