-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathcgdate.c
404 lines (331 loc) · 9.3 KB
/
cgdate.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "glk.h"
#include "cheapglk.h"
#ifdef GLK_MODULE_DATETIME
#ifdef NO_TIMEGM_AVAIL
extern time_t timegm(struct tm *tm);
#endif /* NO_TIMEGM_AVAIL */
#ifdef WIN32
/* Some alterations to make this code work on Windows, in case that's helpful
to you. */
#define mktime(tm) gli_mktime(tm)
extern time_t timegm(struct tm *tm);
extern time_t gli_mktime(struct tm *timeptr);
static struct tm *gmtime_r(const time_t *timer, struct tm *result);
static struct tm *localtime_r(const time_t *timer, struct tm *result);
#endif /* WIN32 */
/* Copy a POSIX tm structure to a glkdate. */
static void gli_date_from_tm(glkdate_t *date, struct tm *tm)
{
date->year = 1900 + tm->tm_year;
date->month = 1 + tm->tm_mon;
date->day = tm->tm_mday;
date->weekday = tm->tm_wday;
date->hour = tm->tm_hour;
date->minute = tm->tm_min;
date->second = tm->tm_sec;
}
/* Copy a glkdate to a POSIX tm structure.
This is used in the "glk_date_to_..." functions, which are supposed
to normalize the glkdate. We're going to rely on the mktime() /
timegm() functions to do that -- except they don't handle microseconds.
So we'll have to do that normalization here, adjust the tm_sec value,
and return the normalized number of microseconds.
*/
static glsi32 gli_date_to_tm(glkdate_t *date, struct tm *tm)
{
glsi32 microsec;
bzero(tm, sizeof(*tm));
tm->tm_year = date->year - 1900;
tm->tm_mon = date->month - 1;
tm->tm_mday = date->day;
tm->tm_wday = date->weekday;
tm->tm_hour = date->hour;
tm->tm_min = date->minute;
tm->tm_sec = date->second;
microsec = date->microsec;
if (microsec >= 1000000) {
tm->tm_sec += (microsec / 1000000);
microsec = microsec % 1000000;
}
else if (microsec < 0) {
microsec = -1 - microsec;
tm->tm_sec -= (1 + microsec / 1000000);
microsec = 999999 - (microsec % 1000000);
}
return microsec;
}
/* Convert a Unix timestamp, along with a microseconds value, to
a glktimeval.
*/
static void gli_timestamp_to_time(time_t timestamp, glsi32 microsec,
glktimeval_t *time)
{
if (sizeof(timestamp) <= 4) {
/* This platform has 32-bit time, but we can't do anything
about that. Hope it's not 2038 yet. */
if (timestamp >= 0)
time->high_sec = 0;
else
time->high_sec = -1;
time->low_sec = timestamp;
}
else {
/* The cast to int64_t shouldn't be necessary, but it
suppresses a pointless warning in the 32-bit case.
(Remember that we won't be executing this line in the
32-bit case.) */
time->high_sec = (((int64_t)timestamp) >> 32) & 0xFFFFFFFF;
time->low_sec = timestamp & 0xFFFFFFFF;
}
time->microsec = microsec;
}
/* Divide a Unix timestamp by a (positive) value. */
static glsi32 gli_simplify_time(time_t timestamp, glui32 factor)
{
/* We want to round towards negative infinity, which takes a little
bit of fussing. */
if (timestamp >= 0) {
return timestamp / (time_t)factor;
}
else {
return -1 - (((time_t)-1 - timestamp) / (time_t)factor);
}
}
void glk_current_time(glktimeval_t *time)
{
struct timespec ts;
if (!timespec_get(&ts, TIME_UTC)) {
gli_timestamp_to_time(0, 0, time);
gli_strict_warning("current_time: timespec_get() failed.");
return;
}
gli_timestamp_to_time(ts.tv_sec, ts.tv_nsec/1000, time);
}
glsi32 glk_current_simple_time(glui32 factor)
{
struct timespec ts;
if (factor == 0) {
gli_strict_warning("current_simple_time: factor cannot be zero.");
return 0;
}
if (!timespec_get(&ts, TIME_UTC)) {
gli_strict_warning("current_simple_time: timespec_get() failed.");
return 0;
}
return gli_simplify_time(ts.tv_sec, factor);
}
void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date)
{
time_t timestamp;
struct tm tm;
timestamp = time->low_sec;
if (sizeof(timestamp) > 4) {
timestamp += ((int64_t)time->high_sec << 32);
}
gmtime_r(×tamp, &tm);
gli_date_from_tm(date, &tm);
date->microsec = time->microsec;
}
void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date)
{
time_t timestamp;
struct tm tm;
timestamp = time->low_sec;
if (sizeof(timestamp) > 4) {
timestamp += ((int64_t)time->high_sec << 32);
}
localtime_r(×tamp, &tm);
gli_date_from_tm(date, &tm);
date->microsec = time->microsec;
}
void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
glkdate_t *date)
{
time_t timestamp = (time_t)time * factor;
struct tm tm;
gmtime_r(×tamp, &tm);
gli_date_from_tm(date, &tm);
date->microsec = 0;
}
void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
glkdate_t *date)
{
time_t timestamp = (time_t)time * factor;
struct tm tm;
localtime_r(×tamp, &tm);
gli_date_from_tm(date, &tm);
date->microsec = 0;
}
void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time)
{
time_t timestamp;
struct tm tm;
glsi32 microsec;
microsec = gli_date_to_tm(date, &tm);
/* The timegm function is not standard POSIX. If it's not available
on your platform, try setting the env var "TZ" to "", calling
mktime(), and then resetting "TZ". */
tm.tm_isdst = 0;
timestamp = timegm(&tm);
gli_timestamp_to_time(timestamp, microsec, time);
}
void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time)
{
time_t timestamp;
struct tm tm;
glsi32 microsec;
microsec = gli_date_to_tm(date, &tm);
tm.tm_isdst = -1;
timestamp = mktime(&tm);
gli_timestamp_to_time(timestamp, microsec, time);
}
glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor)
{
time_t timestamp;
struct tm tm;
if (factor == 0) {
gli_strict_warning("date_to_simple_time_utc: factor cannot be zero.");
return 0;
}
gli_date_to_tm(date, &tm);
/* The timegm function is not standard POSIX. If it's not available
on your platform, try setting the env var "TZ" to "", calling
mktime(), and then resetting "TZ". */
tm.tm_isdst = 0;
timestamp = timegm(&tm);
return gli_simplify_time(timestamp, factor);
}
glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor)
{
time_t timestamp;
struct tm tm;
if (factor == 0) {
gli_strict_warning("date_to_simple_time_local: factor cannot be zero.");
return 0;
}
gli_date_to_tm(date, &tm);
tm.tm_isdst = -1;
timestamp = mktime(&tm);
return gli_simplify_time(timestamp, factor);
}
#ifdef NO_TIMEGM_AVAIL
/* If you have no timegm() function, you can #define NO_TIMEGM_AVAIL to
get this definition. */
time_t timegm(struct tm *tm)
{
time_t res;
char *origtz;
origtz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
res = mktime(tm);
if (origtz)
setenv("TZ", origtz, 1);
else
unsetenv("TZ");
tzset();
return res;
}
#endif /* NO_TIMEGM_AVAIL */
#ifdef WIN32
/* Windows needs wrappers for time functions to handle pre-epoch dates */
/* 31,557,600 seconds per year */
#define OFFSET84 ((glui32) 0x9E009580) /* 1902-1951 => 1986-2035, +84 years */
#define OFFSET28 ((glui32) 0x34AADC80) /* 1952-1969 => 1980-1997, +28 years */
time_t gli_mktime (struct tm * timeptr)
{
glui32 offset = 0;
glui32 adjust = 0;
if (timeptr->tm_year < 70 && timeptr->tm_year > 1)
{
if (timeptr->tm_year < 52)
{
offset = OFFSET84;
adjust = 84;
}
else
{
offset = OFFSET28;
adjust = 28;
}
}
timeptr->tm_year += adjust;
time_t ret = (mktime)(timeptr) - offset;
timeptr->tm_year -= adjust;
return ret;
}
time_t timegm(struct tm *tm)
{
time_t answer;
putenv("TZ=UTC");
tzset();
answer=mktime(tm);
putenv("TZ=");
tzset();
return answer;
}
#define UTC_1901 (-2145916801) /* Dec 31, 1901 at 23:59:59 UTC */
#define UTC_1951 (-568080001) /* Dec 31, 1951 at 23:59:59 UTC */
static struct tm * gmtime_r(const time_t *timer, struct tm *result)
{
time_t eval = *timer;
glui32 adjust = 0;
if (eval < 0 && eval > UTC_1901)
{
if (eval > UTC_1951)
{
eval += OFFSET28;
adjust = 28;
}
else
{
eval += OFFSET84;
adjust = 84;
}
}
struct tm *gm = gmtime(&eval);
if (!gm)
{
time_t zero = 0;
gm = gmtime(&zero);
adjust = 0;
}
*result = *gm;
result->tm_year -= adjust;
return result;
}
static struct tm * localtime_r(const time_t *timer, struct tm *result)
{
time_t eval = *timer;
glui32 adjust = 0;
if (eval < 0 && eval > UTC_1901)
{
if (eval > UTC_1951)
{
eval += OFFSET28;
adjust = 28;
}
else
{
eval += OFFSET84;
adjust = 84;
}
}
struct tm *loc = localtime(&eval);
if (!loc)
{
time_t zero = 0;
loc = localtime(&zero);
adjust = 0;
}
*result = *loc;
result->tm_year -= adjust;
return result;
}
#endif /* WIN32 */
#endif /* GLK_MODULE_DATETIME */