Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / lib / support / TimeUtils.cpp
1 /*
2  *
3  *    Copyright (c) 2020-2021 Project CHIP Authors
4  *    Copyright (c) 2013-2017 Nest Labs, Inc.
5  *    All rights reserved.
6  *
7  *    Licensed under the Apache License, Version 2.0 (the "License");
8  *    you may not use this file except in compliance with the License.
9  *    You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *    Unless required by applicable law or agreed to in writing, software
14  *    distributed under the License is distributed on an "AS IS" BASIS,
15  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *    See the License for the specific language governing permissions and
17  *    limitations under the License.
18  */
19
20 /**
21  *    @file
22  *      Various utility functions for dealing with time and dates.
23  *
24  */
25
26 #ifndef __STDC_LIMIT_MACROS
27 #define __STDC_LIMIT_MACROS
28 #endif
29 #include <limits>
30 #include <stdint.h>
31 #include <type_traits>
32
33 #include <core/CHIPCore.h>
34 #include <support/SafeInt.h>
35
36 #include "TimeUtils.h"
37
38 namespace chip {
39
40 enum
41 {
42     // Number of days during the invariant part of the year (after the leap day).
43     kDaysFromMarch1ToDecember31 = 306,
44
45     // Number of years in a Gregorian "cycle", where a cycle is the 400-year period
46     // over which the Gregorian calendar repeats.
47     kYearsPerCycle = 400,
48
49     // Total number of days within cycle.
50     kDaysPerCycle = 146097,
51
52     // Total number of days between 0000/03/01 and 1970/01/01.
53     kEpochOffsetDays = 719468
54 };
55
56 /* Returns the number of days between January 1st and March 1st for a given year.
57  */
58 static inline uint8_t DaysToMarch1(uint16_t year)
59 {
60     if (IsLeapYear(year))
61         return 60;
62     return 59;
63 }
64
65 /* Converts a March-based month number (0=March, 1=April, etc.) to a March-1st based day of year (0=March 1st, 1=March 2nd, etc.).
66  *
67  * NOTE: This is based on the math described in http://howardhinnant.github.io/date_algorithms.html.
68  */
69 static uint16_t MarchBasedMonthToDayOfYear(uint8_t month)
70 {
71     return static_cast<uint16_t>((153 * month + 2) / 5);
72 }
73
74 /* Converts a March-1st based day of year (0=March 1st, 1=March 2nd, etc.) to a March-based month number (0=March, 1=April, etc.).
75  */
76 static uint8_t MarchBasedDayOfYearToMonth(uint16_t dayOfYear)
77 {
78     // This assumes dayOfYear is not using the full uint16_t range, so the cast
79     // to uint8_t doesn't overflow.
80     return static_cast<uint8_t>((5 * dayOfYear + 2) / 153);
81 }
82
83 /**
84  *  @def IsLeapYear
85  *
86  *  @brief
87  *    Returns true if the given year is a leap year according to the Gregorian calendar.
88  *
89  *  @param year
90  *    Gregorian calendar year.
91  *
92  */
93 bool IsLeapYear(uint16_t year)
94 {
95     return (year % kLeapYearInterval) == 0 && ((year % kYearsPerCentury) != 0 || (year % kYearsPerCycle) == 0);
96 }
97
98 /**
99  *  @def DaysInMonth
100  *
101  *  @brief
102  *    Returns the number of days in the given month/year.
103  *
104  *  @param year
105  *    Gregorian calendar year.
106  *
107  *  @param month
108  *    Month in standard form (1=January ... 12=December).
109  *
110  *  @return
111  *    Number of days in the given month.
112  */
113 uint8_t DaysInMonth(uint16_t year, uint8_t month)
114 {
115     static const uint8_t daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
116
117     if (month == kFebruary && IsLeapYear(year))
118         return 29;
119     if (month >= kJanuary && month <= kDecember)
120         return daysInMonth[month - 1];
121     return 0;
122 }
123
124 /**
125  *  @def FirstWeekdayOfYear
126  *
127  *  @brief
128  *    Returns the day of the week for January 1st of the given year.
129  *
130  *  @param year
131  *    Gregorian calendar year.
132  *
133  *  @return
134  *    The day-of-week (0=Sunday...6=Saturday).
135  */
136 uint8_t FirstWeekdayOfYear(uint16_t year)
137 {
138     // Compute the day of the week for the first day of the given year using Gauss' algorithm.
139     return static_cast<uint8_t>(
140         (1 + 5 * ((year - 1) % kLeapYearInterval) + 4 * ((year - 1) % kYearsPerCentury) + 6 * ((year - 1) % kYearsPerCycle)) %
141         kDaysPerWeek);
142 }
143
144 /**
145  *  @def OrdinalDateToCalendarDate
146  *
147  *  @brief
148  *    Convert an ordinal date (year/day-of-year) to a calendar date.
149  *
150  *  @param year
151  *    Gregorian calendar year.
152  *
153  *  @param dayOfYear
154  *    Ordinal day of year, base 1 (1=January 1st, 2=January 2nd, etc.).
155  *
156  *  @param month
157  *    [OUTPUT] Corresponding month in standard form (1=January ... 12=December).
158  *
159  *  @param dayOfMonth
160  *    [OUTPUT] Corresponding day-of-month in standard form (1=1st, 2=2nd, etc.).
161  *
162  */
163 void OrdinalDateToCalendarDate(uint16_t year, uint16_t dayOfYear, uint8_t & month, uint8_t & dayOfMonth)
164 {
165     uint8_t daysToMarch1 = DaysToMarch1(year);
166
167     // Make dayOfYear base 0.
168     dayOfYear = static_cast<uint16_t>(dayOfYear - 1);
169
170     // Adjust dayOfYear to a March 1st base (i.e. 0 = March 1, 1 = March 2, etc.).  This numbers January
171     // and February at the end of the range, with the benefit that day numbering is identical between
172     // standard and leap years with the exception of the leap day itself.
173     if (dayOfYear < daysToMarch1)
174         dayOfYear = static_cast<uint16_t>(dayOfYear + kDaysFromMarch1ToDecember31);
175     else
176         dayOfYear = static_cast<uint16_t>(dayOfYear - daysToMarch1);
177
178     // Compute a March-based month number (i.e. 0=March...11=February) from the day of year.  This is based
179     // on the logic in http://howardhinnant.github.io/date_algorithms.html.
180     month = MarchBasedDayOfYearToMonth(dayOfYear);
181
182     // Compute the days from March 1st to the start of the corresponding month.
183     uint16_t daysFromMarch1ToStartOfMonth = MarchBasedMonthToDayOfYear(month);
184
185     // Compute the day of month in standard form (1=1st, 2=2nd, etc.).
186     dayOfMonth = static_cast<uint8_t>(dayOfYear - daysFromMarch1ToStartOfMonth + 1);
187
188     // Convert the month number to standard form (1=January...12=December).
189     month = static_cast<uint8_t>(month + (month < 10 ? 3 : -9));
190 }
191
192 /**
193  *  @def CalendarDateToOrdinalDate
194  *
195  *  @brief
196  *    Convert an calendar date to ordinal form (year/day-of-year).
197  *
198  *  @param year
199  *    Gregorian calendar year.
200  *
201  *  @param month
202  *    Month in standard form (1=January ... 12=December).
203  *
204  *  @param dayOfMonth
205  *    Day-of-month in standard form (1=1st, 2=2nd, etc.).
206  *
207  *  @param dayOfYear
208  *    [OUTPUT] Ordinal day of year, base 1 (1=January 1st, 2=January 2nd, etc.).
209  *
210  */
211 void CalendarDateToOrdinalDate(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint16_t & dayOfYear)
212 {
213     // Convert month to a March-based month number (i.e. 0=March, 1=April, ...11=February).
214     month = static_cast<uint8_t>(month + (month > kFebruary ? -3 : 9));
215
216     // Compute the days from March 1st to the start of the corresponding month.
217     dayOfYear = MarchBasedMonthToDayOfYear(month);
218
219     // Adjust dayOfYear to be January-based (0=January 1st, 1=January 2nd...).
220     if (dayOfYear < kDaysFromMarch1ToDecember31)
221         dayOfYear = static_cast<uint16_t>(dayOfYear + DaysToMarch1(year));
222     else
223         dayOfYear = static_cast<uint16_t>(dayOfYear - kDaysFromMarch1ToDecember31);
224
225     // Add in day of month, converting to base 1 in the process.
226     dayOfYear = static_cast<uint16_t>(dayOfYear + dayOfMonth);
227 }
228
229 /**
230  *  @def CalendarDateToDaysSinceEpoch
231  *
232  *  @brief
233  *    Convert a calendar date to the number of days since 1970-01-01.
234  *
235  *  @param year
236  *    Gregorian calendar year in the range 1970 to 28276.
237  *
238  *  @param month
239  *    Month in standard form (1=January ... 12=December).
240  *
241  *  @param dayOfMonth
242  *    Day-of-month in standard form (1=1st, 2=2nd, etc.).
243  *
244  *  @param daysSinceEpoch
245  *    [OUTPUT] Number of days since 1970-01-01.
246  *
247  *  @return
248  *    True if the date was converted successfully.  False if the given year falls outside the
249  *    representable range.
250  *
251  *  @note
252  *    This function makes no attempt to verify the correct range of any arguments other than year.
253  *    Therefore callers must make sure the supplied values are valid prior to calling the function.
254  */
255 bool CalendarDateToDaysSinceEpoch(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint32_t & daysSinceEpoch)
256 {
257     // NOTE: This algorithm is based on the logic described in http://howardhinnant.github.io/date_algorithms.html.
258
259     // Return immediately if the year is out of range.
260     if (year < kEpochYear || year > kMaxYearInDaysSinceEpoch32)
261     {
262         daysSinceEpoch = UINT32_MAX;
263         return false;
264     }
265
266     // Adjust the year and month to be March-based (i.e. 0=March, 1=April, ...11=February).
267     if (month <= kFebruary)
268     {
269         year--;
270         month = static_cast<uint8_t>(month + 9);
271     }
272     else
273         month = static_cast<uint8_t>(month - 3);
274
275     // Compute the days from March 1st to the start of the specified day.
276     uint16_t dayOfYear = static_cast<uint16_t>(MarchBasedMonthToDayOfYear(month) + (dayOfMonth - 1));
277
278     // Compute the 400-year Gregorian "cycle" within which the given year falls.
279     uint16_t cycle = static_cast<uint16_t>(year / kYearsPerCycle);
280
281     // Compute the relative year within the cycle.
282     uint32_t yearOfCycle = year - (cycle * kYearsPerCycle);
283
284     // Compute the relative day within the cycle, accounting for leap-years.
285     uint32_t dayOfCycle =
286         (yearOfCycle * kDaysPerStandardYear) + dayOfYear - (yearOfCycle / kYearsPerCentury) + (yearOfCycle / kLeapYearInterval);
287
288     // Compute the total number of days since the start of the logical calendar (0000-03-01).
289     uint32_t daysSinceCalendarStart = (cycle * kDaysPerCycle) + dayOfCycle;
290
291     // Adjust the days value to be days since 1970-01-01.
292     daysSinceEpoch = daysSinceCalendarStart - kEpochOffsetDays;
293
294     return true;
295 }
296
297 /**
298  *  @def DaysSinceEpochToCalendarDate
299  *
300  *  @brief
301  *    Convert the number of days since 1970-01-01 to a calendar date.
302  *
303  *  @param daysSinceEpoch
304  *    Number of days since 1970-01-01.
305  *
306  *  @param year
307  *    [OUTPUT] Gregorian calendar year.
308  *
309  *  @param month
310  *    [OUTPUT] Month in standard form (1=January ... 12=December).
311  *
312  *  @param dayOfMonth
313  *    [OUTPUT] Day-of-month in standard form (1=1st, 2=2nd, etc.).
314  *
315  *  @return
316  *     True if the conversion was successful.  False if the year would not fit
317  *     in uint16_t.
318  */
319 bool DaysSinceEpochToCalendarDate(uint32_t daysSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth)
320 {
321     // NOTE: This algorithm is based on the logic described in http://howardhinnant.github.io/date_algorithms.html.
322     if (daysSinceEpoch / kDaysPerStandardYear + 1 > std::numeric_limits<std::remove_reference<decltype(year)>::type>::max())
323     {
324         // Our year calculation will likely overflow.
325         return false;
326     }
327
328     // Adjust days value to be relative to 0000-03-01.
329     daysSinceEpoch += kEpochOffsetDays;
330
331     // Compute the 400-year Gregorian cycle in which the given day resides.
332     uint32_t cycle = daysSinceEpoch / kDaysPerCycle;
333
334     // Compute the relative day within the cycle.
335     uint32_t dayOfCycle = daysSinceEpoch - (cycle * kDaysPerCycle);
336
337     // Compute the relative year within the cycle, adjusting for leap-years.
338     uint16_t yearOfCycle =
339         static_cast<uint16_t>((dayOfCycle - dayOfCycle / 1460 + dayOfCycle / 36524 - dayOfCycle / 146096) / kDaysPerStandardYear);
340
341     // Compute the relative day with the year.
342     uint16_t dayOfYear = static_cast<uint16_t>(
343         dayOfCycle - (yearOfCycle * kDaysPerStandardYear + yearOfCycle / kLeapYearInterval - yearOfCycle / kYearsPerCentury));
344
345     // Compute a March-based month number (i.e. 0=March...11=February) from the day of year.
346     month = MarchBasedDayOfYearToMonth(dayOfYear);
347
348     // Compute the days from March 1st to the start of the corresponding month.
349     uint16_t daysFromMarch1ToStartOfMonth = MarchBasedMonthToDayOfYear(month);
350
351     // Compute the day of month in standard form (1=1st, 2=2nd, etc.).
352     dayOfMonth = static_cast<uint8_t>(dayOfYear - daysFromMarch1ToStartOfMonth + 1);
353
354     // Convert the month number to standard form (1=January...12=December).
355     month = static_cast<uint8_t>(month + (month < 10 ? 3 : -9));
356
357     // Compute the year, adjusting for the standard start of year (January).
358     year = static_cast<uint16_t>(yearOfCycle + cycle * kYearsPerCycle);
359     if (month <= kFebruary)
360         year++;
361     return true;
362 }
363
364 /**
365  *  @def AdjustCalendarDate
366  *
367  *  @brief
368  *    Adjust a calendar date by a given number of days (positive or negative).
369  *
370  *  @param year
371  *    [INPUT/OUTPUT] Gregorian calendar year.
372  *
373  *  @param month
374  *    [INPUT/OUTPUT] Month in standard form (1=January ... 12=December).
375  *
376  *  @param dayOfMonth
377  *    [INPUT/OUTPUT] Day-of-month in standard form (1=1st, 2=2nd, etc.).
378  *
379  *  @param relativeDays
380  *    Number of days to add/subtract from given calendar date.
381  *
382  *  @return
383  *    True if the adjustment succeeded.  False if the adjustment would put us
384  *    outside the representable date range.
385  *
386  *  @note
387  *    Given date must be equal to or greater than 1970-01-01.
388  */
389 bool AdjustCalendarDate(uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, int32_t relativeDays)
390 {
391     uint32_t daysSinceEpoch;
392     if (!CalendarDateToDaysSinceEpoch(year, month, dayOfMonth, daysSinceEpoch))
393     {
394         return false;
395     }
396
397     // Make sure we can do our additions without overflowing.
398     int64_t adjustedDays = static_cast<int64_t>(daysSinceEpoch) + relativeDays;
399     if (!CanCastTo<uint32_t>(adjustedDays))
400     {
401         return false;
402     }
403
404     return DaysSinceEpochToCalendarDate(static_cast<uint32_t>(adjustedDays), year, month, dayOfMonth);
405 }
406
407 /**
408  *  @def CalendarTimeToSecondsSinceEpoch
409  *
410  *  @brief
411  *    Convert a calendar date and time to the number of seconds since 1970-01-01 00:00:00 UTC.
412  *
413  *  @details
414  *    This function is roughly equivalent to the POSIX gmtime() function with the exception
415  *    that the output time value is limited to positive values up to 2^32-1.  This limits the
416  *    representable date range to the year 2105.
417  *
418  *  @note
419  *    This function makes no attempt to verify the correct range of any arguments other than year.
420  *    Therefore callers must make sure the supplied values are valid prior to invocation.
421  *
422  *  @param secondsSinceEpoch
423  *    Number of seconds since 1970-01-01 00:00:00 UTC.  Note: this value is compatible with
424  *    *positive* values of the POSIX time_t value up to the year 2105.
425  *
426  *  @param year
427  *    Gregorian calendar year in the range 1970 to 2105.
428  *
429  *  @param month
430  *    Month in standard form (1=January ... 12=December).
431  *
432  *  @param dayOfMonth
433  *    Day-of-month in standard form (1=1st, 2=2nd, etc.).
434  *
435  *  @param hour
436  *    Hour (0-23).
437  *
438  *  @param minute
439  *    Minute (0-59).
440  *
441  *  @param second
442  *    Second (0-59).
443  *
444  *  @return
445  *    True if the date/time was converted successfully.  False if the given year falls outside the
446  *    representable range.
447  */
448 bool CalendarTimeToSecondsSinceEpoch(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute, uint8_t second,
449                                      uint32_t & secondsSinceEpoch)
450 {
451     uint32_t daysSinceEpoch;
452
453     // Return immediately if the year is out of range.
454     if (year < kEpochYear || year > kMaxYearInSecondsSinceEpoch32)
455     {
456         secondsSinceEpoch = UINT32_MAX;
457         return false;
458     }
459
460     CalendarDateToDaysSinceEpoch(year, month, dayOfMonth, daysSinceEpoch);
461
462     secondsSinceEpoch = (daysSinceEpoch * kSecondsPerDay) + (hour * kSecondsPerHour) + (minute * kSecondsPerMinute) + second;
463
464     return true;
465 }
466 /**
467  *  @brief
468  *    Convert the number of seconds since 1970-01-01 00:00:00 UTC to a calendar date and time.
469  *
470  *  @note
471  *    If secondsSinceEpoch is large enough this function will generate bad result. The way it is
472  *    used in this file the generated result should be valid. Specifically, the largest
473  *    possible value of secondsSinceEpoch input is (UINT32_MAX + kChipEpochSecondsSinceUnixEpoch),
474  *    when it is called from ChipEpochToCalendarTime().
475  */
476 static void SecondsSinceEpochToCalendarTime(uint64_t secondsSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth,
477                                             uint8_t & hour, uint8_t & minute, uint8_t & second)
478 {
479     uint32_t daysSinceEpoch = static_cast<uint32_t>(secondsSinceEpoch / kSecondsPerDay);
480     static_assert((static_cast<uint64_t>(UINT32_MAX) + kChipEpochSecondsSinceUnixEpoch) / kSecondsPerDay <=
481                       std::numeric_limits<decltype(daysSinceEpoch)>::max(),
482                   "daysSinceEpoch would overflow");
483     uint32_t timeOfDay = static_cast<uint32_t>(secondsSinceEpoch - (daysSinceEpoch * kSecondsPerDay));
484
485     // Note: This call to DaysSinceEpochToCalendarDate can't fail, because we
486     // can't overflow a uint16_t year with a muximum possible value of the
487     // secondsSinceEpoch input.
488     static_assert((static_cast<uint64_t>(UINT32_MAX) + kChipEpochSecondsSinceUnixEpoch) / (kDaysPerStandardYear * kSecondsPerDay) +
489                           1 <=
490                       std::numeric_limits<std::remove_reference<decltype(year)>::type>::max(),
491                   "What happened to our year or day lengths?");
492     DaysSinceEpochToCalendarDate(daysSinceEpoch, year, month, dayOfMonth);
493
494     hour = static_cast<uint8_t>(timeOfDay / kSecondsPerHour);
495     timeOfDay -= (hour * kSecondsPerHour);
496     minute = static_cast<uint8_t>(timeOfDay / kSecondsPerMinute);
497     timeOfDay -= (minute * kSecondsPerMinute);
498     second = static_cast<uint8_t>(timeOfDay);
499 }
500
501 /**
502  *  @def SecondsSinceEpochToCalendarTime
503  *
504  *  @brief
505  *    Convert the number of seconds since 1970-01-01 00:00:00 UTC to a calendar date and time.
506  *
507  *  @details
508  *    This function is roughly equivalent to the POSIX mktime() function, with the following
509  *    exceptions:
510  *
511  *    - Input time values are limited to positive values up to 2^32-1.  This limits the
512  *    representable date range to the year 2105.
513  *
514  *    - The output time is always UTC (unlike mktime() which outputs time in the process's
515  *    configured timezone).
516  *
517  *  @param secondsSinceEpoch
518  *    Number of seconds since 1970-01-01 00:00:00 UTC.  Note: this value is compatible with
519  *    *positive* values of the POSIX time_t value up to the year 2105.
520  *
521  *  @param year
522  *    [OUTPUT] Gregorian calendar year.
523  *
524  *  @param month
525  *    [OUTPUT] Month in standard form (1=January ... 12=December).
526  *
527  *  @param dayOfMonth
528  *    [OUTPUT] Day-of-month in standard form (1=1st, 2=2nd, etc.).
529  *
530  *  @param hour
531  *    [OUTPUT] Hour (0-23).
532  *
533  *  @param minute
534  *    [OUTPUT] Minute (0-59).
535  *
536  *  @param second
537  *    [OUTPUT] Second (0-59).
538  */
539 void SecondsSinceEpochToCalendarTime(uint32_t secondsSinceEpoch, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth,
540                                      uint8_t & hour, uint8_t & minute, uint8_t & second)
541 {
542     SecondsSinceEpochToCalendarTime(static_cast<uint64_t>(secondsSinceEpoch), year, month, dayOfMonth, hour, minute, second);
543 }
544
545 bool CalendarToChipEpochTime(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute, uint8_t second,
546                              uint32_t & chipEpochTime)
547 {
548     bool res = true;
549     uint32_t daysSinceUnixEpoch;
550
551     VerifyOrExit(year >= kChipEpochBaseYear && year <= kChipEpochMaxYear, res = false);
552
553     CalendarDateToDaysSinceEpoch(year, month, dayOfMonth, daysSinceUnixEpoch);
554
555     chipEpochTime = ((daysSinceUnixEpoch - kChipEpochDaysSinceUnixEpoch) * kSecondsPerDay) + (hour * kSecondsPerHour) +
556         (minute * kSecondsPerMinute) + second;
557
558 exit:
559     return res;
560 }
561
562 void ChipEpochToCalendarTime(uint32_t chipEpochTime, uint16_t & year, uint8_t & month, uint8_t & dayOfMonth, uint8_t & hour,
563                              uint8_t & minute, uint8_t & second)
564 {
565     SecondsSinceEpochToCalendarTime(static_cast<uint64_t>(chipEpochTime) + kChipEpochSecondsSinceUnixEpoch, year, month, dayOfMonth,
566                                     hour, minute, second);
567 }
568
569 bool UnixEpochToChipEpochTime(uint32_t unixEpochTime, uint32_t & chipEpochTime)
570 {
571     bool res = true;
572
573     VerifyOrExit(unixEpochTime >= kChipEpochSecondsSinceUnixEpoch, res = false);
574
575     chipEpochTime = unixEpochTime - kChipEpochSecondsSinceUnixEpoch;
576
577 exit:
578     return res;
579 }
580
581 } // namespace chip