LCOV - code coverage report
Current view: top level - mutt - date.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 238 243 97.9 %
Date: 2022-03-09 12:17:43 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  * Time and date handling routines
       4             :  *
       5             :  * @authors
       6             :  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
       7             :  *
       8             :  * @copyright
       9             :  * This program is free software: you can redistribute it and/or modify it under
      10             :  * the terms of the GNU General Public License as published by the Free Software
      11             :  * Foundation, either version 2 of the License, or (at your option) any later
      12             :  * version.
      13             :  *
      14             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      15             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      16             :  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
      17             :  * details.
      18             :  *
      19             :  * You should have received a copy of the GNU General Public License along with
      20             :  * this program.  If not, see <http://www.gnu.org/licenses/>.
      21             :  */
      22             : 
      23             : /**
      24             :  * @page mutt_date Time and date handling routines
      25             :  *
      26             :  * Some commonly used time and date functions.
      27             :  */
      28             : 
      29             : #include "config.h"
      30             : #include <stdbool.h>
      31             : #include <stdint.h>
      32             : #include <stdio.h>
      33             : #include <stdlib.h>
      34             : #include <sys/time.h>
      35             : #include <time.h>
      36             : #include "date.h"
      37             : #include "buffer.h"
      38             : #include "logging.h"
      39             : #include "memory.h"
      40             : #include "prex.h"
      41             : #include "regex3.h"
      42             : #include "string2.h"
      43             : 
      44             : /**
      45             :  * Weekdays - Day of the week (abbreviated)
      46             :  */
      47             : static const char *const Weekdays[] = {
      48             :   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
      49             : };
      50             : 
      51             : /**
      52             :  * Months - Months of the year (abbreviated)
      53             :  */
      54             : static const char *const Months[] = {
      55             :   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      56             :   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
      57             : };
      58             : 
      59             : /**
      60             :  * TimeZones - Lookup table of Time Zones
      61             :  *
      62             :  * @note Keep in alphabetical order
      63             :  */
      64             : static const struct Tz TimeZones[] = {
      65             :   // clang-format off
      66             :   { "aat",     1,  0, true  }, /* Atlantic Africa Time */
      67             :   { "adt",     4,  0, false }, /* Arabia DST */
      68             :   { "ast",     3,  0, false }, /* Arabia */
      69             : //{ "ast",     4,  0, true  }, /* Atlantic */
      70             :   { "bst",     1,  0, false }, /* British DST */
      71             :   { "cat",     1,  0, false }, /* Central Africa */
      72             :   { "cdt",     5,  0, true  },
      73             :   { "cest",    2,  0, false }, /* Central Europe DST */
      74             :   { "cet",     1,  0, false }, /* Central Europe */
      75             :   { "cst",     6,  0, true  },
      76             : //{ "cst",     8,  0, false }, /* China */
      77             : //{ "cst",     9, 30, false }, /* Australian Central Standard Time */
      78             :   { "eat",     3,  0, false }, /* East Africa */
      79             :   { "edt",     4,  0, true  },
      80             :   { "eest",    3,  0, false }, /* Eastern Europe DST */
      81             :   { "eet",     2,  0, false }, /* Eastern Europe */
      82             :   { "egst",    0,  0, false }, /* Eastern Greenland DST */
      83             :   { "egt",     1,  0, true  }, /* Eastern Greenland */
      84             :   { "est",     5,  0, true  },
      85             :   { "gmt",     0,  0, false },
      86             :   { "gst",     4,  0, false }, /* Presian Gulf */
      87             :   { "hkt",     8,  0, false }, /* Hong Kong */
      88             :   { "ict",     7,  0, false }, /* Indochina */
      89             :   { "idt",     3,  0, false }, /* Israel DST */
      90             :   { "ist",     2,  0, false }, /* Israel */
      91             : //{ "ist",     5, 30, false }, /* India */
      92             :   { "jst",     9,  0, false }, /* Japan */
      93             :   { "kst",     9,  0, false }, /* Korea */
      94             :   { "mdt",     6,  0, true  },
      95             :   { "met",     1,  0, false }, /* This is now officially CET */
      96             :   { "met dst", 2,  0, false }, /* MET in Daylight Saving Time */
      97             :   { "msd",     4,  0, false }, /* Moscow DST */
      98             :   { "msk",     3,  0, false }, /* Moscow */
      99             :   { "mst",     7,  0, true  },
     100             :   { "nzdt",   13,  0, false }, /* New Zealand DST */
     101             :   { "nzst",   12,  0, false }, /* New Zealand */
     102             :   { "pdt",     7,  0, true  },
     103             :   { "pst",     8,  0, true  },
     104             :   { "sat",     2,  0, false }, /* South Africa */
     105             :   { "smt",     4,  0, false }, /* Seychelles */
     106             :   { "sst",    11,  0, true  }, /* Samoa */
     107             : //{ "sst",     8,  0, false }, /* Singapore */
     108             :   { "utc",     0,  0, false },
     109             :   { "wat",     0,  0, false }, /* West Africa */
     110             :   { "west",    1,  0, false }, /* Western Europe DST */
     111             :   { "wet",     0,  0, false }, /* Western Europe */
     112             :   { "wgst",    2,  0, true  }, /* Western Greenland DST */
     113             :   { "wgt",     3,  0, true  }, /* Western Greenland */
     114             :   { "wst",     8,  0, false }, /* Western Australia */
     115             :   // clang-format on
     116             : };
     117             : 
     118             : /**
     119             :  * compute_tz - Calculate the number of seconds east of UTC
     120             :  * @param g   Local time
     121             :  * @param utc UTC time
     122             :  * @retval num Seconds east of UTC
     123             :  *
     124             :  * returns the seconds east of UTC given 'g' and its corresponding gmtime()
     125             :  * representation
     126             :  */
     127          12 : static time_t compute_tz(time_t g, struct tm *utc)
     128             : {
     129          12 :   struct tm lt = mutt_date_localtime(g);
     130             : 
     131          12 :   time_t t = (((lt.tm_hour - utc->tm_hour) * 60) + (lt.tm_min - utc->tm_min)) * 60;
     132             : 
     133          12 :   int yday = (lt.tm_yday - utc->tm_yday);
     134          12 :   if (yday != 0)
     135             :   {
     136             :     /* This code is optimized to negative timezones (West of Greenwich) */
     137           0 :     if ((yday == -1) || /* UTC passed midnight before localtime */
     138             :         (yday > 1))     /* UTC passed new year before localtime */
     139             :     {
     140           0 :       t -= (24 * 60 * 60);
     141             :     }
     142             :     else
     143             :     {
     144           0 :       t += (24 * 60 * 60);
     145             :     }
     146             :   }
     147             : 
     148          12 :   return t;
     149             : }
     150             : 
     151             : /**
     152             :  * add_tz_offset - Compute and add a timezone offset to an UTC time
     153             :  * @param t UTC time
     154             :  * @param w True if west of UTC, false if east
     155             :  * @param h Number of hours in the timezone
     156             :  * @param m Number of minutes in the timezone
     157             :  * @retval num Timezone offset in seconds
     158             :  */
     159          70 : static time_t add_tz_offset(time_t t, bool w, time_t h, time_t m)
     160             : {
     161          70 :   if ((t != TIME_T_MAX) && (t != TIME_T_MIN))
     162          70 :     return t + (w ? 1 : -1) * (((time_t) h * 3600) + ((time_t) m * 60));
     163             :   else
     164           0 :     return t;
     165             : }
     166             : 
     167             : /**
     168             :  * find_tz - Look up a timezone
     169             :  * @param s Timezone to lookup
     170             :  * @param len Length of the s string
     171             :  * @retval ptr Pointer to the Tz struct
     172             :  * @retval NULL Not found
     173             :  */
     174          16 : static const struct Tz *find_tz(const char *s, size_t len)
     175             : {
     176         416 :   for (size_t i = 0; i < mutt_array_size(TimeZones); i++)
     177             :   {
     178         414 :     if (mutt_istrn_equal(TimeZones[i].tzname, s, len))
     179          14 :       return &TimeZones[i];
     180             :   }
     181           2 :   return NULL;
     182             : }
     183             : 
     184             : /**
     185             :  * is_leap_year_feb - Is a given February in a leap year
     186             :  * @param tm Date to be tested
     187             :  * @retval true It's a leap year
     188             :  */
     189          30 : static int is_leap_year_feb(struct tm *tm)
     190             : {
     191          30 :   if (tm->tm_mon != 1)
     192          28 :     return 0;
     193             : 
     194           2 :   int y = tm->tm_year + 1900;
     195           2 :   return ((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0));
     196             : }
     197             : 
     198             : /**
     199             :  * mutt_date_local_tz - Calculate the local timezone in seconds east of UTC
     200             :  * @param t Time to examine
     201             :  * @retval num Seconds east of UTC
     202             :  *
     203             :  * Returns the local timezone in seconds east of UTC for the time t,
     204             :  * or for the current time if t is zero.
     205             :  */
     206          14 : time_t mutt_date_local_tz(time_t t)
     207             : {
     208             :   /* Check we haven't overflowed the time (on 32-bit arches) */
     209          14 :   if ((t == TIME_T_MAX) || (t == TIME_T_MIN))
     210           4 :     return 0;
     211             : 
     212          10 :   if (t == 0)
     213           2 :     t = mutt_date_epoch();
     214             : 
     215          10 :   struct tm tm = mutt_date_gmtime(t);
     216          10 :   return compute_tz(t, &tm);
     217             : }
     218             : 
     219             : /**
     220             :  * mutt_date_make_time - Convert `struct tm` to `time_t`
     221             :  * @param t     Time to convert
     222             :  * @param local Should the local timezone be considered
     223             :  * @retval num        Time in Unix format
     224             :  * @retval TIME_T_MIN Error
     225             :  *
     226             :  * Convert a struct tm to time_t, but don't take the local timezone into
     227             :  * account unless "local" is nonzero
     228             :  */
     229         114 : time_t mutt_date_make_time(struct tm *t, bool local)
     230             : {
     231         114 :   if (!t)
     232           2 :     return TIME_T_MIN;
     233             : 
     234             :   static const int AccumDaysPerMonth[mutt_array_size(Months)] = {
     235             :     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
     236             :   };
     237             : 
     238             :   /* Prevent an integer overflow, with some arbitrary limits. */
     239         112 :   if (t->tm_year > 10000)
     240           2 :     return TIME_T_MAX;
     241         110 :   if (t->tm_year < -10000)
     242           2 :     return TIME_T_MIN;
     243             : 
     244         108 :   if ((t->tm_mday < 1) || (t->tm_mday > 31))
     245           4 :     return TIME_T_MIN;
     246         104 :   if ((t->tm_hour < 0) || (t->tm_hour > 23) || (t->tm_min < 0) ||
     247          98 :       (t->tm_min > 59) || (t->tm_sec < 0) || (t->tm_sec > 60))
     248             :   {
     249          12 :     return TIME_T_MIN;
     250             :   }
     251          92 :   if (t->tm_year > 9999)
     252           0 :     return TIME_T_MAX;
     253             : 
     254             :   /* Compute the number of days since January 1 in the same year */
     255          92 :   time_t g = AccumDaysPerMonth[t->tm_mon % mutt_array_size(Months)];
     256             : 
     257             :   /* The leap years are 1972 and every 4. year until 2096,
     258             :    * but this algorithm will fail after year 2099 */
     259          92 :   g += t->tm_mday;
     260          92 :   if ((t->tm_year % 4) || (t->tm_mon < 2))
     261          74 :     g--;
     262          92 :   t->tm_yday = g;
     263             : 
     264             :   /* Compute the number of days since January 1, 1970 */
     265          92 :   g += (t->tm_year - 70) * (time_t) 365;
     266          92 :   g += (t->tm_year - 69) / 4;
     267             : 
     268             :   /* Compute the number of hours */
     269          92 :   g *= 24;
     270          92 :   g += t->tm_hour;
     271             : 
     272             :   /* Compute the number of minutes */
     273          92 :   g *= 60;
     274          92 :   g += t->tm_min;
     275             : 
     276             :   /* Compute the number of seconds */
     277          92 :   g *= 60;
     278          92 :   g += t->tm_sec;
     279             : 
     280          92 :   if (local)
     281           2 :     g -= compute_tz(g, t);
     282             : 
     283          92 :   return g;
     284             : }
     285             : 
     286             : /**
     287             :  * mutt_date_normalize_time - Fix the contents of a struct tm
     288             :  * @param tm Time to correct
     289             :  *
     290             :  * If values have been added/subtracted from a struct tm, it can lead to
     291             :  * invalid dates, e.g.  Adding 10 days to the 25th of a month.
     292             :  *
     293             :  * This function will correct any over/under-flow.
     294             :  */
     295          20 : void mutt_date_normalize_time(struct tm *tm)
     296             : {
     297          20 :   if (!tm)
     298           2 :     return;
     299             : 
     300             :   static const char DaysPerMonth[mutt_array_size(Months)] = {
     301             :     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
     302             :   };
     303             :   int leap;
     304             : 
     305          22 :   while (tm->tm_sec < 0)
     306             :   {
     307           4 :     tm->tm_sec += 60;
     308           4 :     tm->tm_min--;
     309             :   }
     310          22 :   while (tm->tm_sec >= 60)
     311             :   {
     312           4 :     tm->tm_sec -= 60;
     313           4 :     tm->tm_min++;
     314             :   }
     315          22 :   while (tm->tm_min < 0)
     316             :   {
     317           4 :     tm->tm_min += 60;
     318           4 :     tm->tm_hour--;
     319             :   }
     320          22 :   while (tm->tm_min >= 60)
     321             :   {
     322           4 :     tm->tm_min -= 60;
     323           4 :     tm->tm_hour++;
     324             :   }
     325          22 :   while (tm->tm_hour < 0)
     326             :   {
     327           4 :     tm->tm_hour += 24;
     328           4 :     tm->tm_mday--;
     329             :   }
     330          22 :   while (tm->tm_hour >= 24)
     331             :   {
     332           4 :     tm->tm_hour -= 24;
     333           4 :     tm->tm_mday++;
     334             :   }
     335             :   /* use loops on NNNdwmy user input values? */
     336          20 :   while (tm->tm_mon < 0)
     337             :   {
     338           2 :     tm->tm_mon += 12;
     339           2 :     tm->tm_year--;
     340             :   }
     341          20 :   while (tm->tm_mon >= 12)
     342             :   {
     343           2 :     tm->tm_mon -= 12;
     344           2 :     tm->tm_year++;
     345             :   }
     346          24 :   while (tm->tm_mday <= 0)
     347             :   {
     348           6 :     if (tm->tm_mon)
     349           2 :       tm->tm_mon--;
     350             :     else
     351             :     {
     352           4 :       tm->tm_mon = 11;
     353           4 :       tm->tm_year--;
     354             :     }
     355           6 :     tm->tm_mday += DaysPerMonth[tm->tm_mon] + is_leap_year_feb(tm);
     356             :   }
     357          24 :   while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + (leap = is_leap_year_feb(tm))))
     358             :   {
     359           6 :     tm->tm_mday -= DaysPerMonth[tm->tm_mon] + leap;
     360           6 :     if (tm->tm_mon < 11)
     361           2 :       tm->tm_mon++;
     362             :     else
     363             :     {
     364           4 :       tm->tm_mon = 0;
     365           4 :       tm->tm_year++;
     366             :     }
     367             :   }
     368             : }
     369             : 
     370             : /**
     371             :  * mutt_date_make_date - Write a date in RFC822 format to a buffer
     372             :  * @param buf   Buffer for result
     373             :  * @param local If true, use the local timezone.  Otherwise use UTC.
     374             :  *
     375             :  * Appends the date to the passed in buffer.
     376             :  * The buffer is not cleared because some callers prepend quotes.
     377             :  */
     378           8 : void mutt_date_make_date(struct Buffer *buf, bool local)
     379             : {
     380           8 :   if (!buf)
     381           4 :     return;
     382             : 
     383             :   struct tm tm;
     384           4 :   time_t tz = 0;
     385             : 
     386           4 :   time_t t = mutt_date_epoch();
     387           4 :   if (local)
     388             :   {
     389           2 :     tm = mutt_date_localtime(t);
     390           2 :     tz = mutt_date_local_tz(t);
     391             :   }
     392             :   else
     393             :   {
     394           2 :     tm = mutt_date_gmtime(t);
     395             :   }
     396             : 
     397           4 :   tz /= 60;
     398             : 
     399           4 :   mutt_buffer_add_printf(buf, "%s, %d %s %d %02d:%02d:%02d %+03d%02d",
     400           4 :                          Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
     401           4 :                          tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec,
     402           4 :                          (int) tz / 60, (int) abs((int) tz) % 60);
     403             : }
     404             : 
     405             : /**
     406             :  * mutt_date_check_month - Is the string a valid month name
     407             :  * @param s String to check
     408             :  * @retval num Index into Months array (0-based)
     409             :  * @retval -1  Error
     410             :  *
     411             :  * @note Only the first three characters are checked
     412             :  * @note The comparison is case insensitive
     413             :  */
     414         112 : int mutt_date_check_month(const char *s)
     415             : {
     416         592 :   for (int i = 0; i < mutt_array_size(Months); i++)
     417         586 :     if (mutt_istr_startswith(s, Months[i]))
     418         106 :       return i;
     419             : 
     420           6 :   return -1; /* error */
     421             : }
     422             : 
     423             : /**
     424             :  * mutt_date_epoch - Return the number of seconds since the Unix epoch
     425             :  * @retval num Number of seconds since the Unix epoch, or 0 on failure
     426             :  */
     427          16 : time_t mutt_date_epoch(void)
     428             : {
     429          16 :   return mutt_date_epoch_ms() / 1000;
     430             : }
     431             : 
     432             : /**
     433             :  * mutt_date_epoch_ms - Return the number of milliseconds since the Unix epoch
     434             :  * @retval ms The number of ms since the Unix epoch, or 0 on failure
     435             :  */
     436          16 : uint64_t mutt_date_epoch_ms(void)
     437             : {
     438          16 :   struct timeval tv = { 0, 0 };
     439          16 :   gettimeofday(&tv, NULL);
     440             :   /* We assume that gettimeofday doesn't modify its first argument on failure.
     441             :    * We also kind of assume that gettimeofday does not fail. */
     442          16 :   return (uint64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
     443             : }
     444             : 
     445             : /**
     446             :  * mutt_date_parse_date - Parse a date string in RFC822 format
     447             :  * @param[in]  s      String to parse
     448             :  * @param[out] tz_out Pointer to timezone (optional)
     449             :  * @retval num Unix time in seconds
     450             :  *
     451             :  * Parse a date of the form:
     452             :  * `[ weekday , ] day-of-month month year hour:minute:second [ timezone ]`
     453             :  *
     454             :  * The 'timezone' field is optional; it defaults to +0000 if missing.
     455             :  */
     456         100 : time_t mutt_date_parse_date(const char *s, struct Tz *tz_out)
     457             : {
     458         100 :   if (!s)
     459           2 :     return -1;
     460             : 
     461          98 :   bool lax = false;
     462             : 
     463          98 :   const regmatch_t *match = mutt_prex_capture(PREX_RFC5322_DATE, s);
     464          98 :   if (!match)
     465             :   {
     466          60 :     match = mutt_prex_capture(PREX_RFC5322_DATE_LAX, s);
     467          60 :     if (!match)
     468             :     {
     469          22 :       mutt_debug(LL_DEBUG1, "Could not parse date: <%s>\n", s);
     470          22 :       return -1;
     471             :     }
     472          38 :     lax = true;
     473          38 :     mutt_debug(LL_DEBUG2, "Fallback regex for date: <%s>\n", s);
     474             :   }
     475             : 
     476          76 :   struct tm tm = { 0 };
     477             : 
     478             :   // clang-format off
     479          76 :   const regmatch_t *mday    = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_DAY    : PREX_RFC5322_DATE_MATCH_DAY];
     480          76 :   const regmatch_t *mmonth  = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_MONTH  : PREX_RFC5322_DATE_MATCH_MONTH];
     481          76 :   const regmatch_t *myear   = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_YEAR   : PREX_RFC5322_DATE_MATCH_YEAR];
     482          76 :   const regmatch_t *mhour   = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_HOUR   : PREX_RFC5322_DATE_MATCH_HOUR];
     483          76 :   const regmatch_t *mminute = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_MINUTE : PREX_RFC5322_DATE_MATCH_MINUTE];
     484          76 :   const regmatch_t *msecond = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_SECOND : PREX_RFC5322_DATE_MATCH_SECOND];
     485          76 :   const regmatch_t *mtz     = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_TZ     : PREX_RFC5322_DATE_MATCH_TZ];
     486          76 :   const regmatch_t *mtzobs  = &match[lax ? PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS : PREX_RFC5322_DATE_MATCH_TZ_OBS];
     487             :   // clang-format on
     488             : 
     489             :   /* Day */
     490          76 :   sscanf(s + mutt_regmatch_start(mday), "%d", &tm.tm_mday);
     491          76 :   if (tm.tm_mday > 31)
     492           2 :     return -1;
     493             : 
     494             :   /* Month */
     495          74 :   tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
     496             : 
     497             :   /* Year */
     498          74 :   sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
     499          74 :   if (tm.tm_year < 50)
     500           2 :     tm.tm_year += 100;
     501          72 :   else if (tm.tm_year >= 1900)
     502          72 :     tm.tm_year -= 1900;
     503             : 
     504             :   /* Time */
     505          74 :   int hour, min, sec = 0;
     506          74 :   sscanf(s + mutt_regmatch_start(mhour), "%d", &hour);
     507          74 :   sscanf(s + mutt_regmatch_start(mminute), "%d", &min);
     508          74 :   if (mutt_regmatch_start(msecond) != -1)
     509          68 :     sscanf(s + mutt_regmatch_start(msecond), "%d", &sec);
     510          74 :   if ((hour > 23) || (min > 59) || (sec > 60))
     511          10 :     return -1;
     512          64 :   tm.tm_hour = hour;
     513          64 :   tm.tm_min = min;
     514          64 :   tm.tm_sec = sec;
     515             : 
     516             :   /* Time zone */
     517          64 :   int zhours = 0;
     518          64 :   int zminutes = 0;
     519          64 :   bool zoccident = false;
     520          64 :   if (mutt_regmatch_start(mtz) != -1)
     521             :   {
     522             :     char direction;
     523          34 :     sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
     524          34 :     zoccident = (direction == '-');
     525             :   }
     526          30 :   else if (mutt_regmatch_start(mtzobs) != -1)
     527             :   {
     528             :     const struct Tz *tz =
     529          16 :         find_tz(s + mutt_regmatch_start(mtzobs), mutt_regmatch_len(mtzobs));
     530          16 :     if (tz)
     531             :     {
     532          14 :       zhours = tz->zhours;
     533          14 :       zminutes = tz->zminutes;
     534          14 :       zoccident = tz->zoccident;
     535             :     }
     536             :   }
     537             : 
     538          64 :   if (tz_out)
     539             :   {
     540          64 :     tz_out->zhours = zhours;
     541          64 :     tz_out->zminutes = zminutes;
     542          64 :     tz_out->zoccident = zoccident;
     543             :   }
     544             : 
     545          64 :   return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
     546             : }
     547             : 
     548             : /**
     549             :  * mutt_date_make_imap - Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz
     550             :  * @param buf       Buffer to store the results
     551             :  * @param buflen    Length of buffer
     552             :  * @param timestamp Time to format
     553             :  * @retval num Characters written to buf
     554             :  *
     555             :  * Caller should provide a buffer of at least 27 bytes.
     556             :  */
     557           4 : int mutt_date_make_imap(char *buf, size_t buflen, time_t timestamp)
     558             : {
     559           4 :   if (!buf)
     560           2 :     return -1;
     561             : 
     562           2 :   struct tm tm = mutt_date_localtime(timestamp);
     563           2 :   time_t tz = mutt_date_local_tz(timestamp);
     564             : 
     565           2 :   tz /= 60;
     566             : 
     567           2 :   return snprintf(buf, buflen, "%02d-%s-%d %02d:%02d:%02d %+03d%02d",
     568           2 :                   tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
     569           2 :                   tm.tm_min, tm.tm_sec, (int) tz / 60, (int) abs((int) tz) % 60);
     570             : }
     571             : 
     572             : /**
     573             :  * mutt_date_make_tls - Format date in TLS certificate verification style
     574             :  * @param buf       Buffer to store the results
     575             :  * @param buflen    Length of buffer
     576             :  * @param timestamp Time to format
     577             :  * @retval num Characters written to buf
     578             :  *
     579             :  * e.g., Mar 17 16:40:46 2016 UTC. The time is always in UTC.
     580             :  *
     581             :  * Caller should provide a buffer of at least 27 bytes.
     582             :  */
     583           4 : int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
     584             : {
     585           4 :   if (!buf)
     586           2 :     return -1;
     587             : 
     588           2 :   struct tm tm = mutt_date_gmtime(timestamp);
     589           2 :   return snprintf(buf, buflen, "%s, %d %s %d %02d:%02d:%02d UTC",
     590           2 :                   Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
     591           2 :                   tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
     592             : }
     593             : 
     594             : /**
     595             :  * mutt_date_parse_imap - Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz
     596             :  * @param s Date in string form
     597             :  * @retval num Unix time
     598             :  * @retval 0   Error
     599             :  */
     600          20 : time_t mutt_date_parse_imap(const char *s)
     601             : {
     602          20 :   const regmatch_t *match = mutt_prex_capture(PREX_IMAP_DATE, s);
     603          20 :   if (!match)
     604          14 :     return 0;
     605             : 
     606           6 :   const regmatch_t *mday = &match[PREX_IMAP_DATE_MATCH_DAY];
     607           6 :   const regmatch_t *mmonth = &match[PREX_IMAP_DATE_MATCH_MONTH];
     608           6 :   const regmatch_t *myear = &match[PREX_IMAP_DATE_MATCH_YEAR];
     609           6 :   const regmatch_t *mtime = &match[PREX_IMAP_DATE_MATCH_TIME];
     610           6 :   const regmatch_t *mtz = &match[PREX_IMAP_DATE_MATCH_TZ];
     611             : 
     612             :   struct tm tm;
     613             : 
     614           6 :   sscanf(s + mutt_regmatch_start(mday), " %d", &tm.tm_mday);
     615           6 :   tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
     616           6 :   sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
     617           6 :   tm.tm_year -= 1900;
     618           6 :   sscanf(s + mutt_regmatch_start(mtime), "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
     619             : 
     620             :   char direction;
     621             :   int zhours, zminutes;
     622           6 :   sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
     623           6 :   bool zoccident = (direction == '-');
     624             : 
     625           6 :   return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
     626             : }
     627             : 
     628             : /**
     629             :  * mutt_date_add_timeout - Safely add a timeout to a given time_t value
     630             :  * @param now     Time now
     631             :  * @param timeout Timeout in seconds
     632             :  * @retval num Unix time to timeout
     633             :  *
     634             :  * This will truncate instead of overflowing.
     635             :  */
     636          18 : time_t mutt_date_add_timeout(time_t now, time_t timeout)
     637             : {
     638          18 :   if (timeout < 0)
     639           4 :     return now;
     640             : 
     641          14 :   if ((TIME_T_MAX - now) < timeout)
     642           4 :     return TIME_T_MAX;
     643             : 
     644          10 :   return now + timeout;
     645             : }
     646             : 
     647             : /**
     648             :  * mutt_date_localtime - Converts calendar time to a broken-down time structure expressed in user timezone
     649             :  * @param  t  Time
     650             :  * @retval obj Broken-down time representation
     651             :  *
     652             :  * Uses current time if t is #MUTT_DATE_NOW
     653             :  */
     654          24 : struct tm mutt_date_localtime(time_t t)
     655             : {
     656          24 :   struct tm tm = { 0 };
     657             : 
     658          24 :   if (t == MUTT_DATE_NOW)
     659           2 :     t = mutt_date_epoch();
     660             : 
     661          24 :   localtime_r(&t, &tm);
     662          24 :   return tm;
     663             : }
     664             : 
     665             : /**
     666             :  * mutt_date_gmtime - Converts calendar time to a broken-down time structure expressed in UTC timezone
     667             :  * @param  t  Time
     668             :  * @retval obj Broken-down time representation
     669             :  *
     670             :  * Uses current time if t is #MUTT_DATE_NOW
     671             :  */
     672          20 : struct tm mutt_date_gmtime(time_t t)
     673             : {
     674          20 :   struct tm tm = { 0 };
     675             : 
     676          20 :   if (t == MUTT_DATE_NOW)
     677           2 :     t = mutt_date_epoch();
     678             : 
     679          20 :   gmtime_r(&t, &tm);
     680          20 :   return tm;
     681             : }
     682             : 
     683             : /**
     684             :  * mutt_date_localtime_format - Format localtime
     685             :  * @param buf    Buffer to store formatted time
     686             :  * @param buflen Buffer size
     687             :  * @param format Format to apply
     688             :  * @param t      Time to format
     689             :  * @retval num   Number of Bytes added to buffer, excluding NUL byte
     690             :  */
     691           6 : size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
     692             : {
     693           6 :   if (!buf || !format)
     694           4 :     return 0;
     695             : 
     696           2 :   struct tm tm = mutt_date_localtime(t);
     697           2 :   return strftime(buf, buflen, format, &tm);
     698             : }
     699             : 
     700             : /**
     701             :  * mutt_date_sleep_ms - Sleep for milliseconds
     702             :  * @param ms Number of milliseconds to sleep
     703             :  */
     704           2 : void mutt_date_sleep_ms(size_t ms)
     705             : {
     706           2 :   const struct timespec sleep = {
     707           2 :     .tv_sec = ms / 1000,
     708           2 :     .tv_nsec = (ms % 1000) * 1000000UL,
     709             :   };
     710           2 :   nanosleep(&sleep, NULL);
     711           2 : }

Generated by: LCOV version 1.14