LCOV - code coverage report
Current view: top level - core/core/utils - SPTime.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 431 468 92.1 %
Date: 2024-05-12 00:16:13 Functions: 48 48 100.0 %

          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(&lt, &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, &lt);
     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             : }

Generated by: LCOV version 1.14