LCOV - code coverage report
Current view: top level - port - cpl_time.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 154 162 95.1 %
Date: 2024-11-21 22:18:42 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     cpl_time.cpp
       4             :  * Project:  CPL - Common Portability Library
       5             :  * Purpose:  Time functions.
       6             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       7             :  *
       8             :  **********************************************************************
       9             :  *
      10             :  * CPLUnixTimeToYMDHMS() is derived from timesub() in localtime.c from
      11             :  * openbsd/freebsd/netbsd.
      12             :  *
      13             :  * CPLYMDHMSToUnixTime() has been implemented by Even Rouault and is in the
      14             :  * public domain.
      15             :  *
      16             :  * c.f.
      17             :  *http://svn.freebsd.org/viewvc/base/stable/7/lib/libc/stdtime/localtime.c?revision=178142&view=markup
      18             :  * localtime.c comes with the following header :
      19             :  *
      20             :  * This file is in the public domain, so clarified as of
      21             :  * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
      22             :  */
      23             : 
      24             : #include "cpl_time.h"
      25             : #include "cpl_string.h"
      26             : 
      27             : #include <cstring>
      28             : #include <ctime>
      29             : 
      30             : #include "cpl_error.h"
      31             : 
      32             : constexpr int SECSPERMIN = 60;
      33             : constexpr int MINSPERHOUR = 60;
      34             : constexpr int HOURSPERDAY = 24;
      35             : constexpr int SECSPERHOUR = SECSPERMIN * MINSPERHOUR;
      36             : constexpr int SECSPERDAY = SECSPERHOUR * HOURSPERDAY;
      37             : constexpr int DAYSPERWEEK = 7;
      38             : constexpr int MONSPERYEAR = 12;
      39             : 
      40             : constexpr int EPOCH_YEAR = 1970;
      41             : constexpr int EPOCH_WDAY = 4;
      42             : constexpr int TM_YEAR_BASE = 1900;
      43             : constexpr int DAYSPERNYEAR = 365;
      44             : constexpr int DAYSPERLYEAR = 366;
      45             : 
      46       73910 : static bool isleap(int y)
      47             : {
      48       73910 :     return ((y % 4) == 0 && (y % 100) != 0) || (y % 400) == 0;
      49             : }
      50             : 
      51      104322 : static int LEAPS_THROUGH_END_OF(int y)
      52             : {
      53      104322 :     return y / 4 - y / 100 + y / 400;
      54             : }
      55             : 
      56             : constexpr int mon_lengths[2][MONSPERYEAR] = {
      57             :     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
      58             :     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
      59             : 
      60             : constexpr int year_lengths[2] = {DAYSPERNYEAR, DAYSPERLYEAR};
      61             : 
      62             : /************************************************************************/
      63             : /*                   CPLUnixTimeToYMDHMS()                              */
      64             : /************************************************************************/
      65             : 
      66             : /** Converts a time value since the Epoch (aka "unix" time) to a broken-down
      67             :  *  UTC time.
      68             :  *
      69             :  * This function is similar to gmtime_r().
      70             :  * This function will always set tm_isdst to 0.
      71             :  *
      72             :  * @param unixTime number of seconds since the Epoch.
      73             :  * @param pRet address of the return structure.
      74             :  *
      75             :  * @return the structure pointed by pRet filled with a broken-down UTC time.
      76             :  */
      77             : 
      78       29391 : struct tm *CPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm *pRet)
      79             : {
      80       29391 :     GIntBig days = unixTime / SECSPERDAY;
      81       29391 :     GIntBig rem = unixTime % SECSPERDAY;
      82             : 
      83       29391 :     constexpr GIntBig TEN_THOUSAND_YEARS =
      84             :         static_cast<GIntBig>(10000) * SECSPERDAY * DAYSPERLYEAR;
      85       29391 :     if (unixTime < -TEN_THOUSAND_YEARS || unixTime > TEN_THOUSAND_YEARS)
      86             :     {
      87           0 :         CPLError(CE_Failure, CPLE_NotSupported,
      88             :                  "Invalid unixTime = " CPL_FRMT_GIB, unixTime);
      89           0 :         memset(pRet, 0, sizeof(*pRet));
      90           0 :         return pRet;
      91             :     }
      92             : 
      93       29411 :     while (rem < 0)
      94             :     {
      95          20 :         rem += SECSPERDAY;
      96          20 :         --days;
      97             :     }
      98             : 
      99       29391 :     pRet->tm_hour = static_cast<int>(rem / SECSPERHOUR);
     100       29391 :     rem = rem % SECSPERHOUR;
     101       29391 :     pRet->tm_min = static_cast<int>(rem / SECSPERMIN);
     102             :     /*
     103             :     ** A positive leap second requires a special
     104             :     ** representation.  This uses "... ??:59:60" et seq.
     105             :     */
     106       29391 :     pRet->tm_sec = static_cast<int>(rem % SECSPERMIN);
     107       29391 :     pRet->tm_wday = static_cast<int>((EPOCH_WDAY + days) % DAYSPERWEEK);
     108       29391 :     if (pRet->tm_wday < 0)
     109          25 :         pRet->tm_wday += DAYSPERWEEK;
     110             : 
     111       29391 :     int y = EPOCH_YEAR;
     112       29391 :     int yleap = 0;
     113       29391 :     int iters = 0;
     114      117448 :     while (iters < 1000 &&
     115       54903 :            (days < 0 ||
     116       54903 :             days >= static_cast<GIntBig>(year_lengths[yleap = isleap(y)])))
     117             :     {
     118       33154 :         int newy = y + static_cast<int>(days / DAYSPERNYEAR);
     119       33154 :         if (days < 0)
     120        7642 :             --newy;
     121       99462 :         days -= static_cast<GIntBig>(newy - y) * DAYSPERNYEAR +
     122       33154 :                 LEAPS_THROUGH_END_OF(newy - 1) - LEAPS_THROUGH_END_OF(y - 1);
     123       33154 :         y = newy;
     124       33154 :         iters++;
     125             :     }
     126       29391 :     if (iters == 1000)
     127             :     {
     128           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     129             :                  "Invalid unixTime = " CPL_FRMT_GIB, unixTime);
     130           0 :         memset(pRet, 0, sizeof(*pRet));
     131           0 :         return pRet;
     132             :     }
     133             : 
     134       29391 :     pRet->tm_year = static_cast<int>(y - TM_YEAR_BASE);
     135       29391 :     pRet->tm_yday = static_cast<int>(days);
     136       29391 :     const int *ip = mon_lengths[yleap];
     137             : 
     138      175052 :     for (pRet->tm_mon = 0; days >= static_cast<GIntBig>(ip[pRet->tm_mon]);
     139      145661 :          ++(pRet->tm_mon))
     140      145661 :         days = days - static_cast<GIntBig>(ip[pRet->tm_mon]);
     141             : 
     142       29391 :     pRet->tm_mday = static_cast<int>((days + 1));
     143       29391 :     pRet->tm_isdst = 0;
     144             : 
     145       29391 :     return pRet;
     146             : }
     147             : 
     148             : /************************************************************************/
     149             : /*                      CPLYMDHMSToUnixTime()                           */
     150             : /************************************************************************/
     151             : 
     152             : /** Converts a broken-down UTC time into time since the Epoch (aka "unix" time).
     153             :  *
     154             :  * This function is similar to mktime(), but the passed structure is not
     155             :  * modified.  This function ignores the tm_wday, tm_yday and tm_isdst fields of
     156             :  * the passed value.  No timezone shift will be applied. This function
     157             :  * returns 0 for the 1/1/1970 00:00:00
     158             :  *
     159             :  * @param brokendowntime broken-downtime UTC time.
     160             :  *
     161             :  * @return a number of seconds since the Epoch encoded as a value of type
     162             :  *         GIntBig, or -1 if the time cannot be represented.
     163             :  */
     164             : 
     165       19010 : GIntBig CPLYMDHMSToUnixTime(const struct tm *brokendowntime)
     166             : {
     167             : 
     168       19010 :     if (brokendowntime->tm_mon < 0 || brokendowntime->tm_mon >= 12)
     169           3 :         return -1;
     170             : 
     171             :     // Number of days of the current month.
     172       19007 :     GIntBig days = brokendowntime->tm_mday - 1;
     173             : 
     174             :     // Add the number of days of the current year.
     175             :     const int *ip = mon_lengths[static_cast<int>(
     176       19007 :         isleap(TM_YEAR_BASE + brokendowntime->tm_year))];
     177      109979 :     for (int mon = 0; mon < brokendowntime->tm_mon; mon++)
     178       90972 :         days += ip[mon];
     179             : 
     180             :     // Add the number of days of the other years.
     181       38014 :     days += (TM_YEAR_BASE + static_cast<GIntBig>(brokendowntime->tm_year) -
     182       19007 :              EPOCH_YEAR) *
     183       19007 :                 DAYSPERNYEAR +
     184       38014 :             LEAPS_THROUGH_END_OF(static_cast<int>(TM_YEAR_BASE) +
     185       19007 :                                  static_cast<int>(brokendowntime->tm_year) -
     186       19007 :                                  static_cast<int>(1)) -
     187       19007 :             LEAPS_THROUGH_END_OF(EPOCH_YEAR - 1);
     188             : 
     189             :     // Now add the secondes, minutes and hours to the number of days
     190             :     // since EPOCH.
     191       19007 :     return brokendowntime->tm_sec + brokendowntime->tm_min * SECSPERMIN +
     192       19007 :            brokendowntime->tm_hour * SECSPERHOUR + days * SECSPERDAY;
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                      OGRParseRFC822DateTime()                        */
     197             : /************************************************************************/
     198             : 
     199             : static const char *const aszWeekDayStr[] = {"Mon", "Tue", "Wed", "Thu",
     200             :                                             "Fri", "Sat", "Sun"};
     201             : 
     202             : static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
     203             :                                           "May", "Jun", "Jul", "Aug",
     204             :                                           "Sep", "Oct", "Nov", "Dec"};
     205             : 
     206             : /** Parse a RFC822 formatted date-time string.
     207             :  *
     208             :  * Such as [Fri,] 28 Dec 2007 05:24[:17] GMT
     209             :  *
     210             :  * @param pszRFC822DateTime formatted string.
     211             :  * @param pnYear pointer to int receiving year (like 1980, 2000, etc...), or
     212             :  * NULL
     213             :  * @param pnMonth pointer to int receiving month (between 1 and 12), or NULL
     214             :  * @param pnDay pointer to int receiving day of month (between 1 and 31), or
     215             :  * NULL
     216             :  * @param pnHour pointer to int receiving hour of day (between 0 and 23), or
     217             :  * NULL
     218             :  * @param pnMinute pointer to int receiving minute (between 0 and 59), or NULL
     219             :  * @param pnSecond pointer to int receiving second (between 0 and 60, or -1 if
     220             :  * unknown), or NULL
     221             :  * @param pnTZFlag pointer to int receiving time zone flag (0=unknown, 100=GMT,
     222             :  *                 101=GMT+15minute, 99=GMT-15minute), or NULL
     223             :  * @param pnWeekDay pointer to int receiving day of week (between 1 and 7, or 0
     224             :  * if invalid/unset), or NULL
     225             :  * @return TRUE if parsing is successful
     226             :  *
     227             :  * @since GDAL 2.3
     228             :  */
     229          75 : int CPLParseRFC822DateTime(const char *pszRFC822DateTime, int *pnYear,
     230             :                            int *pnMonth, int *pnDay, int *pnHour, int *pnMinute,
     231             :                            int *pnSecond, int *pnTZFlag, int *pnWeekDay)
     232             : {
     233             :     // Following
     234             :     // https://www.w3.org/Protocols/rfc822/#z28 :
     235             :     // [Fri,] 28 Dec 2007 05:24[:17] GMT
     236             :     char **papszTokens =
     237          75 :         CSLTokenizeStringComplex(pszRFC822DateTime, " ,:", TRUE, FALSE);
     238          75 :     char **papszVal = papszTokens;
     239          75 :     int nTokens = CSLCount(papszTokens);
     240          75 :     if (nTokens < 5)
     241             :     {
     242          16 :         CSLDestroy(papszTokens);
     243          16 :         return false;
     244             :     }
     245             : 
     246          59 :     if (pnWeekDay)
     247          17 :         *pnWeekDay = 0;
     248             : 
     249          59 :     if (!((*papszVal)[0] >= '0' && (*papszVal)[0] <= '9'))
     250             :     {
     251          36 :         if (pnWeekDay)
     252             :         {
     253          23 :             for (size_t i = 0; i < CPL_ARRAYSIZE(aszWeekDayStr); ++i)
     254             :             {
     255          22 :                 if (EQUAL(*papszVal, aszWeekDayStr[i]))
     256             :                 {
     257           3 :                     *pnWeekDay = static_cast<int>(i + 1);
     258           3 :                     break;
     259             :                 }
     260             :             }
     261             :         }
     262             : 
     263          36 :         ++papszVal;
     264             :     }
     265             : 
     266          59 :     int day = atoi(*papszVal);
     267          59 :     if (day <= 0 || day >= 32)
     268             :     {
     269           2 :         CSLDestroy(papszTokens);
     270           2 :         return false;
     271             :     }
     272          57 :     if (pnDay)
     273          56 :         *pnDay = day;
     274          57 :     ++papszVal;
     275             : 
     276          57 :     int month = 0;
     277         319 :     for (int i = 0; i < 12; ++i)
     278             :     {
     279         318 :         if (EQUAL(*papszVal, aszMonthStr[i]))
     280             :         {
     281          56 :             month = i + 1;
     282          56 :             break;
     283             :         }
     284             :     }
     285          57 :     if (month == 0)
     286             :     {
     287           1 :         CSLDestroy(papszTokens);
     288           1 :         return false;
     289             :     }
     290          56 :     if (pnMonth)
     291          55 :         *pnMonth = month;
     292          56 :     ++papszVal;
     293             : 
     294          56 :     int year = atoi(*papszVal);
     295          56 :     if (year < 100 && year >= 30)
     296           0 :         year += 1900;
     297          56 :     else if (year < 30 && year >= 0)
     298           0 :         year += 2000;
     299          56 :     if (pnYear)
     300          55 :         *pnYear = year;
     301          56 :     ++papszVal;
     302             : 
     303          56 :     int hour = atoi(*papszVal);
     304          56 :     if (hour < 0 || hour >= 24)
     305             :     {
     306           2 :         CSLDestroy(papszTokens);
     307           2 :         return false;
     308             :     }
     309          54 :     if (pnHour)
     310          53 :         *pnHour = hour;
     311          54 :     ++papszVal;
     312             : 
     313          54 :     if (*papszVal == nullptr)
     314             :     {
     315           1 :         CSLDestroy(papszTokens);
     316           1 :         return false;
     317             :     }
     318          53 :     int minute = atoi(*papszVal);
     319          53 :     if (minute < 0 || minute >= 60)
     320             :     {
     321           2 :         CSLDestroy(papszTokens);
     322           2 :         return false;
     323             :     }
     324          51 :     if (pnMinute)
     325          50 :         *pnMinute = minute;
     326          51 :     ++papszVal;
     327             : 
     328          51 :     if (*papszVal != nullptr && (*papszVal)[0] >= '0' && (*papszVal)[0] <= '9')
     329             :     {
     330          49 :         int second = atoi(*papszVal);
     331          49 :         if (second < 0 || second >= 61)
     332             :         {
     333           1 :             CSLDestroy(papszTokens);
     334           1 :             return false;
     335             :         }
     336          48 :         if (pnSecond)
     337          47 :             *pnSecond = second;
     338          48 :         ++papszVal;
     339             :     }
     340           2 :     else if (pnSecond)
     341           2 :         *pnSecond = -1;
     342             : 
     343          50 :     int TZ = 0;
     344          50 :     if (*papszVal == nullptr)
     345             :     {
     346             :     }
     347          29 :     else if (strlen(*papszVal) == 5 &&
     348          25 :              ((*papszVal)[0] == '+' || (*papszVal)[0] == '-'))
     349             :     {
     350          25 :         char szBuf[3] = {(*papszVal)[1], (*papszVal)[2], 0};
     351          25 :         const int TZHour = atoi(szBuf);
     352          25 :         if (TZHour < 0 || TZHour >= 15)
     353             :         {
     354           2 :             CSLDestroy(papszTokens);
     355           2 :             return false;
     356             :         }
     357          23 :         szBuf[0] = (*papszVal)[3];
     358          23 :         szBuf[1] = (*papszVal)[4];
     359          23 :         szBuf[2] = 0;
     360          23 :         const int TZMinute = atoi(szBuf);
     361          23 :         TZ = 100 + (((*papszVal)[0] == '+') ? 1 : -1) *
     362          23 :                        ((TZHour * 60 + TZMinute) / 15);
     363             :     }
     364             :     else
     365             :     {
     366           4 :         const char *aszTZStr[] = {"GMT", "UT",  "Z",   "EST", "EDT", "CST",
     367             :                                   "CDT", "MST", "MDT", "PST", "PDT"};
     368           4 :         const int anTZVal[] = {0, 0, 0, -5, -4, -6, -5, -7, -6, -8, -7};
     369           4 :         TZ = -1;
     370          26 :         for (int i = 0; i < 11; ++i)
     371             :         {
     372          24 :             if (EQUAL(*papszVal, aszTZStr[i]))
     373             :             {
     374           2 :                 TZ = 100 + anTZVal[i] * 4;
     375           2 :                 break;
     376             :             }
     377             :         }
     378           4 :         if (TZ < 0)
     379             :         {
     380           2 :             CSLDestroy(papszTokens);
     381           2 :             return false;
     382             :         }
     383             :     }
     384             : 
     385          46 :     if (pnTZFlag)
     386          25 :         *pnTZFlag = TZ;
     387             : 
     388          46 :     CSLDestroy(papszTokens);
     389          46 :     return true;
     390             : }

Generated by: LCOV version 1.14