1 /***********************************************************************************************************************************
2 Time Management
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <stdio.h>
7 #include <sys/time.h>
8 
9 #include "common/debug.h"
10 #include "common/time.h"
11 
12 /***********************************************************************************************************************************
13 Constants describing number of sub-units in an interval
14 ***********************************************************************************************************************************/
15 #define MSEC_PER_USEC                                               ((TimeMSec)1000)
16 
17 /**********************************************************************************************************************************/
18 TimeMSec
timeMSec(void)19 timeMSec(void)
20 {
21     FUNCTION_TEST_VOID();
22 
23     struct timeval currentTime;
24     gettimeofday(&currentTime, NULL);
25 
26     FUNCTION_TEST_RETURN(((TimeMSec)currentTime.tv_sec * MSEC_PER_SEC) + (TimeMSec)currentTime.tv_usec / MSEC_PER_USEC);
27 }
28 
29 /**********************************************************************************************************************************/
30 void
sleepMSec(TimeMSec sleepMSec)31 sleepMSec(TimeMSec sleepMSec)
32 {
33     FUNCTION_TEST_BEGIN();
34         FUNCTION_TEST_PARAM(UINT64, sleepMSec);
35     FUNCTION_TEST_END();
36 
37     if (sleepMSec > 0)
38     {
39         struct timeval delay;
40         delay.tv_sec = (time_t)(sleepMSec / MSEC_PER_SEC);
41         delay.tv_usec = (suseconds_t)(sleepMSec % MSEC_PER_SEC * 1000);
42         select(0, NULL, NULL, NULL, &delay);
43     }
44 
45     FUNCTION_TEST_RETURN_VOID();
46 }
47 
48 /**********************************************************************************************************************************/
49 void
datePartsValid(int year,int month,int day)50 datePartsValid(int year, int month, int day)
51 {
52     FUNCTION_TEST_BEGIN();
53         FUNCTION_TEST_PARAM(INT, year);
54         FUNCTION_TEST_PARAM(INT, month);
55         FUNCTION_TEST_PARAM(INT, day);
56     FUNCTION_TEST_END();
57 
58     static const int daysPerMonth[2][12] =
59     {
60         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
61         {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
62     };
63 
64     if (!(year >= 1970 && month > 0 && month <= 12 && day > 0 && day <= daysPerMonth[yearIsLeap(year) ? 1 : 0][month - 1]))
65         THROW_FMT(FormatError, "invalid date %04d-%02d-%02d", year, month, day);
66 
67     FUNCTION_TEST_RETURN_VOID();
68 }
69 
70 /**********************************************************************************************************************************/
71 void
timePartsValid(int hour,int minute,int second)72 timePartsValid(int hour, int minute, int second)
73 {
74     FUNCTION_TEST_BEGIN();
75         FUNCTION_TEST_PARAM(INT, hour);
76         FUNCTION_TEST_PARAM(INT, minute);
77         FUNCTION_TEST_PARAM(INT, second);
78     FUNCTION_TEST_END();
79 
80     if (!(hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60))
81         THROW_FMT(FormatError, "invalid time %02d:%02d:%02d", hour, minute, second);
82 
83     FUNCTION_TEST_RETURN_VOID();
84 }
85 
86 /**********************************************************************************************************************************/
87 void
tzPartsValid(int tzHour,int tzMinute)88 tzPartsValid(int tzHour, int tzMinute)
89 {
90     FUNCTION_TEST_BEGIN();
91         FUNCTION_TEST_PARAM(INT, tzHour);                           // signed hour part of timezone
92         FUNCTION_TEST_PARAM(INT, tzMinute);                         // minutes part of timezone
93     FUNCTION_TEST_END();
94 
95     // Valid time zones range from GMT-12 all the way to GMT+14 (i.e. -1200 and +1400 are the min/max).
96     // ??? This is only a sanity check for basic validity of timezone offset of 15 minute intervals until the timezone
97     // database is implemented.
98     if (!(((tzHour > -12 && tzHour < 14) && (tzMinute % 15 == 0)) || (tzHour == -12 && tzMinute == 0) ||
99         (tzHour == 14 && tzMinute == 0)))
100     {
101         THROW_FMT(FormatError, "invalid timezone %02d%02d", tzHour, tzMinute);
102     }
103 
104     FUNCTION_TEST_RETURN_VOID();
105 }
106 
107 /**********************************************************************************************************************************/
108 int
tzOffsetSeconds(int tzHour,int tzMinute)109 tzOffsetSeconds(int tzHour, int tzMinute)
110 {
111     FUNCTION_TEST_BEGIN();
112         FUNCTION_TEST_PARAM(INT, tzHour);                           // signed hour part of timezone (e.g. -7)
113         FUNCTION_TEST_PARAM(INT, tzMinute);                         // minutes part of timezone
114     FUNCTION_TEST_END();
115 
116     // Validate the timezone hour and minute
117     tzPartsValid(tzHour, tzMinute);
118 
119     int sign = 1;
120 
121     // Preserve the sign and convert the hours to a positive number for calculating seconds
122     if (tzHour < 0)
123     {
124         sign = -1;
125         tzHour = sign * tzHour;
126     }
127 
128     FUNCTION_TEST_RETURN(sign * (tzHour * 3600 + tzMinute * 60));
129 }
130 
131 /**********************************************************************************************************************************/
132 bool
yearIsLeap(int year)133 yearIsLeap(int year)
134 {
135     FUNCTION_TEST_BEGIN();
136         FUNCTION_TEST_PARAM(INT, year);
137     FUNCTION_TEST_END();
138 
139     FUNCTION_TEST_RETURN((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
140 }
141 
142 /**********************************************************************************************************************************/
143 int
dayOfYear(int year,int month,int day)144 dayOfYear(int year, int month, int day)
145 {
146     FUNCTION_TEST_BEGIN();
147         FUNCTION_TEST_PARAM(INT, year);
148         FUNCTION_TEST_PARAM(INT, month);
149         FUNCTION_TEST_PARAM(INT, day);
150     FUNCTION_TEST_END();
151 
152     datePartsValid(year, month, day);
153 
154     static const int cumulativeDaysPerMonth[2][12] =
155     {
156         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
157         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
158     };
159 
160     FUNCTION_TEST_RETURN(cumulativeDaysPerMonth[yearIsLeap(year) ? 1 : 0][month - 1] + day);
161 }
162 
163 /**********************************************************************************************************************************/
164 time_t
epochFromParts(int year,int month,int day,int hour,int minute,int second,int tzOffsetSecond)165 epochFromParts(int year, int month, int day, int hour, int minute, int second, int tzOffsetSecond)
166 {
167     FUNCTION_TEST_BEGIN();
168         FUNCTION_TEST_PARAM(INT, year);
169         FUNCTION_TEST_PARAM(INT, month);
170         FUNCTION_TEST_PARAM(INT, day);
171         FUNCTION_TEST_PARAM(INT, hour);
172         FUNCTION_TEST_PARAM(INT, minute);
173         FUNCTION_TEST_PARAM(INT, second);
174         FUNCTION_TEST_PARAM(INT, tzOffsetSecond);
175     FUNCTION_TEST_END();
176 
177     timePartsValid(hour, minute, second);
178 
179     // Return epoch using calculation from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16
180     FUNCTION_TEST_RETURN(
181         -1 * tzOffsetSecond + second + minute * 60 + hour * 3600 +
182         (dayOfYear(year, month, day) - 1) * 86400 + (year - 1900 - 70) * 31536000 +
183         ((year - 1900 - 69) / 4) * 86400 - ((year - 1900 - 1) / 100) * 86400 + ((year - 1900 + 299) / 400) * 86400);
184 }
185