Line data Source code
1 : /*
2 : * Present - Date/Time Library
3 : *
4 : * Implementations of utility functions for dealing with dates and times
5 : *
6 : * Licensed under the MIT License.
7 : * For details, see LICENSE.
8 : */
9 :
10 : #include <assert.h>
11 : #include <math.h>
12 : #include <stddef.h>
13 : #include <string.h>
14 : #include <time.h>
15 : #include <unistd.h>
16 :
17 : #include "present-config.h"
18 :
19 : #ifdef PRESENT_WRAP_STDLIB_CALLS
20 : # include <pthread.h>
21 : #endif
22 :
23 : #include "utils/constants.h"
24 : #include "utils/impl-utils.h"
25 :
26 : #include "utils/time-utils.h"
27 :
28 : #ifdef PRESENT_WRAP_STDLIB_CALLS
29 : static pthread_mutex_t stdlib_call_access;
30 : static int is_initialized = 0;
31 :
32 : #define INITIALIZE() \
33 : do { \
34 : if (!is_initialized) { \
35 : pthread_mutex_init(&stdlib_call_access, NULL); \
36 : is_initialized = 1; \
37 : } \
38 : pthread_mutex_lock(&stdlib_call_access); \
39 : } while (0)
40 :
41 : #define DONE() \
42 : do { \
43 : pthread_mutex_unlock(&stdlib_call_access); \
44 : } while (0)
45 :
46 : #else
47 :
48 : #define INITIALIZE()
49 : #define DONE()
50 :
51 : #endif
52 :
53 : static int is_test_time_set = 0;
54 : static struct PresentNowStruct test_time;
55 :
56 : /**
57 : * Calculate the number of days that have occurred since the very beginning
58 : * (January 1st) of a base year.
59 : *
60 : * Precondition: The base year MUST be divisible by 400.
61 : */
62 : static int_timestamp
63 10272 : days_since_base_year(
64 : const int_year base_year,
65 : int_year year,
66 : int_month month,
67 : int_day day)
68 : {
69 : /** Day of the year that each month starts on (in non-leap years). */
70 : static const int_day_of_year DAY_OF_START_OF_MONTH[13] = {
71 : 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
72 : };
73 :
74 : int_timestamp offset_year;
75 : int_timestamp days_since_base;
76 :
77 : /* Fix irregularities in the month
78 : (we don't really care about irregularities in the day, since our final
79 : result is a number of days) */
80 20567 : while (month <= 0) {
81 23 : month += 12;
82 23 : year -= 1;
83 : }
84 10272 : if (month > 12) {
85 10 : year += (month - 1) / 12;
86 10 : month = (month - 1) % 12 + 1;
87 : }
88 :
89 10272 : offset_year = year - base_year;
90 :
91 : /* Start conversion to number of days since base */
92 10272 : days_since_base = day - 1;
93 :
94 10272 : days_since_base += DAY_OF_START_OF_MONTH[month];
95 10272 : days_since_base += 365 * offset_year;
96 :
97 : /* Every 4 years is a leap year */
98 10272 : days_since_base += offset_year / 4;
99 : /* Except the turn of the century */
100 10272 : days_since_base -= offset_year / 100;
101 : /* Except every 4th century */
102 10272 : days_since_base += offset_year / 400;
103 :
104 : /* If it's still January or February of this year, though, we don't need
105 : to include the extra for this year yet */
106 10272 : if (IS_LEAP_YEAR(year) && month <= 2) {
107 631 : days_since_base--;
108 : }
109 :
110 : /* This fix (i.e. hack) ensures compatibility with timegm behavior */
111 10272 : if (offset_year < 0 && !IS_LEAP_YEAR(year)) {
112 608 : days_since_base--;
113 : }
114 :
115 10272 : return days_since_base;
116 : }
117 :
118 : double
119 10 : present_round(double x)
120 : {
121 : double t;
122 :
123 10 : if (x >= 0.0) {
124 10 : t = floor(x);
125 10 : if (x - t >= 0.5) {
126 2 : t += 1.0;
127 : }
128 10 : return t;
129 : } else {
130 0 : t = floor(-x);
131 0 : if ((-x) - t >= 0.5) {
132 0 : t += 1.0;
133 : }
134 0 : return -t;
135 : }
136 : }
137 :
138 : /** Convert a time_t to a UNIX timestamp. */
139 : int_timestamp
140 33 : time_t_to_unix_timestamp(const time_t timestamp)
141 : {
142 : /* TODO: We're just assuming that time_t is already a UNIX timestamp */
143 33 : return (int_timestamp) timestamp;
144 : }
145 :
146 : /** Convert a UNIX timestamp to a time_t. */
147 : time_t
148 5248 : unix_timestamp_to_time_t(const int_timestamp timestamp_seconds)
149 : {
150 : /* TODO: We're just assuming that time_t is a UNIX timestamp */
151 5248 : return (time_t) timestamp_seconds;
152 : }
153 :
154 : int_timestamp
155 5136 : to_unix_timestamp(
156 : int_year year,
157 : int_month month,
158 : int_day day,
159 : int_hour hour,
160 : int_minute minute,
161 : int_second second)
162 : {
163 : /* 1600 is the easiest base year that's divisible by 400 */
164 : static const int_year BASE_YEAR = 1600;
165 5136 : const int_timestamp EPOCH_DAYS_SINCE_BASE_YEAR =
166 5136 : days_since_base_year(
167 : BASE_YEAR,
168 : UNIX_EPOCH_YEAR,
169 : UNIX_EPOCH_MONTH,
170 : UNIX_EPOCH_DAY);
171 :
172 : int_timestamp days_since_epoch;
173 :
174 5136 : days_since_epoch = days_since_base_year(BASE_YEAR, year, month, day) -
175 : EPOCH_DAYS_SINCE_BASE_YEAR;
176 :
177 10272 : return days_since_epoch * SECONDS_IN_DAY
178 5136 : + hour * SECONDS_IN_HOUR
179 5136 : + minute * SECONDS_IN_MINUTE
180 : + second;
181 : }
182 :
183 : void
184 5235 : time_t_to_struct_tm(const time_t * timep, struct tm * result)
185 : {
186 : struct tm * value;
187 :
188 : INITIALIZE();
189 5235 : value = gmtime(timep);
190 5235 : assert(value != NULL);
191 5235 : memcpy(result, value, sizeof(struct tm));
192 : DONE();
193 5235 : }
194 :
195 : time_t
196 2 : struct_tm_to_time_t_local(struct tm * tm)
197 : {
198 : time_t value;
199 :
200 : INITIALIZE();
201 2 : value = mktime(tm);
202 2 : assert(value != (time_t) -1);
203 : DONE();
204 2 : return value;
205 : }
206 :
207 : void
208 6 : time_t_to_struct_tm_local(const time_t * timep, struct tm * result)
209 : {
210 : struct tm * value;
211 :
212 : INITIALIZE();
213 6 : value = localtime(timep);
214 6 : assert(value != NULL);
215 6 : memcpy(result, value, sizeof(struct tm));
216 : DONE();
217 6 : }
218 :
219 : void
220 20 : clean_struct_tm(struct tm * const tm)
221 : {
222 : time_t time;
223 :
224 120 : time = unix_timestamp_to_time_t(to_unix_timestamp(
225 20 : tm->tm_year + STRUCT_TM_YEAR_OFFSET,
226 20 : tm->tm_mon + STRUCT_TM_MONTH_OFFSET,
227 20 : tm->tm_mday,
228 20 : tm->tm_hour,
229 20 : tm->tm_min,
230 20 : tm->tm_sec));
231 20 : time_t_to_struct_tm(&time, tm);
232 20 : }
233 :
234 : void
235 4 : present_now(struct PresentNowStruct * result)
236 : {
237 4 : assert(result != NULL);
238 : #if defined(_POSIX_TIMERS) && !defined(__STRICT_ANSI__)
239 : struct timespec tp;
240 : #endif
241 :
242 : INITIALIZE();
243 4 : if (is_test_time_set) {
244 3 : *result = test_time;
245 : } else {
246 : #if defined(_POSIX_TIMERS) && !defined(__STRICT_ANSI__)
247 1 : clock_gettime(CLOCK_REALTIME, &tp);
248 1 : result->sec = tp.tv_sec;
249 1 : result->nsec = tp.tv_nsec;
250 : #else
251 : result->sec = time(NULL);
252 : result->nsec = 0;
253 : #endif
254 : }
255 : DONE();
256 4 : }
257 :
258 : void
259 1 : present_set_test_time(struct PresentNowStruct value)
260 : {
261 : INITIALIZE();
262 1 : is_test_time_set = 1;
263 1 : test_time = value;
264 : DONE();
265 1 : }
266 :
267 : void
268 1 : present_reset_test_time()
269 : {
270 : INITIALIZE();
271 1 : is_test_time_set = 0;
272 : DONE();
273 1 : }
274 :
|