NeoMutt  2025-12-11-276-g10b23b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
date.c File Reference

Time and date handling routines. More...

#include "config.h"
#include <locale.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "date.h"
#include "buffer.h"
#include "ctype2.h"
#include "eqi.h"
#include "logging2.h"
#include "prex.h"
#include "regex3.h"
#include "string2.h"
+ Include dependency graph for date.c:

Go to the source code of this file.

Functions

static int compute_tz (time_t g, struct tm *utc)
 Calculate the number of seconds east of UTC.
 
static time_t add_tz_offset (time_t t, bool w, time_t h, time_t m)
 Compute and add a timezone offset to an UTC time.
 
static const struct Tzfind_tz (const char *s, size_t len)
 Look up a timezone.
 
static int is_leap_year_feb (struct tm *tm)
 Is a given February in a leap year.
 
int mutt_date_local_tz (time_t t)
 Calculate the local timezone in seconds east of UTC.
 
time_t mutt_date_make_time (struct tm *t, bool local)
 Convert struct tm to time_t
 
void mutt_date_normalize_time (struct tm *tm)
 Fix the contents of a struct tm.
 
void mutt_date_make_date (struct Buffer *buf, bool local)
 Write a date in RFC822 format to a buffer.
 
int mutt_date_check_month (const char *s)
 Is the string a valid month name.
 
time_t mutt_date_now (void)
 Return the number of seconds since the Unix epoch.
 
uint64_t mutt_date_now_ms (void)
 Return the number of milliseconds since the Unix epoch.
 
void mutt_time_now (struct timespec *tp)
 Set the provided time field to the current time.
 
static int parse_small_uint (const char *str, const char *end, int *val)
 Parse a positive integer of at most 5 digits.
 
static time_t mutt_date_parse_rfc5322_strict (const char *s, struct Tz *tz_out)
 Parse a date string in RFC822 format.
 
time_t mutt_date_parse_date (const char *s, struct Tz *tz_out)
 Parse a date string in RFC822 format.
 
int mutt_date_make_imap (struct Buffer *buf, time_t timestamp)
 Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
 
int mutt_date_make_tls (char *buf, size_t buflen, time_t timestamp)
 Format date in TLS certificate verification style.
 
time_t mutt_date_parse_imap (const char *s)
 Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
 
time_t mutt_date_add_timeout (time_t now, time_t timeout)
 Safely add a timeout to a given time_t value.
 
struct tm mutt_date_localtime (time_t t)
 Converts calendar time to a broken-down time structure expressed in user timezone.
 
struct tm mutt_date_gmtime (time_t t)
 Converts calendar time to a broken-down time structure expressed in UTC timezone.
 
size_t mutt_date_localtime_format (char *buf, size_t buflen, const char *format, time_t t)
 Format localtime.
 
size_t mutt_date_localtime_format_locale (char *buf, size_t buflen, const char *format, time_t t, locale_t loc)
 Format localtime using a given locale.
 
void mutt_date_sleep_ms (size_t ms)
 Sleep for milliseconds.
 

Variables

static const char *const Weekdays []
 Day of the week (abbreviated)
 
static const char *const Months []
 Months of the year (abbreviated)
 
static const struct Tz TimeZones []
 Lookup table of Time Zones.
 

Detailed Description

Time and date handling routines.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Victor Fernandes
  • Reto Brunner
  • Dennis Schön
  • Rayford Shireman
  • Steinar H Gunderson
  • наб
  • Alejandro Colomar
  • Thomas Klausner

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file date.c.

Function Documentation

◆ compute_tz()

static int compute_tz ( time_t g,
struct tm * utc )
static

Calculate the number of seconds east of UTC.

Parameters
gLocal time
utcUTC time
Return values
numSeconds east of UTC

returns the seconds east of UTC given 'g' and its corresponding gmtime() representation

Definition at line 141 of file date.c.

142{
143 struct tm lt = mutt_date_localtime(g);
144
145 int tz = (((lt.tm_hour - utc->tm_hour) * 60) + (lt.tm_min - utc->tm_min)) * 60;
146
147 int yday = (lt.tm_yday - utc->tm_yday);
148 if (yday != 0)
149 {
150 /* This code is optimized to negative timezones (West of Greenwich) */
151 if ((yday == -1) || /* UTC passed midnight before localtime */
152 (yday > 1)) /* UTC passed new year before localtime */
153 {
154 tz -= (24 * 60 * 60);
155 }
156 else
157 {
158 tz += (24 * 60 * 60);
159 }
160 }
161
162 return tz;
163}
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition date.c:907
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ add_tz_offset()

static time_t add_tz_offset ( time_t t,
bool w,
time_t h,
time_t m )
static

Compute and add a timezone offset to an UTC time.

Parameters
tUTC time
wTrue if west of UTC, false if east
hNumber of hours in the timezone
mNumber of minutes in the timezone
Return values
numTimezone offset in seconds

Definition at line 173 of file date.c.

174{
175 if ((t != TIME_T_MAX) && (t != TIME_T_MIN))
176 return t + (w ? 1 : -1) * (((time_t) h * 3600) + ((time_t) m * 60));
177 else
178 return t;
179}
#define TIME_T_MAX
Maximum value for a time_t.
Definition date.h:40
#define TIME_T_MIN
Minimum value for a time_t.
Definition date.h:42
+ Here is the caller graph for this function:

◆ find_tz()

static const struct Tz * find_tz ( const char * s,
size_t len )
static

Look up a timezone.

Parameters
sTimezone to lookup
lenLength of the s string
Return values
ptrPointer to the Tz struct
NULLNot found

Definition at line 188 of file date.c.

189{
190 for (size_t i = 0; i < countof(TimeZones); i++)
191 {
192 if (mutt_istrn_equal(TimeZones[i].tzname, s, len))
193 return &TimeZones[i];
194 }
195 return NULL;
196}
#define countof(x)
Definition memory.h:49
static const struct Tz TimeZones[]
Lookup table of Time Zones.
Definition date.c:78
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition string.c:457
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ is_leap_year_feb()

static int is_leap_year_feb ( struct tm * tm)
static

Is a given February in a leap year.

Parameters
tmDate to be tested
Return values
trueIt's a leap year

Definition at line 203 of file date.c.

204{
205 if (tm->tm_mon != 1)
206 return 0;
207
208 int y = tm->tm_year + 1900;
209 return ((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0));
210}
+ Here is the caller graph for this function:

◆ mutt_date_local_tz()

int mutt_date_local_tz ( time_t t)

Calculate the local timezone in seconds east of UTC.

Parameters
tTime to examine
Return values
numSeconds east of UTC

Returns the local timezone in seconds east of UTC for the time t, or for the current time if t is zero.

Definition at line 220 of file date.c.

221{
222 /* Check we haven't overflowed the time (on 32-bit arches) */
223 if ((t == TIME_T_MAX) || (t == TIME_T_MIN))
224 return 0;
225
226 if (t == 0)
227 t = mutt_date_now();
228
229 struct tm tm = mutt_date_gmtime(t);
230 return compute_tz(t, &tm);
231}
static int compute_tz(time_t g, struct tm *utc)
Calculate the number of seconds east of UTC.
Definition date.c:141
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition date.c:928
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_make_time()

time_t mutt_date_make_time ( struct tm * t,
bool local )

Convert struct tm to time_t

Parameters
tTime to convert
localShould the local timezone be considered
Return values
numTime in Unix format
TIME_T_MINError

Convert a struct tm to time_t, but don't take the local timezone into account unless "local" is nonzero

Definition at line 243 of file date.c.

244{
245 if (!t)
246 return TIME_T_MIN;
247
248 static const int AccumDaysPerMonth[countof(Months)] = {
249 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
250 };
251
252 /* Prevent an integer overflow, with some arbitrary limits. */
253 if (t->tm_year > 10000)
254 return TIME_T_MAX;
255 if (t->tm_year < -10000)
256 return TIME_T_MIN;
257
258 if ((t->tm_mday < 1) || (t->tm_mday > 31))
259 return TIME_T_MIN;
260 if ((t->tm_hour < 0) || (t->tm_hour > 23) || (t->tm_min < 0) ||
261 (t->tm_min > 59) || (t->tm_sec < 0) || (t->tm_sec > 60))
262 {
263 return TIME_T_MIN;
264 }
265 if (t->tm_year > 9999)
266 return TIME_T_MAX;
267
268 /* Compute the number of days since January 1 in the same year */
269 int yday = AccumDaysPerMonth[t->tm_mon % countof(Months)];
270
271 /* The leap years are 1972 and every 4. year until 2096,
272 * but this algorithm will fail after year 2099 */
273 yday += t->tm_mday;
274 if ((t->tm_year % 4) || (t->tm_mon < 2))
275 yday--;
276 t->tm_yday = yday;
277
278 time_t g = yday;
279
280 /* Compute the number of days since January 1, 1970 */
281 g += (t->tm_year - 70) * (time_t) 365;
282 g += (t->tm_year - 69) / 4;
283
284 /* Compute the number of hours */
285 g *= 24;
286 g += t->tm_hour;
287
288 /* Compute the number of minutes */
289 g *= 60;
290 g += t->tm_min;
291
292 /* Compute the number of seconds */
293 g *= 60;
294 g += t->tm_sec;
295
296 if (local)
297 g -= compute_tz(g, t);
298
299 return g;
300}
static const char *const Months[]
Months of the year (abbreviated)
Definition date.c:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_normalize_time()

void mutt_date_normalize_time ( struct tm * tm)

Fix the contents of a struct tm.

Parameters
tmTime to correct

If values have been added/subtracted from a struct tm, it can lead to invalid dates, e.g. Adding 10 days to the 25th of a month.

This function will correct any over/under-flow.

Definition at line 311 of file date.c.

312{
313 if (!tm)
314 return;
315
316 static const char DaysPerMonth[countof(Months)] = {
317 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
318 };
319 int leap;
320
321 while (tm->tm_sec < 0)
322 {
323 tm->tm_sec += 60;
324 tm->tm_min--;
325 }
326 while (tm->tm_sec >= 60)
327 {
328 tm->tm_sec -= 60;
329 tm->tm_min++;
330 }
331 while (tm->tm_min < 0)
332 {
333 tm->tm_min += 60;
334 tm->tm_hour--;
335 }
336 while (tm->tm_min >= 60)
337 {
338 tm->tm_min -= 60;
339 tm->tm_hour++;
340 }
341 while (tm->tm_hour < 0)
342 {
343 tm->tm_hour += 24;
344 tm->tm_mday--;
345 }
346 while (tm->tm_hour >= 24)
347 {
348 tm->tm_hour -= 24;
349 tm->tm_mday++;
350 }
351 /* use loops on NNNdwmy user input values? */
352 while (tm->tm_mon < 0)
353 {
354 tm->tm_mon += 12;
355 tm->tm_year--;
356 }
357 while (tm->tm_mon >= 12)
358 {
359 tm->tm_mon -= 12;
360 tm->tm_year++;
361 }
362 while (tm->tm_mday <= 0)
363 {
364 if (tm->tm_mon)
365 {
366 tm->tm_mon--;
367 }
368 else
369 {
370 tm->tm_mon = 11;
371 tm->tm_year--;
372 }
373 tm->tm_mday += DaysPerMonth[tm->tm_mon] + is_leap_year_feb(tm);
374 }
375 while (tm->tm_mday > (DaysPerMonth[tm->tm_mon] + (leap = is_leap_year_feb(tm))))
376 {
377 tm->tm_mday -= DaysPerMonth[tm->tm_mon] + leap;
378 if (tm->tm_mon < 11)
379 {
380 tm->tm_mon++;
381 }
382 else
383 {
384 tm->tm_mon = 0;
385 tm->tm_year++;
386 }
387 }
388}
static int is_leap_year_feb(struct tm *tm)
Is a given February in a leap year.
Definition date.c:203
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_make_date()

void mutt_date_make_date ( struct Buffer * buf,
bool local )

Write a date in RFC822 format to a buffer.

Parameters
bufBuffer for result
localIf true, use the local timezone. Otherwise use UTC.

Appends the date to the passed in buffer. The buffer is not cleared because some callers prepend quotes.

Definition at line 398 of file date.c.

399{
400 if (!buf)
401 return;
402
403 struct tm tm = { 0 };
404 int tz = 0;
405
406 time_t t = mutt_date_now();
407 if (local)
408 {
409 tm = mutt_date_localtime(t);
410 tz = mutt_date_local_tz(t);
411 }
412 else
413 {
414 tm = mutt_date_gmtime(t);
415 }
416
417 tz /= 60;
418
419 buf_add_printf(buf, "%s, %d %s %d %02d:%02d:%02d %+03d%02d", Weekdays[tm.tm_wday],
420 tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
421 tm.tm_min, tm.tm_sec, tz / 60, abs(tz) % 60);
422}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
static const char *const Weekdays[]
Day of the week (abbreviated)
Definition date.c:61
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:220
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_check_month()

int mutt_date_check_month ( const char * s)

Is the string a valid month name.

Parameters
sString to check (must be at least 3 bytes long)
Return values
numIndex into Months array (0-based)
-1Error
Note
Only the first three characters are checked
The comparison is case insensitive

Definition at line 433 of file date.c.

434{
435 if (!s)
436 return -1;
437
438 char buf[4] = { 0 };
439 memcpy(buf, s, 3);
440 uint32_t sv;
441 memcpy(&sv, buf, sizeof(sv));
442 for (int i = 0; i < countof(Months); i++)
443 {
444 uint32_t mv;
445 memcpy(&mv, Months[i], sizeof(mv));
446 if (sv == mv)
447 return i;
448 }
449
450 return -1; /* error */
451}
+ Here is the caller graph for this function:

◆ mutt_date_now()

time_t mutt_date_now ( void )

Return the number of seconds since the Unix epoch.

Return values
numNumber of seconds since the Unix epoch, or 0 on failure

Definition at line 457 of file date.c.

458{
459 return mutt_date_now_ms() / 1000;
460}
uint64_t mutt_date_now_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition date.c:466
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_now_ms()

uint64_t mutt_date_now_ms ( void )

Return the number of milliseconds since the Unix epoch.

Return values
numThe number of ms since the Unix epoch, or 0 on failure

Definition at line 466 of file date.c.

467{
468 struct timeval tv = { 0, 0 };
469 gettimeofday(&tv, NULL);
470 /* We assume that gettimeofday doesn't modify its first argument on failure.
471 * We also kind of assume that gettimeofday does not fail. */
472 return ((uint64_t) tv.tv_sec * 1000) + (tv.tv_usec / 1000);
473}
+ Here is the caller graph for this function:

◆ mutt_time_now()

void mutt_time_now ( struct timespec * tp)

Set the provided time field to the current time.

Parameters
[out]tpField to set

Uses nanosecond precision if available, if not we fallback to microseconds.

Definition at line 481 of file date.c.

482{
483#ifdef HAVE_CLOCK_GETTIME
484 if (clock_gettime(CLOCK_REALTIME, tp) != 0)
485 mutt_perror("clock_gettime");
486#else
487 struct timeval tv = { 0, 0 };
488 if (gettimeofday(&tv, NULL) != 0)
489 mutt_perror("gettimeofday");
490 tp->tv_sec = tv.tv_sec;
491 tp->tv_nsec = tv.tv_usec * 1000;
492#endif
493}
#define mutt_perror(...)
Definition logging2.h:95
+ Here is the caller graph for this function:

◆ parse_small_uint()

static int parse_small_uint ( const char * str,
const char * end,
int * val )
static

Parse a positive integer of at most 5 digits.

Parameters
[in]strString to parse
[in]endEnd of the string
[out]valValue
Return values
numNumber of chars parsed

Leaves val untouched if the given string does not start with an integer; ignores junk after it or any digits beyond the first five (this is so that the function can never overflow, yet check if the integer is larger than the maximum 4 digits supported in a year). and does not support negative numbers. Empty strings are parsed as zero.

Definition at line 508 of file date.c.

509{
510 const char *ptr = str;
511 int v = 0;
512 while ((ptr < end) && (ptr < (str + 5)) && (*ptr >= '0') && (*ptr <= '9'))
513 {
514 v = (v * 10) + (*ptr - '0');
515 ptr++;
516 }
517 *val = v;
518 return ptr - str;
519}
+ Here is the caller graph for this function:

◆ mutt_date_parse_rfc5322_strict()

static time_t mutt_date_parse_rfc5322_strict ( const char * s,
struct Tz * tz_out )
static

Parse a date string in RFC822 format.

Parameters
[in]sString to parse
[out]tz_outTimezone info (OPTIONAL)
Return values
numUnix time in seconds

Parse a date string in RFC822 format, without any comments or extra whitespace (except a comment at the very end, since that is very common for time zones).

This is a fairly straightforward implementation in the hope of extracting the valid cases quickly, i.e., without having to resort to a regex. The hard cases are left to a regex implementation further down in mutt_date_parse_date() (which calls us).

Spec: https://tools.ietf.org/html/rfc5322#section-3.3

Definition at line 538 of file date.c.

539{
540 size_t len = strlen(s);
541
542 /* Skip over the weekday, if any. */
543 if ((len >= 5) && (s[4] == ' ') &&
544 (eqi4(s, "Mon,") || eqi4(s, "Tue,") || eqi4(s, "Wed,") ||
545 eqi4(s, "Thu,") || eqi4(s, "Fri,") || eqi4(s, "Sat,") || eqi4(s, "Sun,")))
546 {
547 s += 5;
548 len -= 5;
549 }
550
551 while ((len > 0) && (*s == ' '))
552 {
553 s++;
554 len--;
555 }
556
557 if ((len == 0) || (*s < '0') || (*s > '9'))
558 return -1;
559
560 struct tm tm = { 0 };
561
562 /* Day */
563 int mday_len = parse_small_uint(s, s + len, &tm.tm_mday);
564 if ((mday_len == 0) || (mday_len > 2) || (tm.tm_mday > 31))
565 return -1;
566 s += mday_len;
567 len -= mday_len;
568
569 if ((len == 0) || (*s != ' '))
570 return -1;
571 s++;
572 len--;
573
574 /* Month */
575 if (len < 3)
576 return -1;
577 tm.tm_mon = mutt_date_check_month(s);
578 if (tm.tm_mon == -1)
579 return -1;
580 s += 3;
581 len -= 3;
582
583 if ((len == 0) || (*s != ' '))
584 return -1;
585 s++;
586 len--;
587
588 /* Year */
589 int year_len = parse_small_uint(s, s + len, &tm.tm_year);
590 if ((year_len != 2) && (year_len != 4))
591 return -1;
592 if (tm.tm_year < 50)
593 tm.tm_year += 100;
594 else if (tm.tm_year >= 1900)
595 tm.tm_year -= 1900;
596 s += year_len;
597 len -= year_len;
598
599 if ((len == 0) || (*s != ' '))
600 return -1;
601 s++;
602 len--;
603
604 /* Hour */
605 if ((len < 3) || (s[0] < '0') || (s[0] > '2') || (s[1] < '0') ||
606 (s[1] > '9') || (s[2] != ':'))
607 {
608 return -1;
609 }
610 tm.tm_hour = ((s[0] - '0') * 10) + (s[1] - '0');
611 if (tm.tm_hour > 23)
612 return -1;
613 s += 3;
614 len -= 3;
615
616 /* Minute */
617 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
618 return -1;
619 tm.tm_min = ((s[0] - '0') * 10) + (s[1] - '0');
620 if (tm.tm_min > 59)
621 return -1;
622 s += 2;
623 len -= 2;
624
625 /* Second (optional) */
626 if ((len > 0) && (s[0] == ':'))
627 {
628 s++;
629 len--;
630 if ((len < 2) || (s[0] < '0') || (s[0] > '5') || (s[1] < '0') || (s[1] > '9'))
631 return -1;
632 tm.tm_sec = ((s[0] - '0') * 10) + (s[1] - '0');
633 if (tm.tm_sec > 60)
634 return -1;
635 s += 2;
636 len -= 2;
637 }
638
639 while ((len > 0) && (*s == ' '))
640 {
641 s++;
642 len--;
643 }
644
645 /* Strip optional time zone comment and white space from the end
646 * (this is the only one that is very common) */
647 while ((len > 0) && (s[len - 1] == ' '))
648 len--;
649 if ((len >= 2) && (s[len - 1] == ')'))
650 {
651 for (int i = len - 2; i >= 0; i--)
652 {
653 if (s[i] == '(')
654 {
655 len = i;
656 break;
657 }
658 if (!mutt_isalpha(s[i]) && (s[i] != ' '))
659 return -1; /* give up more complex comment parsing */
660 }
661 }
662 while ((len > 0) && (s[len - 1] == ' '))
663 len--;
664
665 /* Time zone (optional) */
666 int zhours = 0;
667 int zminutes = 0;
668 bool zoccident = false;
669 if (len > 0)
670 {
671 if ((len == 5) && ((s[0] == '+') || (s[0] == '-')) && (s[1] >= '0') &&
672 (s[1] <= '9') && (s[2] >= '0') && (s[2] <= '9') && (s[3] >= '0') &&
673 (s[3] <= '9') && (s[4] >= '0') && (s[4] <= '9'))
674 {
675 zoccident = (s[0] == '-');
676 zhours = ((s[1] - '0') * 10) + (s[2] - '0');
677 zminutes = ((s[3] - '0') * 10) + (s[4] - '0');
678 }
679 else
680 {
681 for (int i = 0; i < len; i++)
682 {
683 if (!mutt_isalpha(s[i]))
684 return -1;
685 }
686 const struct Tz *tz = find_tz(s, len);
687 if (tz)
688 {
689 zhours = tz->zhours;
690 zminutes = tz->zminutes;
691 zoccident = tz->zoccident;
692 }
693 }
694 }
695
696 if (tz_out)
697 {
698 tz_out->zhours = zhours;
699 tz_out->zminutes = zminutes;
700 tz_out->zoccident = zoccident;
701 }
702
704}
bool mutt_isalpha(int arg)
Wrapper for isalpha(3)
Definition ctype.c:53
static bool eqi4(const char *a, const char b[4])
Compare two 4-byte strings, ignoring case - See: Case-insensitive fixed-chunk comparisons.
Definition eqi.h:106
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition date.c:243
static int parse_small_uint(const char *str, const char *end, int *val)
Parse a positive integer of at most 5 digits.
Definition date.c:508
int mutt_date_check_month(const char *s)
Is the string a valid month name.
Definition date.c:433
static const struct Tz * find_tz(const char *s, size_t len)
Look up a timezone.
Definition date.c:188
static time_t add_tz_offset(time_t t, bool w, time_t h, time_t m)
Compute and add a timezone offset to an UTC time.
Definition date.c:173
List of recognised Timezones.
Definition date.h:53
unsigned char zminutes
Minutes away from UTC.
Definition date.h:56
bool zoccident
True if west of UTC, False if East.
Definition date.h:57
unsigned char zhours
Hours away from UTC.
Definition date.h:55
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_parse_date()

time_t mutt_date_parse_date ( const char * s,
struct Tz * tz_out )

Parse a date string in RFC822 format.

Parameters
[in]sString to parse
[out]tz_outPointer to timezone (optional)
Return values
numUnix time in seconds, or -1 on failure

Parse a date of the form: [ weekday , ] day-of-month month year hour:minute:second [ timezone ]

The 'timezone' field is optional; it defaults to +0000 if missing.

Definition at line 717 of file date.c.

718{
719 if (!s)
720 return -1;
721
722 const time_t strict_t = mutt_date_parse_rfc5322_strict(s, tz_out);
723 if (strict_t != -1)
724 return strict_t;
725
726 const regmatch_t *match = mutt_prex_capture(PREX_RFC5322_DATE_LAX, s);
727 if (!match)
728 {
729 mutt_debug(LL_DEBUG1, "Could not parse date: <%s>\n", s);
730 return -1;
731 }
732 mutt_debug(LL_DEBUG2, "Fallback regex for date: <%s>\n", s);
733
734 struct tm tm = { 0 };
735
736 // clang-format off
737 const regmatch_t *mday = &match[PREX_RFC5322_DATE_LAX_MATCH_DAY];
738 const regmatch_t *mmonth = &match[PREX_RFC5322_DATE_LAX_MATCH_MONTH];
739 const regmatch_t *myear = &match[PREX_RFC5322_DATE_LAX_MATCH_YEAR];
740 const regmatch_t *mhour = &match[PREX_RFC5322_DATE_LAX_MATCH_HOUR];
741 const regmatch_t *mminute = &match[PREX_RFC5322_DATE_LAX_MATCH_MINUTE];
742 const regmatch_t *msecond = &match[PREX_RFC5322_DATE_LAX_MATCH_SECOND];
743 const regmatch_t *mtz = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ];
744 const regmatch_t *mtzobs = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS];
745 // clang-format on
746
747 /* Day */
748 sscanf(s + mutt_regmatch_start(mday), "%d", &tm.tm_mday);
749 if (tm.tm_mday > 31)
750 return -1;
751
752 /* Month */
753 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
754
755 /* Year */
756 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
757 if (tm.tm_year < 50)
758 tm.tm_year += 100;
759 else if (tm.tm_year >= 1900)
760 tm.tm_year -= 1900;
761
762 /* Time */
763 int hour = 0, min = 0, sec = 0;
764 sscanf(s + mutt_regmatch_start(mhour), "%d", &hour);
765 sscanf(s + mutt_regmatch_start(mminute), "%d", &min);
766 if (mutt_regmatch_start(msecond) != -1)
767 sscanf(s + mutt_regmatch_start(msecond), "%d", &sec);
768 if ((hour > 23) || (min > 59) || (sec > 60))
769 return -1;
770 tm.tm_hour = hour;
771 tm.tm_min = min;
772 tm.tm_sec = sec;
773
774 /* Time zone */
775 int zhours = 0;
776 int zminutes = 0;
777 bool zoccident = false;
778 if (mutt_regmatch_start(mtz) != -1)
779 {
780 char direction = '\0';
781 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
782 zoccident = (direction == '-');
783 }
784 else if (mutt_regmatch_start(mtzobs) != -1)
785 {
786 const struct Tz *tz = find_tz(s + mutt_regmatch_start(mtzobs),
787 mutt_regmatch_len(mtzobs));
788 if (tz)
789 {
790 zhours = tz->zhours;
791 zminutes = tz->zminutes;
792 zoccident = tz->zoccident;
793 }
794 }
795
796 if (tz_out)
797 {
798 tz_out->zhours = zhours;
799 tz_out->zminutes = zminutes;
800 tz_out->zoccident = zoccident;
801 }
802
804}
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
static time_t mutt_date_parse_rfc5322_strict(const char *s, struct Tz *tz_out)
Parse a date string in RFC822 format.
Definition date.c:538
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition prex.c:301
@ PREX_RFC5322_DATE_LAX
[Mon, (Comment) 16 Mar 2020 15:09:35 -0700]
Definition prex.h:38
@ PREX_RFC5322_DATE_LAX_MATCH_SECOND
Tue, 3 Mar 2020 14:32:[55] +0200
Definition prex.h:147
@ PREX_RFC5322_DATE_LAX_MATCH_TZ
Tue, 3 Mar 2020 14:32:55 [+0200]
Definition prex.h:150
@ PREX_RFC5322_DATE_LAX_MATCH_YEAR
Tue, 3 Mar [2020] 14:32:55 +0200
Definition prex.h:139
@ PREX_RFC5322_DATE_LAX_MATCH_HOUR
Tue, 3 Mar 2020 [14]:32:55 +0200
Definition prex.h:141
@ PREX_RFC5322_DATE_LAX_MATCH_MINUTE
Tue, 3 Mar 2020 14:[32]:55 +0200
Definition prex.h:143
@ PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS
Tue, 3 Mar 2020 14:32:55[UT]
Definition prex.h:151
@ PREX_RFC5322_DATE_LAX_MATCH_MONTH
Tue, 3 [Jan] 2020 14:32:55 +0200
Definition prex.h:137
@ PREX_RFC5322_DATE_LAX_MATCH_DAY
Tue, [3] Mar 2020 14:32:55 +0200
Definition prex.h:135
static size_t mutt_regmatch_len(const regmatch_t *match)
Return the length of a match.
Definition regex3.h:76
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition regex3.h:56
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_make_imap()

int mutt_date_make_imap ( struct Buffer * buf,
time_t timestamp )

Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.

Parameters
bufBuffer to store the results
timestampTime to format
Return values
numCharacters written to buf

Definition at line 812 of file date.c.

813{
814 if (!buf)
815 return -1;
816
817 struct tm tm = mutt_date_localtime(timestamp);
819
820 tz /= 60;
821
822 return buf_printf(buf, "%02d-%s-%d %02d:%02d:%02d %+03d%02d", tm.tm_mday,
823 Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
824 tm.tm_sec, tz / 60, abs(tz) % 60);
825}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
static const char * timestamp(time_t stamp)
Create a YYYY-MM-DD HH:MM:SS timestamp.
Definition logging.c:79
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_make_tls()

int mutt_date_make_tls ( char * buf,
size_t buflen,
time_t timestamp )

Format date in TLS certificate verification style.

Parameters
bufBuffer to store the results
buflenLength of buffer
timestampTime to format
Return values
numCharacters written to buf

e.g., Mar 17 16:40:46 2016 UTC. The time is always in UTC.

Caller should provide a buffer of at least 27 bytes.

Definition at line 838 of file date.c.

839{
840 if (!buf)
841 return -1;
842
843 struct tm tm = mutt_date_gmtime(timestamp);
844 return snprintf(buf, buflen, "%s, %d %s %d %02d:%02d:%02d UTC",
845 Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
846 tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
847}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_parse_imap()

time_t mutt_date_parse_imap ( const char * s)

Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.

Parameters
sDate in string form
Return values
numUnix time
0Error

Definition at line 855 of file date.c.

856{
857 const regmatch_t *match = mutt_prex_capture(PREX_IMAP_DATE, s);
858 if (!match)
859 return 0;
860
861 const regmatch_t *mday = &match[PREX_IMAP_DATE_MATCH_DAY];
862 const regmatch_t *mmonth = &match[PREX_IMAP_DATE_MATCH_MONTH];
863 const regmatch_t *myear = &match[PREX_IMAP_DATE_MATCH_YEAR];
864 const regmatch_t *mtime = &match[PREX_IMAP_DATE_MATCH_TIME];
865 const regmatch_t *mtz = &match[PREX_IMAP_DATE_MATCH_TZ];
866
867 struct tm tm = { 0 };
868
869 sscanf(s + mutt_regmatch_start(mday), " %d", &tm.tm_mday);
870 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
871 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
872 tm.tm_year -= 1900;
873 sscanf(s + mutt_regmatch_start(mtime), "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
874
875 char direction = '\0';
876 int zhours = 0, zminutes = 0;
877 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
878 bool zoccident = (direction == '-');
879
880 return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
881}
@ PREX_IMAP_DATE_MATCH_TIME
15-MAR-2020 [15:09:35] -0700
Definition prex.h:168
@ PREX_IMAP_DATE_MATCH_YEAR
15-MAR-[2020] 15:09:35 -0700
Definition prex.h:167
@ PREX_IMAP_DATE_MATCH_DAY
[ 4]-MAR-2020 15:09:35 -0700
Definition prex.h:163
@ PREX_IMAP_DATE_MATCH_TZ
15-MAR-2020 15:09:35 [-0700]
Definition prex.h:169
@ PREX_IMAP_DATE_MATCH_MONTH
15-[MAR]-2020 15:09:35 -0700
Definition prex.h:166
@ PREX_IMAP_DATE
[16-MAR-2020 15:09:35 -0700]
Definition prex.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_add_timeout()

time_t mutt_date_add_timeout ( time_t now,
time_t timeout )

Safely add a timeout to a given time_t value.

Parameters
nowTime now
timeoutTimeout in seconds
Return values
numUnix time to timeout

This will truncate instead of overflowing.

Definition at line 891 of file date.c.

892{
893 if (timeout < 0)
894 return now;
895
896 if ((TIME_T_MAX - now) < timeout)
897 return TIME_T_MAX;
898
899 return now + timeout;
900}
+ Here is the caller graph for this function:

◆ mutt_date_localtime()

struct tm mutt_date_localtime ( time_t t)

Converts calendar time to a broken-down time structure expressed in user timezone.

Parameters
tTime
Return values
objBroken-down time representation

Definition at line 907 of file date.c.

908{
909 struct tm tm = { 0 };
910
911 struct tm *ret = localtime_r(&t, &tm);
912 if (!ret)
913 {
914 mutt_debug(LL_DEBUG1, "Could not convert time_t via localtime_r() to struct tm: time_t = %jd\n",
915 (intmax_t) t);
916 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
917 mktime(&default_tm); // update derived fields making tm into a valid tm.
918 tm = default_tm;
919 }
920 return tm;
921}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_gmtime()

struct tm mutt_date_gmtime ( time_t t)

Converts calendar time to a broken-down time structure expressed in UTC timezone.

Parameters
tTime
Return values
objBroken-down time representation

Definition at line 928 of file date.c.

929{
930 struct tm tm = { 0 };
931
932 struct tm *ret = gmtime_r(&t, &tm);
933 if (!ret)
934 {
935 mutt_debug(LL_DEBUG1, "Could not convert time_t via gmtime_r() to struct tm: time_t = %jd\n",
936 (intmax_t) t);
937 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
938 mktime(&default_tm); // update derived fields making tm into a valid tm.
939 tm = default_tm;
940 }
941 return tm;
942}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_localtime_format()

size_t mutt_date_localtime_format ( char * buf,
size_t buflen,
const char * format,
time_t t )

Format localtime.

Parameters
bufBuffer to store formatted time
buflenBuffer size
formatFormat to apply
tTime to format
Return values
numNumber of Bytes added to buffer, excluding NUL byte

Definition at line 952 of file date.c.

953{
954 if (!buf || !format)
955 return 0;
956
957 struct tm tm = mutt_date_localtime(t);
958 return strftime(buf, buflen, format, &tm);
959}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_localtime_format_locale()

size_t mutt_date_localtime_format_locale ( char * buf,
size_t buflen,
const char * format,
time_t t,
locale_t loc )

Format localtime using a given locale.

Parameters
bufBuffer to store formatted time
buflenBuffer size
formatFormat to apply
tTime to format
locLocale to use
Return values
numNumber of Bytes added to buffer, excluding NUL byte

Definition at line 970 of file date.c.

972{
973 if (!buf || !format)
974 return 0;
975
976 struct tm tm = mutt_date_localtime(t);
977 return strftime_l(buf, buflen, format, &tm, loc);
978}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_date_sleep_ms()

void mutt_date_sleep_ms ( size_t ms)

Sleep for milliseconds.

Parameters
msNumber of milliseconds to sleep

Definition at line 984 of file date.c.

985{
986 const struct timespec sleep = {
987 .tv_sec = ms / 1000,
988 .tv_nsec = (ms % 1000) * 1000000UL,
989 };
990 nanosleep(&sleep, NULL);
991}
+ Here is the caller graph for this function:

Variable Documentation

◆ Weekdays

const char* const Weekdays[]
static
Initial value:
= {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
}

Day of the week (abbreviated)

Definition at line 61 of file date.c.

61 {
62 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
63};

◆ Months

const char* const Months[]
static
Initial value:
= {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
}

Months of the year (abbreviated)

Definition at line 68 of file date.c.

68 {
69 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
70 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
71};

◆ TimeZones

const struct Tz TimeZones[]
static

Lookup table of Time Zones.

Note
Keep in alphabetical order

Definition at line 78 of file date.c.

78 {
79 // clang-format off
80 { "aat", 1, 0, true }, /* Atlantic Africa Time */
81 { "adt", 4, 0, false }, /* Arabia DST */
82 { "ast", 3, 0, false }, /* Arabia */
83//{ "ast", 4, 0, true }, /* Atlantic */
84 { "bst", 1, 0, false }, /* British DST */
85 { "cat", 1, 0, false }, /* Central Africa */
86 { "cdt", 5, 0, true },
87 { "cest", 2, 0, false }, /* Central Europe DST */
88 { "cet", 1, 0, false }, /* Central Europe */
89 { "cst", 6, 0, true },
90//{ "cst", 8, 0, false }, /* China */
91//{ "cst", 9, 30, false }, /* Australian Central Standard Time */
92 { "eat", 3, 0, false }, /* East Africa */
93 { "edt", 4, 0, true },
94 { "eest", 3, 0, false }, /* Eastern Europe DST */
95 { "eet", 2, 0, false }, /* Eastern Europe */
96 { "egst", 0, 0, false }, /* Eastern Greenland DST */
97 { "egt", 1, 0, true }, /* Eastern Greenland */
98 { "est", 5, 0, true },
99 { "gmt", 0, 0, false },
100 { "gst", 4, 0, false }, /* Presian Gulf */
101 { "hkt", 8, 0, false }, /* Hong Kong */
102 { "ict", 7, 0, false }, /* Indochina */
103 { "idt", 3, 0, false }, /* Israel DST */
104 { "ist", 2, 0, false }, /* Israel */
105//{ "ist", 5, 30, false }, /* India */
106 { "jst", 9, 0, false }, /* Japan */
107 { "kst", 9, 0, false }, /* Korea */
108 { "mdt", 6, 0, true },
109 { "met", 1, 0, false }, /* This is now officially CET */
110 { "met dst", 2, 0, false }, /* MET in Daylight Saving Time */
111 { "msd", 4, 0, false }, /* Moscow DST */
112 { "msk", 3, 0, false }, /* Moscow */
113 { "mst", 7, 0, true },
114 { "nzdt", 13, 0, false }, /* New Zealand DST */
115 { "nzst", 12, 0, false }, /* New Zealand */
116 { "pdt", 7, 0, true },
117 { "pst", 8, 0, true },
118 { "sat", 2, 0, false }, /* South Africa */
119 { "smt", 4, 0, false }, /* Seychelles */
120 { "sst", 11, 0, true }, /* Samoa */
121//{ "sst", 8, 0, false }, /* Singapore */
122 { "utc", 0, 0, false },
123 { "wat", 0, 0, false }, /* West Africa */
124 { "west", 1, 0, false }, /* Western Europe DST */
125 { "wet", 0, 0, false }, /* Western Europe */
126 { "wgst", 2, 0, true }, /* Western Greenland DST */
127 { "wgt", 3, 0, true }, /* Western Greenland */
128 { "wst", 8, 0, false }, /* Western Australia */
129 // clang-format on
130};