Line data Source code
1 : /**
2 : Copyright (c) 2016-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPTime.h"
25 : #include "SPPlatformUnistd.h"
26 :
27 : inline time_t _time() {
28 : return time(NULL);
29 : }
30 :
31 : namespace STAPPLER_VERSIONIZED stappler {
32 :
33 : static constexpr uint64_t SP_USEC_PER_SEC(1000000);
34 :
35 : using sp_time_t = uint32_t;
36 :
37 13814 : TimeInterval TimeInterval::between(const Time &v1, const Time &v2) {
38 13814 : if (v1 > v2) {
39 10055 : return TimeInterval(v1._value - v2._value);
40 : } else {
41 3759 : return TimeInterval(v2._value - v1._value);
42 : }
43 : }
44 :
45 607585 : uint64_t TimeStorage::toMicroseconds() const {
46 607585 : return _value;
47 : }
48 100 : uint64_t TimeStorage::toMilliseconds() const {
49 100 : return _value / 1000ULL;
50 : }
51 6130 : uint64_t TimeStorage::toSeconds() const {
52 6130 : return _value / 1000000ULL;
53 : }
54 25 : float TimeStorage::toFloatSeconds() const {
55 25 : return _value / 1000000.0f;
56 : }
57 :
58 25 : struct tm TimeStorage::asLocal() const {
59 25 : auto sec = time_t(toSeconds());
60 : struct tm tm;
61 : #if WIN32
62 : localtime_s(&tm, &sec);
63 : #else
64 25 : localtime_r(&sec, &tm);
65 : #endif
66 50 : return tm;
67 : }
68 :
69 25 : struct tm TimeStorage::asGmt() const {
70 25 : auto sec = time_t(toSeconds());
71 : struct tm tm;
72 : #if WIN32
73 : gmtime_s(&tm, &sec);
74 : #else
75 25 : gmtime_r(&sec, &tm);
76 : #endif
77 50 : return tm;
78 : }
79 :
80 50 : void TimeStorage::setMicroseconds(uint64_t v) {
81 50 : _value = v;
82 50 : }
83 50 : void TimeStorage::setMilliseconds(uint64_t v) {
84 50 : _value = v * 1000ULL;
85 50 : }
86 25 : void TimeStorage::setSeconds(time_t v) {
87 25 : _value = v * 1000000ULL;
88 25 : }
89 :
90 4772 : void TimeStorage::clear() {
91 4772 : _value = 0;
92 4772 : }
93 :
94 175 : TimeInterval::TimeInterval(nullptr_t) {
95 175 : _value = 0;
96 175 : }
97 25 : TimeInterval & TimeInterval::operator= (nullptr_t) {
98 25 : _value = 0;
99 25 : return *this;
100 : }
101 :
102 :
103 193961 : Time Time::now() {
104 : #if (WIN32)
105 : static const uint64_t EPOCH = ((uint64_t) 116444736000000000LL);
106 : FILETIME ft;
107 : GetSystemTimeAsFileTime(&ft);
108 : uint64_t tt = ft.dwHighDateTime;
109 : tt <<= 32;
110 : tt |= ft.dwLowDateTime;
111 : tt -= EPOCH;
112 : tt /= 10;
113 : return Time(tt);
114 : #else
115 : struct timeval t0;
116 193961 : gettimeofday(&t0, NULL);
117 193963 : return Time(t0.tv_sec * 1000000LL + t0.tv_usec);
118 : #endif
119 : }
120 :
121 7602 : Time Time::microseconds(uint64_t mksec) {
122 7602 : return Time(mksec);
123 : }
124 98 : Time Time::milliseconds(uint64_t msec) {
125 98 : return Time(msec * 1000ULL);
126 : }
127 375 : Time Time::seconds(time_t sec) {
128 375 : return Time(sec * 1000000ULL);
129 : }
130 25 : Time Time::floatSeconds(float sec) {
131 25 : return Time(uint64_t(sec * 1000000.0f));
132 : }
133 :
134 100 : Time::Time(nullptr_t) : TimeStorage(0) { }
135 25 : Time & Time::operator= (nullptr_t) {
136 25 : _value = 0;
137 25 : return *this;
138 : }
139 :
140 1300 : sp_time_exp_t::sp_time_exp_t() {
141 1300 : tm_usec = 0;
142 1300 : tm_sec = 0;
143 1300 : tm_min = 0;
144 1300 : tm_hour = 0;
145 1300 : tm_mday = 0;
146 1300 : tm_mon = 0;
147 1300 : tm_year = 0;
148 1300 : tm_wday = 0;
149 1300 : tm_yday = 0;
150 1300 : tm_isdst = 0;
151 1300 : tm_gmtoff = 0;
152 1300 : }
153 :
154 5650 : sp_time_exp_t::sp_time_exp_t(int64_t t, int32_t offset, bool use_localtime) {
155 : struct tm tm;
156 5650 : time_t tt = time_t(t / int64_t(SP_USEC_PER_SEC));
157 5650 : tm_usec = t % int64_t(SP_USEC_PER_SEC);
158 :
159 5650 : if (use_localtime) {
160 : #if WIN32
161 : localtime_s(&tm, &tt);
162 : #else
163 325 : localtime_r(&tt, &tm);
164 : #endif
165 325 : tm_gmt_type = gmt_local;
166 : } else {
167 : #if WIN32
168 : gmtime_s(&tm, &tt);
169 : #else
170 5325 : gmtime_r(&tt, &tm);
171 : #endif
172 5325 : tm_gmt_type = gmt_set;
173 : }
174 :
175 5650 : tm_sec = tm.tm_sec;
176 5650 : tm_min = tm.tm_min;
177 5650 : tm_hour = tm.tm_hour;
178 5650 : tm_mday = tm.tm_mday;
179 5650 : tm_mon = tm.tm_mon;
180 5650 : tm_year = tm.tm_year;
181 5650 : tm_wday = tm.tm_wday;
182 5650 : tm_yday = tm.tm_yday;
183 5650 : tm_isdst = tm.tm_isdst;
184 : #ifndef WIN32
185 5650 : tm_gmtoff = tm.tm_gmtoff;
186 : #else
187 : tm_gmtoff = offset;
188 : #endif
189 5650 : }
190 :
191 50 : sp_time_exp_t::sp_time_exp_t(int64_t t, int32_t offs) : sp_time_exp_t(t, offs, false) {
192 50 : tm_gmtoff = offs;
193 50 : }
194 :
195 4725 : sp_time_exp_t::sp_time_exp_t(int64_t t) : sp_time_exp_t(t, 0, false) {
196 4725 : tm_gmtoff = 0;
197 4725 : }
198 :
199 25 : sp_time_exp_t::sp_time_exp_t(int64_t t, bool use_localtime) : sp_time_exp_t(t, 0, use_localtime) { }
200 :
201 25 : sp_time_exp_t::sp_time_exp_t(Time t, int32_t offset, bool use_localtime)
202 25 : : sp_time_exp_t(int64_t(t.toMicroseconds()), offset, use_localtime) { }
203 :
204 : // apr_time_exp_tz
205 25 : sp_time_exp_t::sp_time_exp_t(Time t, int32_t offs)
206 25 : : sp_time_exp_t(int64_t(t.toMicros()), offs) { }
207 :
208 : // apr_time_exp_gmt
209 4550 : sp_time_exp_t::sp_time_exp_t(Time t)
210 4550 : : sp_time_exp_t(int64_t(t.toMicros())) { }
211 :
212 : // apr_time_exp_lt
213 825 : sp_time_exp_t::sp_time_exp_t(Time t, bool use_localtime)
214 825 : : sp_time_exp_t(int64_t(t.toMicros()), 0, use_localtime) { }
215 :
216 50 : Time sp_time_exp_t::get() const {
217 50 : return Time::microseconds(geti());
218 : }
219 :
220 825 : Time sp_time_exp_t::gmt_get() const {
221 825 : return Time::microseconds(gmt_geti());
222 : }
223 :
224 400 : Time sp_time_exp_t::ltz_get() const {
225 400 : return Time::microseconds(ltz_geti());
226 : }
227 :
228 1275 : int64_t sp_time_exp_t::geti() const {
229 1275 : sp_time_t year = tm_year;
230 1275 : int64_t days = 0;
231 : static const int dayoffset[12] = { 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 };
232 :
233 : /* shift new year to 1st March in order to make leap year calc easy */
234 :
235 1275 : if (tm_mon < 2)
236 50 : year--;
237 :
238 : /* Find number of days since 1st March 1900 (in the Gregorian calendar). */
239 :
240 1275 : days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
241 1275 : days += dayoffset[tm_mon] + tm_mday - 1;
242 1275 : days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
243 :
244 1275 : return int64_t( ( ((days * 24 + tm_hour) * 60 + tm_min) * 60 + tm_sec ) * SP_USEC_PER_SEC + tm_usec );
245 : }
246 :
247 825 : int64_t sp_time_exp_t::gmt_geti() const {
248 825 : return int64_t( geti() - tm_gmtoff * SP_USEC_PER_SEC );
249 : }
250 :
251 400 : int64_t sp_time_exp_t::ltz_geti() const {
252 400 : time_t t = time(NULL);
253 400 : struct tm lt = {0};
254 : #if WIN32
255 : TIME_ZONE_INFORMATION tzi;
256 : GetTimeZoneInformation(&tzi);
257 : localtime_s(<, &t);
258 :
259 : long bias = tzi.Bias;
260 : if (lt.tm_isdst) {
261 : if (tzi.DaylightDate.wMonth) {
262 : bias += tzi.DaylightBias;
263 : } else if (tzi.StandardDate.wMonth) {
264 : bias += tzi.StandardBias;
265 : }
266 : } else {
267 : if (tzi.StandardDate.wMonth) {
268 : bias += tzi.StandardBias;
269 : }
270 : }
271 :
272 : return int64_t( geti() + bias * 60 * SP_USEC_PER_SEC );
273 : #else
274 400 : localtime_r(&t, <);
275 400 : return int64_t( geti() - lt.tm_gmtoff * SP_USEC_PER_SEC );
276 : #endif
277 : }
278 :
279 : /*
280 : * Compare a string to a mask
281 : * Mask characters (arbitrary maximum is 256 characters, just in case):
282 : * @ - uppercase letter
283 : * $ - lowercase letter
284 : * & - hex digit
285 : * # - digit
286 : * ~ - digit or space
287 : * * - swallow remaining characters
288 : * <x> - exact match for any other character
289 : */
290 1875 : static bool sp_date_checkmask(StringView data, StringView mask) {
291 27250 : while (!mask.empty() && !data.empty()) {
292 27225 : auto d = data.front();
293 27225 : switch (mask.front()) {
294 0 : case '\0':
295 0 : return (d == '\0');
296 :
297 1175 : case '*':
298 1175 : return true;
299 :
300 1600 : case '@':
301 1600 : if (!chars::isupper(d))
302 25 : return false;
303 1575 : break;
304 3150 : case '$':
305 3150 : if (!chars::islower(d))
306 0 : return false;
307 3150 : break;
308 13700 : case '#':
309 13700 : if (!chars::isdigit(d))
310 75 : return false;
311 13625 : break;
312 0 : case '&':
313 0 : if (!chars::isxdigit(d))
314 0 : return false;
315 0 : break;
316 325 : case '~':
317 325 : if ((d != ' ') && !chars::isdigit(d))
318 0 : return false;
319 325 : break;
320 7275 : default:
321 7275 : if (*mask != d)
322 575 : return false;
323 6700 : break;
324 : }
325 25375 : ++ mask;
326 25375 : ++ data;
327 : }
328 :
329 25 : while (data.empty() && mask.is('*')) {
330 0 : ++ mask;
331 : }
332 :
333 25 : return mask.empty() && data.empty();
334 : }
335 :
336 : /*
337 : * Parses an HTTP date in one of three standard forms:
338 : *
339 : * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
340 : * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
341 : * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
342 : * 2011-04-28T06:34:00+09:00 ; Atom time format
343 : *
344 : * and returns the apr_time_t number of microseconds since 1 Jan 1970 GMT,
345 : * or APR_DATE_BAD if this would be out of range or if the date is invalid.
346 : *
347 : * The restricted HTTP syntax is
348 : *
349 : * HTTP-date = rfc1123-date | rfc850-date | asctime-date
350 : *
351 : * rfc1123-date = wkday "," SP date1 SP time SP "GMT"
352 : * rfc850-date = weekday "," SP date2 SP time SP "GMT"
353 : * asctime-date = wkday SP date3 SP time SP 4DIGIT
354 : *
355 : * date1 = 2DIGIT SP month SP 4DIGIT
356 : * ; day month year (e.g., 02 Jun 1982)
357 : * date2 = 2DIGIT "-" month "-" 2DIGIT
358 : * ; day-month-year (e.g., 02-Jun-82)
359 : * date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
360 : * ; month day (e.g., Jun 2)
361 : *
362 : * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
363 : * ; 00:00:00 - 23:59:59
364 : *
365 : * wkday = "Mon" | "Tue" | "Wed"
366 : * | "Thu" | "Fri" | "Sat" | "Sun"
367 : *
368 : * weekday = "Monday" | "Tuesday" | "Wednesday"
369 : * | "Thursday" | "Friday" | "Saturday" | "Sunday"
370 : *
371 : * month = "Jan" | "Feb" | "Mar" | "Apr"
372 : * | "May" | "Jun" | "Jul" | "Aug"
373 : * | "Sep" | "Oct" | "Nov" | "Dec"
374 : *
375 : * However, for the sake of robustness (and Netscapeness), we ignore the
376 : * weekday and anything after the time field (including the timezone).
377 : *
378 : * This routine is intended to be very fast; 10x faster than using sscanf.
379 : *
380 : * Originally from Andrew Daviel <andrew@vancouver-webpages.com>, 29 Jul 96
381 : * but many changes since then.
382 : *
383 : */
384 :
385 : static const int s_months[12] = {
386 : ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b',
387 : ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r',
388 : ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n',
389 : ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g',
390 : ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't',
391 : ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c'
392 : };
393 :
394 1150 : static inline bool sp_time_exp_read_time(sp_time_exp_t &ds, StringView timstr) {
395 1150 : ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0');
396 1150 : ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0');
397 1150 : ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0');
398 :
399 1150 : if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61)) { return false; }
400 :
401 1150 : return true;
402 : }
403 :
404 400 : static inline bool sp_time_exp_check_mon(sp_time_exp_t &ds) {
405 400 : if (ds.tm_mday <= 0 || ds.tm_mday > 31) { return false; }
406 400 : if (ds.tm_mon >= 12) { return false; }
407 400 : if ((ds.tm_mday == 31) && (ds.tm_mon == 3 || ds.tm_mon == 5 || ds.tm_mon == 8 || ds.tm_mon == 10)) { return false; }
408 :
409 400 : if ((ds.tm_mon == 1)
410 25 : && ((ds.tm_mday > 29)
411 25 : || ((ds.tm_mday == 29)
412 25 : && ((ds.tm_year & 3) || (((ds.tm_year % 100) == 0) && (((ds.tm_year % 400) != 100)))))
413 : ))
414 0 : return false;
415 400 : return true;
416 : }
417 :
418 800 : static inline bool sp_time_exp_read_mon(sp_time_exp_t &ds, StringView monstr) {
419 800 : int mon = 0;
420 800 : if (ds.tm_mday <= 0 || ds.tm_mday > 31) { return false; }
421 :
422 800 : if (monstr.size() >= 3) {
423 800 : auto mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
424 4639 : for (mon = 0; mon < 12; mon++)
425 4639 : if (mint == s_months[mon])
426 800 : break;
427 : } else {
428 0 : mon = ds.tm_mon - 1;
429 : }
430 :
431 800 : if (mon >= 12) { return false; }
432 800 : if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10)) { return false; }
433 :
434 800 : if ((mon == 1)
435 25 : && ((ds.tm_mday > 29)
436 25 : || ((ds.tm_mday == 29)
437 25 : && ((ds.tm_year & 3) || (((ds.tm_year % 100) == 0) && (((ds.tm_year % 400) != 100)))))
438 : ))
439 0 : return false;
440 :
441 800 : ds.tm_mon = mon;
442 800 : return true;
443 : }
444 :
445 875 : static inline bool sp_time_exp_read_gmt(sp_time_exp_t &ds, StringView gmtstr) {
446 875 : ds.tm_gmtoff = 0;
447 : /* Do we have a timezone ? */
448 875 : if (!gmtstr.empty()) {
449 875 : if (gmtstr == "GMT") {
450 475 : ds.tm_gmt_type = sp_time_exp_t::gmt_set;
451 475 : return true;
452 : }
453 400 : int sign = 0;
454 400 : switch (*gmtstr) {
455 50 : case '-': sign = -1; break;
456 50 : case '+': sign = 1; break;
457 300 : case 'Z': ds.tm_gmt_type = sp_time_exp_t::gmt_set; break;
458 0 : default: break;
459 : }
460 :
461 400 : ++ gmtstr;
462 400 : auto off1 = gmtstr.readChars<StringView::CharGroup<CharGroupId::Numbers>>();
463 400 : if (off1.size() == 2 && gmtstr.is(':')) {
464 50 : ++gmtstr;
465 50 : auto off2 = gmtstr.readChars<StringView::CharGroup<CharGroupId::Numbers>>();
466 50 : if (off2.size() == 2) {
467 50 : ds.tm_gmtoff += sign * off1.readInteger().get() * 60 * 60;
468 50 : ds.tm_gmtoff += sign * off2.readInteger().get() * 60;
469 50 : ds.tm_gmt_type = sp_time_exp_t::gmt_set;
470 : }
471 350 : } else if (off1.size() == 4) {
472 0 : auto offset = off1.readInteger().get();
473 0 : ds.tm_gmtoff += sign * (offset / 100) * 60 * 60;
474 0 : ds.tm_gmtoff += sign * (offset % 100) * 60;
475 0 : ds.tm_gmt_type = sp_time_exp_t::gmt_set;
476 : }
477 : } else {
478 0 : ds.tm_gmt_type = sp_time_exp_t::gmt_local;
479 : }
480 400 : return true;
481 : }
482 :
483 : /*
484 : * Parses an HTTP date in one of three standard forms:
485 : *
486 : * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
487 : * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
488 : * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
489 : * 2011-04-28T06:34:00+09:00 ; Atom time format
490 : */
491 :
492 1225 : bool sp_time_exp_t::read(StringView r) {
493 1225 : StringView monstr, timstr, gmtstr;
494 :
495 1225 : r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
496 :
497 1225 : if (r.empty()) {
498 25 : return false;
499 : }
500 :
501 1200 : auto tmp = r;
502 1200 : tmp.skipUntil<StringView::Chars<' '>>();
503 :
504 1200 : tm_gmt_type = gmt_unset;
505 1200 : if (!tmp.is(' ')) {
506 400 : if (sp_date_checkmask(r, "####-##-##T##:##:##*")) {
507 : // 2011-04-28T06:34:00+09:00 ; Atom time format
508 350 : tm_year = ((r[0] - '0') * 10 + (r[1] - '0') - 19) * 100;
509 350 : if (tm_year < 0) { return false; }
510 :
511 350 : tm_year += ((r[2] - '0') * 10) + (r[3] - '0');
512 350 : tm_mon = ((r[5] - '0') * 10) + (r[6] - '0') - 1;
513 350 : tm_mday = ((r[8] - '0') * 10) + (r[9] - '0');
514 :
515 350 : r += 11;
516 350 : if (!sp_time_exp_read_time(*this, r.sub(0, 8))) { return false; }
517 350 : if (!sp_time_exp_check_mon(*this)) { return false; }
518 350 : r += 8;
519 :
520 350 : if (r.is('.')) {
521 275 : double v = r.readDouble().get();
522 275 : tm_usec = 1000000 * v;
523 : }
524 350 : return sp_time_exp_read_gmt(*this, r.empty()?"Z":r);
525 50 : } else if (sp_date_checkmask(r, "####-##-##*")) {
526 : // 2011-04-28 ; Atom date format
527 25 : tm_year = ((r[0] - '0') * 10 + (r[1] - '0') - 19) * 100;
528 25 : if (tm_year < 0)
529 0 : return false;
530 :
531 25 : tm_year += ((r[2] - '0') * 10) + (r[3] - '0');
532 25 : tm_mon = ((r[5] - '0') * 10) + (r[6] - '0') - 1;
533 25 : tm_mday = ((r[8] - '0') * 10) + (r[9] - '0');
534 25 : if (!sp_time_exp_check_mon(*this)) { return false; }
535 25 : r += "####-##-##"_len;
536 25 : return sp_time_exp_read_gmt(*this, r.empty()?"Z":r);
537 25 : } else if (sp_date_checkmask(r, "##.##.####")) {
538 : // 12.03.2010
539 25 : tm_year = ((r[6] - '0') * 10 + (r[7] - '0') - 19) * 100;
540 25 : if (tm_year < 0)
541 0 : return false;
542 :
543 25 : tm_year += ((r[8] - '0') * 10) + (r[9] - '0');
544 25 : tm_mday = ((r[0] - '0') * 10) + (r[1] - '0');
545 25 : tm_mon = ((r[3] - '0') * 10) + (r[4] - '0') - 1;
546 25 : if (!sp_time_exp_check_mon(*this)) { return false; }
547 25 : return sp_time_exp_read_gmt(*this, StringView("Z"));
548 : }
549 0 : return false;
550 : }
551 :
552 800 : if (sp_date_checkmask(r, "@$$ @$$ ~# ##:##:## *")) {
553 : // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
554 325 : auto ydate = r.sub(20); // StringView(r.data() + 20, r.size() - 20);
555 325 : ydate.skipUntil<StringView::CharGroup<CharGroupId::Numbers>>();
556 325 : if (ydate.size() < 4) {
557 0 : return false;
558 : }
559 :
560 325 : tm_year = ((ydate[0] - '0') * 10 + (ydate[1] - '0') - 19) * 100;
561 325 : if (tm_year < 0) { return false; }
562 325 : tm_year += ((ydate[2] - '0') * 10) + (ydate[3] - '0');
563 325 : tm_mday = (r[8] == ' ') ? (r[9] - '0') : (((r[8] - '0') * 10) + (r[9] - '0'));
564 :
565 325 : monstr = r.sub(4, 3);
566 325 : timstr = r.sub(11, 8);
567 :
568 325 : if (!sp_time_exp_read_time(*this, timstr)) { return false; }
569 325 : if (!sp_time_exp_read_mon(*this, monstr)) { return false; }
570 :
571 325 : tm_usec = 0;
572 325 : tm_gmtoff = 0;
573 325 : tm_gmt_type = gmt_local;
574 325 : return true;
575 : }
576 :
577 475 : r.skipUntil<StringView::CharGroup<CharGroupId::Numbers>>();
578 :
579 475 : if (sp_date_checkmask(r, "## @$$ #### ##:##:## *")) {
580 : // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
581 400 : tm_year = ((r[7] - '0') * 10 + (r[8] - '0') - 19) * 100;
582 400 : if (tm_year < 0) { return false; }
583 :
584 400 : tm_year += ((r[9] - '0') * 10) + (r[10] - '0');
585 400 : tm_mday = ((r[0] - '0') * 10) + (r[1] - '0');
586 :
587 400 : monstr = r.sub(3, 3);
588 400 : timstr = r.sub(12, 8);
589 400 : gmtstr = r.sub(21);
590 75 : } else if (sp_date_checkmask(r, "# @$$ #### ##:##:## *")) {
591 : /* RFC 1123 format with one day */
592 25 : tm_year = ((r[6] - '0') * 10 + (r[7] - '0') - 19) * 100;
593 25 : if (tm_year < 0)
594 0 : return false;
595 :
596 25 : tm_year += ((r[8] - '0') * 10) + (r[9] - '0');
597 25 : tm_mday = (r[0] - '0');
598 :
599 25 : monstr = r.sub(2, 3);
600 25 : timstr = r.sub(11, 8);
601 25 : gmtstr = r.sub(20);
602 50 : } else if (sp_date_checkmask(r, "##-@$$-## ##:##:## *")) {
603 : // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
604 50 : tm_year = ((r[7] - '0') * 10) + (r[8] - '0');
605 50 : if (tm_year < 70)
606 0 : tm_year += 100;
607 :
608 50 : tm_mday = ((r[0] - '0') * 10) + (r[1] - '0');
609 :
610 50 : monstr = r.sub(3, 3);
611 50 : timstr = r.sub(10, 8);
612 50 : gmtstr = r.sub(19);
613 : } else {
614 0 : return false;
615 : }
616 :
617 475 : if (!sp_time_exp_read_time(*this, timstr)) { return false; }
618 475 : if (!sp_time_exp_read_mon(*this, monstr)) { return false; }
619 :
620 475 : tm_usec = 0;
621 :
622 475 : if (!gmtstr.empty()) {
623 475 : if (!sp_time_exp_read_gmt(*this, gmtstr)) {
624 0 : return false;
625 : }
626 : } else {
627 0 : tm_gmtoff = 0;
628 : }
629 :
630 475 : return true;
631 : }
632 :
633 : static const char sp_month_snames[12][4] = {
634 : "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
635 : };
636 : static const char sp_day_snames[7][4] = {
637 : "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
638 : };
639 :
640 4725 : size_t sp_time_exp_t::encodeRfc822(char *date_str) const {
641 4725 : auto start = date_str;
642 : const char *s;
643 : int real_year;
644 :
645 : /* example: "Sat, 08 Jan 2000 18:31:41 GMT" */
646 : /* 12345678901234567890123456789 */
647 :
648 4725 : s = &sp_day_snames[tm_wday][0];
649 4725 : *date_str++ = *s++;
650 4725 : *date_str++ = *s++;
651 4725 : *date_str++ = *s++;
652 4725 : *date_str++ = ',';
653 4725 : *date_str++ = ' ';
654 4725 : *date_str++ = tm_mday / 10 + '0';
655 4725 : *date_str++ = tm_mday % 10 + '0';
656 4725 : *date_str++ = ' ';
657 4725 : s = &sp_month_snames[tm_mon][0];
658 4725 : *date_str++ = *s++;
659 4725 : *date_str++ = *s++;
660 4725 : *date_str++ = *s++;
661 4725 : *date_str++ = ' ';
662 4725 : real_year = 1900 + tm_year;
663 : /* This routine isn't y10k ready. */
664 4725 : *date_str++ = real_year / 1000 + '0';
665 4725 : *date_str++ = real_year % 1000 / 100 + '0';
666 4725 : *date_str++ = real_year % 100 / 10 + '0';
667 4725 : *date_str++ = real_year % 10 + '0';
668 4725 : *date_str++ = ' ';
669 4725 : *date_str++ = tm_hour / 10 + '0';
670 4725 : *date_str++ = tm_hour % 10 + '0';
671 4725 : *date_str++ = ':';
672 4725 : *date_str++ = tm_min / 10 + '0';
673 4725 : *date_str++ = tm_min % 10 + '0';
674 4725 : *date_str++ = ':';
675 4725 : *date_str++ = tm_sec / 10 + '0';
676 4725 : *date_str++ = tm_sec % 10 + '0';
677 4725 : *date_str++ = ' ';
678 4725 : *date_str++ = 'G';
679 4725 : *date_str++ = 'M';
680 4725 : *date_str++ = 'T';
681 4725 : *date_str++ = 0;
682 4725 : return date_str - start - 1;
683 : }
684 :
685 275 : size_t sp_time_exp_t::encodeCTime(char *date_str) const {
686 275 : auto start = date_str;
687 : const char *s;
688 : int real_year;
689 :
690 : /* example: "Wed Jun 30 21:49:08 1993" */
691 : /* 123456789012345678901234 */
692 :
693 275 : s = &sp_day_snames[tm_wday][0];
694 275 : *date_str++ = *s++;
695 275 : *date_str++ = *s++;
696 275 : *date_str++ = *s++;
697 275 : *date_str++ = ' ';
698 275 : s = &sp_month_snames[tm_mon][0];
699 275 : *date_str++ = *s++;
700 275 : *date_str++ = *s++;
701 275 : *date_str++ = *s++;
702 275 : *date_str++ = ' ';
703 275 : *date_str++ = tm_mday / 10 + '0';
704 275 : *date_str++ = tm_mday % 10 + '0';
705 275 : *date_str++ = ' ';
706 275 : *date_str++ = tm_hour / 10 + '0';
707 275 : *date_str++ = tm_hour % 10 + '0';
708 275 : *date_str++ = ':';
709 275 : *date_str++ = tm_min / 10 + '0';
710 275 : *date_str++ = tm_min % 10 + '0';
711 275 : *date_str++ = ':';
712 275 : *date_str++ = tm_sec / 10 + '0';
713 275 : *date_str++ = tm_sec % 10 + '0';
714 275 : *date_str++ = ' ';
715 275 : real_year = 1900 + tm_year;
716 275 : *date_str++ = real_year / 1000 + '0';
717 275 : *date_str++ = real_year % 1000 / 100 + '0';
718 275 : *date_str++ = real_year % 100 / 10 + '0';
719 275 : *date_str++ = real_year % 10 + '0';
720 275 : *date_str++ = 0;
721 275 : return date_str - start - 1;
722 : }
723 :
724 550 : size_t sp_time_exp_t::encodeIso8601(char *date_str, size_t precision) const {
725 550 : auto start = date_str;
726 : int real_year;
727 :
728 550 : real_year = 1900 + tm_year;
729 550 : *date_str++ = real_year / 1000 + '0'; // 1
730 550 : *date_str++ = real_year % 1000 / 100 + '0'; // 2
731 550 : *date_str++ = real_year % 100 / 10 + '0'; // 3
732 550 : *date_str++ = real_year % 10 + '0'; // 4
733 550 : *date_str++ = '-'; // 5
734 550 : *date_str++ = (tm_mon + 1) / 10 + '0'; // 6
735 550 : *date_str++ = (tm_mon + 1) % 10 + '0'; // 7
736 550 : *date_str++ = '-'; // 8
737 550 : *date_str++ = tm_mday / 10 + '0'; // 9
738 550 : *date_str++ = tm_mday % 10 + '0'; // 10
739 550 : *date_str++ = 'T'; // 11
740 550 : *date_str++ = tm_hour / 10 + '0'; // 12
741 550 : *date_str++ = tm_hour % 10 + '0'; // 13
742 550 : *date_str++ = ':'; // 14
743 550 : *date_str++ = tm_min / 10 + '0'; // 15
744 550 : *date_str++ = tm_min % 10 + '0'; // 16
745 550 : *date_str++ = ':'; // 17
746 550 : *date_str++ = tm_sec / 10 + '0'; // 18
747 550 : *date_str++ = tm_sec % 10 + '0'; // 19
748 :
749 550 : if (precision > 0 && precision <= 6) {
750 5500 : auto intpow = [] (int val, int p) {
751 5500 : int ret = 1;
752 17875 : while (p > 0) {
753 12375 : ret *= val;
754 12375 : -- p;
755 : }
756 5500 : return ret;
757 : };
758 :
759 550 : *date_str++ = '.';
760 550 : const int desc = SP_USEC_PER_SEC / intpow(10, precision);
761 550 : auto val = int32_t(std::round(tm_usec / double(desc)));
762 3025 : while (precision > 0) {
763 2475 : auto d = val / intpow(10, precision - 1);
764 2475 : *date_str++ = '0' + d;
765 2475 : val = val % intpow(10, precision - 1);
766 2475 : -- precision;
767 : }
768 : }
769 :
770 550 : *date_str++ = 'Z'; // 20
771 550 : *date_str++ = 0;
772 550 : return date_str - start - 1;
773 : }
774 :
775 75 : Time Time::fromCompileTime(const char *date, const char *time) {
776 75 : sp_time_exp_t ds;
777 75 : ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
778 75 : if (ds.tm_year < 0) {
779 0 : return Time();
780 : }
781 :
782 75 : ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
783 75 : ds.tm_mday = ((date[4] != ' ') ? ((date[4] - '0') * 10) : 0) + (date[5] - '0');
784 :
785 75 : int mint = (date[0] << 16) | (date[1] << 8) | date[2];
786 75 : int mon = 0;
787 375 : for (; mon < 12; mon++) {
788 375 : if (mint == s_months[mon])
789 75 : break;
790 : }
791 :
792 75 : if (mon == 12)
793 0 : return Time();
794 :
795 75 : if (ds.tm_mday <= 0 || ds.tm_mday > 31)
796 0 : return Time();
797 :
798 75 : if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
799 0 : return Time();
800 :
801 75 : if ((mon == 1)
802 0 : && ((ds.tm_mday > 29)
803 0 : || ((ds.tm_mday == 29)
804 0 : && ((ds.tm_year & 3) || (((ds.tm_year % 100) == 0) && (((ds.tm_year % 400) != 100))))
805 : )
806 : ))
807 0 : return Time();
808 :
809 75 : ds.tm_mon = mon;
810 75 : ds.tm_hour = ((time[0] - '0') * 10) + (time[1] - '0');
811 75 : ds.tm_min = ((time[3] - '0') * 10) + (time[4] - '0');
812 75 : ds.tm_sec = ((time[6] - '0') * 10) + (time[7] - '0');
813 :
814 75 : if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
815 0 : return Time();
816 :
817 75 : ds.tm_usec = 0;
818 75 : ds.tm_gmtoff = 0;
819 75 : return ds.ltz_get();
820 : }
821 :
822 1225 : Time Time::fromHttp(StringView r) {
823 1225 : sp_time_exp_t ds;
824 1225 : if (!ds.read(r)) {
825 25 : return Time();
826 : }
827 :
828 1200 : switch (ds.tm_gmt_type) {
829 825 : case sp_time_exp_t::gmt_set: return ds.gmt_get();
830 325 : case sp_time_exp_t::gmt_local: return ds.ltz_get();
831 50 : case sp_time_exp_t::gmt_unset: return ds.get();
832 : }
833 :
834 0 : return Time();
835 : }
836 :
837 25 : size_t Time::encodeToFormat(char *buf, size_t bufSize, const char *fmt) const {
838 : struct tm tm;
839 25 : time_t tt = toSeconds();
840 : #if WIN32
841 : gmtime_s(&tm, &tt);
842 : #else
843 25 : gmtime_r(&tt, &tm);
844 : #endif
845 :
846 50 : return strftime(buf, bufSize, fmt, &tm);
847 : }
848 :
849 : }
|