ksgi

Форк
0
/
datetime.c 
579 строк · 13.5 Кб
1
/*	$Id$ */
2
/*
3
 * Copyright (c) 2016, 2017, 2020 Kristaps Dzonsons <kristaps@bsd.lv>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include "config.h"
18

19
#include <assert.h> /* debug */
20
#include <inttypes.h>
21
#include <limits.h>
22
#include <stdarg.h>
23
#include <stdint.h>
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#include <time.h>
28

29
#include "kcgi.h"
30
#include "extern.h"
31

32
/*
33
 * We'll never have khttp_epoch2str or khttp_epoch2ustr larger than
34
 * this, even with 64-bit time.
35
 */
36
#define MAX_TIME_STRING	64
37

38
struct tm64 {
39
	int64_t	tm_sec;		/* seconds after the minute [0-60] */
40
	int64_t	tm_min;		/* minutes after the hour [0-59] */
41
	int64_t	tm_hour;	/* hours since midnight [0-23] */
42
	int64_t	tm_mday;	/* day of the month [1-31] */
43
	int64_t	tm_mon;		/* months since January [0-11] */
44
	int64_t	tm_year;	/* years since 1900 */
45
	int64_t	tm_wday;	/* days since Sunday [0-6] */
46
	int64_t	tm_yday;	/* days since January 1 [0-365] */
47
};
48

49
/* 
50
 * The following code is modified from newlib, the relevant parts being
51
 * licensed as follows:
52
 *
53
 * Copyright (c) 1994-2009  Red Hat, Inc. All rights reserved.
54
 *
55
 * This copyrighted material is made available to anyone wishing to use,
56
 * modify, copy, or redistribute it subject to the terms and conditions
57
 * of the BSD License.   This program is distributed in the hope that
58
 * it will be useful, but WITHOUT ANY WARRANTY expressed or implied,
59
 * including the implied warranties of MERCHANTABILITY or FITNESS FOR
60
 * A PARTICULAR PURPOSE.  A copy of this license is available at
61
 * http://www.opensource.org/licenses. Any Red Hat trademarks that are
62
 * incorporated in the source code or documentation are not subject to
63
 * the BSD License and may only be used or replicated with the express
64
 * permission of Red Hat, Inc.
65
 */
66
#define _SEC_IN_MINUTE 60L
67
#define _SEC_IN_HOUR 3600L
68
#define _SEC_IN_DAY 86400L
69

70
static const int DAYS_IN_MONTH[12] =
71
	{31, 28, 31, 30, 31, 30, 
72
	 31, 31, 30, 31, 30, 31};
73

74
#define _DAYS_IN_MONTH(x) \
75
	((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
76

77
static const int _DAYS_BEFORE_MONTH[12] =
78
	{0, 31, 59, 90, 120, 151, 
79
	 181, 212, 243, 273, 304, 334};
80

81
#define _ISLEAP(y) \
82
	(((y) % 4) == 0 && \
83
	 (((y) % 100) != 0 || (((y)+1900) % 400) == 0))
84
#define _DAYS_IN_YEAR(year) \
85
	(_ISLEAP(year) ? 366 : 365)
86

87
/* 
88
 * There are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 *
89
 * 366).
90
 */
91
#define DAYS_PER_ERA		146097L
92

93
/*
94
 * Make sure that all values are sane.
95
 * Return zero on failure, non-zero on success.
96
 */
97
static int 
98
khttp_validate_time(const struct tm64 *tim_p)
99
{
100
	int64_t	 days_in_feb = 28;
101

102
	if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
103
		return 0;
104
	if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
105
		return 0;
106
	if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
107
		return 0;
108
	if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
109
		return 0;
110

111
	/*
112
	 * This magic number is (more or less) the maximum number of
113
	 * years that we can consider in a 64-bit year.
114
	 * Outside of this we'll get overflow or underflow.
115
	 */
116

117
	if (tim_p->tm_year > 292277026596 ||
118
	    tim_p->tm_year < -292277024557)
119
		return 0;
120

121
	if (_DAYS_IN_YEAR(tim_p->tm_year) == 366)
122
		days_in_feb = 29;
123
	if (tim_p->tm_mday <= 0 || 
124
	    tim_p->tm_mday > _DAYS_IN_MONTH(tim_p->tm_mon))
125
		return 0;
126

127
	return 1;
128
}
129

130
/*
131
 * Convert broken-down time to the UNIX epoch.
132
 * Returns zero if the broken-dwon values are not sane, non-zero
133
 * otherwise.
134
 * See khttp_validate_time().
135
 */
136
static int 
137
khttp_mktime(int64_t *res, struct tm64 *tim_p)
138
{
139
	int64_t	tim = 0, days = 0, year, maxyear, era;
140

141
	/* Validate structure. */
142

143
	if (!khttp_validate_time(tim_p))
144
		return 0;
145

146
	/* Compute hours, minutes, seconds. */
147

148
	tim += tim_p->tm_sec + 
149
		(tim_p->tm_min * _SEC_IN_MINUTE) +
150
		(tim_p->tm_hour * _SEC_IN_HOUR);
151

152
	/* Compute days in year. */
153

154
	days += tim_p->tm_mday - 1;
155
	days += _DAYS_BEFORE_MONTH[tim_p->tm_mon];
156

157
	if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR(tim_p->tm_year) == 366)
158
		days++;
159

160
	/* Compute day of the year. */
161

162
	tim_p->tm_yday = days;
163

164
	/* Compute days in other years. */
165
	/* WARNING: THIS IS VERY SLOW. */
166

167
	if ((year = tim_p->tm_year) > 70) {
168
		maxyear = tim_p->tm_year > 400 ? 400 : tim_p->tm_year;
169
		for (year = 70; year < maxyear; year++)
170
			days += _DAYS_IN_YEAR(year);
171
		era = (tim_p->tm_year - year) / 400;
172
		days += era * DAYS_PER_ERA;
173
		year += era * 400;
174
		for ( ; year < tim_p->tm_year; year++)
175
			days += _DAYS_IN_YEAR(year);
176
	} else if (year < 70) {
177
		maxyear = tim_p->tm_year < -400 ? -400 : tim_p->tm_year;
178
		for (year = 69; year > maxyear; year--)
179
			days -= _DAYS_IN_YEAR(year);
180
		era = (tim_p->tm_year - year) / 400;
181
		assert(era <= 0);
182
		days += era * DAYS_PER_ERA;
183
		year += era * 400;
184
		for ( ; year > tim_p->tm_year; year--)
185
			days -= _DAYS_IN_YEAR(year);
186
		days -= _DAYS_IN_YEAR(year);
187
	}
188

189
	/* Compute total seconds. */
190

191
	tim += days * _SEC_IN_DAY;
192

193
	/* Compute day of the week. */
194

195
	if ((tim_p->tm_wday = (days + 4) % 7) < 0)
196
		tim_p->tm_wday += 7;
197

198
	*res = tim;
199
	return 1;
200
}
201

202
/* 
203
 * Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the
204
 * first day of a 400-year long "era", right after additional day of
205
 * leap year.  This adjustment is required only for date calculation, so
206
 * instead of modifying time_t value (which would require 64-bit
207
 * operations to work correctly) it's enough to adjust the calculated
208
 * number of days since epoch.
209
 */
210
#define EPOCH_ADJUSTMENT_DAYS	719468L
211

212
/* Year to which the adjustment was made. */
213
#define ADJUSTED_EPOCH_YEAR	0
214

215
/* 1st March of year 0 is Wednesday. */
216
#define ADJUSTED_EPOCH_WDAY	3
217

218
/* 
219
 * There are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 *
220
 * 366).
221
 */
222
#define DAYS_PER_CENTURY	36524L
223

224
/* There is one leap year every 4 years. */
225
#define DAYS_PER_4_YEARS	(3 * 365 + 366)
226

227
/* Number of days in a non-leap year. */
228
#define DAYS_PER_YEAR		365
229

230
/* Number of days in January. */
231
#define DAYS_IN_JANUARY		31
232

233
/* Number of days in non-leap February. */
234
#define DAYS_IN_FEBRUARY	28
235

236
/* Number of years per era. */
237
#define YEARS_PER_ERA		400
238

239
/* Various constants. */
240
#define SECSPERMIN	60L
241
#define MINSPERHOUR	60L
242
#define HOURSPERDAY	24L
243
#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
244
#define SECSPERDAY	(SECSPERHOUR * HOURSPERDAY)
245
#define YEAR_BASE	1900
246
#define DAYSPERWEEK	7
247
#define MONSPERYEAR	12
248

249
/*
250
 * This form of _ISLEAP doesn't adjust the year.
251
 */
252
#define _ISLEAP2(y) \
253
	(((y) % 4) == 0 && \
254
	 (((y) % 100) != 0 || ((y) % 400) == 0))
255

256
/*
257
 * Convert UNIX epoch to values in "res".
258
 */
259
static void
260
khttp_gmtime_r(int64_t lcltime, struct tm64 *res)
261
{
262
	int64_t		days, rem, era, weekday, year;
263
	uint64_t	erayear, yearday, month, day, eraday;
264

265
	memset(res, 0, sizeof(struct tm64));
266

267
	days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
268
	rem = lcltime % SECSPERDAY;
269

270
	if (rem < 0) {
271
		rem += SECSPERDAY;
272
		--days;
273
	}
274

275
	/* Compute hour, min, and sec. */
276

277
	res->tm_hour = (int64_t)(rem / SECSPERHOUR);
278
	rem %= SECSPERHOUR;
279
	res->tm_min = (int64_t)(rem / SECSPERMIN);
280
	res->tm_sec = (int64_t)(rem % SECSPERMIN);
281

282
	/* Compute day of week. */
283

284
	if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
285
		weekday += DAYSPERWEEK;
286

287
	res->tm_wday = weekday;
288

289
	/* 
290
	 * Compute year, month, day & day of year. 
291
	 * For description of this algorithm see
292
	 * http://howardhinnant.github.io/date_algorithms.html#civil_from_days
293
	 */
294

295
	era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
296

297
	/* [0, 146096] */
298
	eraday = days - era * DAYS_PER_ERA;
299

300
	/* [0, 399] */
301
	erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + 
302
			eraday / DAYS_PER_CENTURY - eraday / 
303
			(DAYS_PER_ERA - 1)) / 365;	
304

305
	/* [0, 365] */
306
	yearday = eraday - (DAYS_PER_YEAR * erayear + 
307
			erayear / 4 - erayear / 100);
308

309
	/* [0, 11] */
310
	month = (5 * yearday + 2) / 153;
311

312
	/* [1, 31] */
313
	day = yearday - (153 * month + 2) / 5 + 1;
314

315
	month += month < 10 ? 2 : -10;
316
	year = ADJUSTED_EPOCH_YEAR + erayear + 
317
		era * YEARS_PER_ERA + (month <= 1);
318

319
	res->tm_yday = 
320
		yearday >= DAYS_PER_YEAR - 
321
			DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
322
		yearday - (DAYS_PER_YEAR - 
323
			DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
324
		yearday + DAYS_IN_JANUARY + 
325
			DAYS_IN_FEBRUARY + _ISLEAP2(erayear);
326
	res->tm_year = year - YEAR_BASE;
327
	res->tm_mon = month;
328
	res->tm_mday = day;
329
}
330

331
char *
332
khttp_epoch2str(int64_t tt, char *buf, size_t sz)
333
{
334
	struct tm64	 tm;
335
	char		 rbuf[MAX_TIME_STRING];
336
	const char	*days[7] = {
337
		"Sun",
338
		"Mon",
339
		"Tue",
340
		"Wed",
341
		"Thu",
342
		"Fri",
343
		"Sat"
344
	};
345
	const char	*months[12] = {
346
		"Jan",
347
		"Feb",
348
		"Mar",
349
		"Apr",
350
		"May",
351
		"Jun",
352
		"Jul",
353
		"Aug",
354
		"Sep",
355
		"Oct",
356
		"Nov",
357
		"Dec"
358
	};
359

360
	if (buf == NULL || sz == 0)
361
		return NULL;
362

363
	khttp_gmtime_r(tt, &tm);
364

365
	if (snprintf(rbuf, sizeof(rbuf), 
366
	    "%s, %.2" PRId64 " %s %.4" PRId64 " "
367
	    "%.2" PRId64 ":%.2" PRId64 ":%.2" PRId64 " GMT",
368
	    days[tm.tm_wday], tm.tm_mday,
369
	    months[tm.tm_mon], tm.tm_year + 1900,
370
	    tm.tm_hour, tm.tm_min, tm.tm_sec) == -1) {
371
		kutil_warn(NULL, NULL, "snprintf");
372
		return NULL;
373
	}
374

375
	strlcpy(buf, rbuf, sz);
376
	return buf;
377
}
378

379
/*
380
 * Deprecated interface simply zeroes the time structure if it's
381
 * negative.  The original implementation did not do this (it set the
382
 * "struct tm" to zero), but the documentation still said otherwise.
383
 * This uses the original documented behaviour.
384
 */
385
char *
386
kutil_epoch2str(int64_t tt, char *buf, size_t sz)
387
{
388

389
	return khttp_epoch2str(tt < 0 ? 0 : tt, buf, sz);
390
}
391

392
char *
393
khttp_epoch2ustr(int64_t tt, char *buf, size_t sz)
394
{
395
	char		 rbuf[MAX_TIME_STRING];
396
	struct tm64	 tm;
397

398
	if (buf == NULL || sz == 0)
399
		return NULL;
400

401
	khttp_gmtime_r(tt, &tm);
402

403
	if (snprintf(rbuf, sizeof(rbuf), 
404
	    "%.4" PRId64 "-%.2" PRId64 "-%.2" PRId64 
405
	    "T%.2" PRId64 ":%.2" PRId64 ":%.2" PRId64 "Z",
406
	    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 
407
	    tm.tm_hour, tm.tm_min, tm.tm_sec) == -1) {
408
		kutil_warn(NULL, NULL, "snprintf");
409
		return NULL;
410
	}
411

412
	strlcpy(buf, rbuf, sz);
413
	return buf;
414
}
415

416
/*
417
 * Deprecated.
418
 * See kutil_epoch2str() for behaviour notes.
419
 */
420
char *
421
kutil_epoch2utcstr(int64_t tt, char *buf, size_t sz)
422
{
423

424
	return khttp_epoch2ustr(tt < 0 ? 0 : tt, buf, sz);
425
}
426

427
void
428
khttp_epoch2datetime(int64_t tt, int64_t *tm_sec, int64_t *tm_min,
429
	int64_t *tm_hour, int64_t *tm_mday, int64_t *tm_mon,
430
	int64_t *tm_year, int64_t *tm_wday, int64_t *tm_yday)
431
{
432
	struct tm64	tm;
433

434
	khttp_gmtime_r(tt, &tm);
435

436
	if (tm_sec != NULL)
437
		*tm_sec = tm.tm_sec;
438
	if (tm_min != NULL)
439
		*tm_min = tm.tm_min;
440
	if (tm_hour != NULL)
441
		*tm_hour = tm.tm_hour;
442
	if (tm_mday != NULL)
443
		*tm_mday = tm.tm_mday;
444
	if (tm_mon != NULL)
445
		*tm_mon = tm.tm_mon + 1;
446
	if (tm_year != NULL)
447
		*tm_year = tm.tm_year + 1900;
448
	if (tm_wday != NULL)
449
		*tm_wday = tm.tm_wday;
450
	if (tm_yday != NULL)
451
		*tm_yday = tm.tm_yday;
452
}
453

454
int
455
khttp_epoch2tms(int64_t tt, int *tm_sec, int *tm_min, 
456
	int *tm_hour, int *tm_mday, int *tm_mon, 
457
	int *tm_year, int *tm_wday, int *tm_yday)
458
{
459
	struct tm64	tm;
460

461
	khttp_gmtime_r(tt, &tm);
462

463
	if (tm.tm_year > INT_MAX || tm.tm_year < -INT_MAX)
464
		return 0;
465
	if (tm_sec != NULL)
466
		*tm_sec = tm.tm_sec;
467
	if (tm_min != NULL)
468
		*tm_min = tm.tm_min;
469
	if (tm_hour != NULL)
470
		*tm_hour = tm.tm_hour;
471
	if (tm_mday != NULL)
472
		*tm_mday = tm.tm_mday;
473
	if (tm_mon != NULL)
474
		*tm_mon = tm.tm_mon;
475
	if (tm_year != NULL)
476
		*tm_year = tm.tm_year;
477
	if (tm_wday != NULL)
478
		*tm_wday = tm.tm_wday;
479
	if (tm_yday != NULL)
480
		*tm_yday = tm.tm_yday;
481

482
	return 1;
483
}
484

485
/*
486
 * Deprecated.
487
 * This has a bad corner case where gmtime() returns NULL and we just
488
 * assume the epoch---this wasn't present in the earlier version of this
489
 * because it was using a hand-rolled gmtime() that didn't fail.  This
490
 * is suitably unlikely that it's ok, as it's deprecated.
491
 */
492
void
493
kutil_epoch2tmvals(int64_t tt, int *tm_sec, int *tm_min, 
494
	int *tm_hour, int *tm_mday, int *tm_mon, 
495
	int *tm_year, int *tm_wday, int *tm_yday)
496
{
497

498
	khttp_epoch2tms(tt < 0 ? 0 : tt, tm_sec, tm_min,
499
		tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday);
500
}
501

502
int
503
khttp_datetime2epoch(int64_t *res, int64_t day, int64_t mon,
504
	int64_t year, int64_t hour, int64_t min, int64_t sec)
505
{
506
	struct tm64	 tm;
507
	int64_t		 val;
508

509
	if (res == NULL)
510
		res = &val;
511

512
	memset(&tm, 0, sizeof(struct tm64));
513
	tm.tm_sec = sec;
514
	tm.tm_min = min;
515
	tm.tm_hour = hour;
516
	tm.tm_mday = day;
517
	tm.tm_mon = mon - 1;
518
	tm.tm_year = year - 1900;
519
	return khttp_mktime(res, &tm);
520
}
521

522
int
523
khttp_date2epoch(int64_t *res, int64_t day, int64_t mon, int64_t year)
524
{
525

526
	return khttp_datetime2epoch(res, day, mon, year, 0, 0, 0);
527
}
528

529
/*
530
 * Deprecated interface.
531
 */
532
int64_t
533
kutil_date2epoch(int64_t day, int64_t mon, int64_t year)
534
{
535
	int64_t	 res;
536

537
	if (!khttp_date2epoch(&res, day, mon, year))
538
		return -1;
539

540
	return res;
541
}
542

543
/*
544
 * Deprecated interface.
545
 */
546
int
547
kutil_datetime_check(int64_t day, int64_t mon, int64_t year,
548
	int64_t hour, int64_t min, int64_t sec)
549
{
550

551
	return khttp_datetime2epoch
552
		(NULL, day, mon, year, hour, min, sec);
553
}
554

555
/*
556
 * Deprecated interface.
557
 */
558
int
559
kutil_date_check(int64_t day, int64_t mon, int64_t year)
560
{
561

562
	return khttp_date2epoch(NULL, day, mon, year);
563
}
564

565
/*
566
 * Deprecated interface.
567
 */
568
int64_t
569
kutil_datetime2epoch(int64_t day, int64_t mon, int64_t year,
570
	int64_t hour, int64_t min, int64_t sec)
571
{
572
	int64_t	 res;
573

574
	if (!khttp_datetime2epoch
575
	    (&res, day, mon, year, hour, min, sec))
576
		return -1;
577

578
	return res;
579
}
580

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.