NeoMutt  2025-09-05-55-g97fc89
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
  • наб

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 139 of file date.c.

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

172{
173 if ((t != TIME_T_MAX) && (t != TIME_T_MIN))
174 return t + (w ? 1 : -1) * (((time_t) h * 3600) + ((time_t) m * 60));
175 else
176 return t;
177}
#define TIME_T_MAX
Definition date.h:40
#define TIME_T_MIN
Definition date.h:41
+ 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 186 of file date.c.

187{
188 for (size_t i = 0; i < countof(TimeZones); i++)
189 {
190 if (mutt_istrn_equal(TimeZones[i].tzname, s, len))
191 return &TimeZones[i];
192 }
193 return NULL;
194}
#define countof(x)
Definition memory.h:44
static const struct Tz TimeZones[]
Lookup table of Time Zones.
Definition date.c:76
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:455
+ 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 201 of file date.c.

202{
203 if (tm->tm_mon != 1)
204 return 0;
205
206 int y = tm->tm_year + 1900;
207 return ((y & 3) == 0) && (((y % 100) != 0) || ((y % 400) == 0));
208}
+ 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 218 of file date.c.

219{
220 /* Check we haven't overflowed the time (on 32-bit arches) */
221 if ((t == TIME_T_MAX) || (t == TIME_T_MIN))
222 return 0;
223
224 if (t == 0)
225 t = mutt_date_now();
226
227 struct tm tm = mutt_date_gmtime(t);
228 return compute_tz(t, &tm);
229}
static int compute_tz(time_t g, struct tm *utc)
Calculate the number of seconds east of UTC.
Definition date.c:139
struct tm mutt_date_gmtime(time_t t)
Converts calendar time to a broken-down time structure expressed in UTC timezone.
Definition date.c:926
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:455
+ 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 241 of file date.c.

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

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

397{
398 if (!buf)
399 return;
400
401 struct tm tm = { 0 };
402 int tz = 0;
403
404 time_t t = mutt_date_now();
405 if (local)
406 {
407 tm = mutt_date_localtime(t);
408 tz = mutt_date_local_tz(t);
409 }
410 else
411 {
412 tm = mutt_date_gmtime(t);
413 }
414
415 tz /= 60;
416
417 buf_add_printf(buf, "%s, %d %s %d %02d:%02d:%02d %+03d%02d", Weekdays[tm.tm_wday],
418 tm.tm_mday, Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour,
419 tm.tm_min, tm.tm_sec, tz / 60, abs(tz) % 60);
420}
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:59
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:218
+ 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 431 of file date.c.

432{
433 if (!s)
434 return -1;
435
436 char buf[4] = { 0 };
437 memcpy(buf, s, 3);
438 uint32_t sv;
439 memcpy(&sv, buf, sizeof(sv));
440 for (int i = 0; i < countof(Months); i++)
441 {
442 uint32_t mv;
443 memcpy(&mv, Months[i], sizeof(mv));
444 if (sv == mv)
445 return i;
446 }
447
448 return -1; /* error */
449}
+ 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 455 of file date.c.

456{
457 return mutt_date_now_ms() / 1000;
458}
uint64_t mutt_date_now_ms(void)
Return the number of milliseconds since the Unix epoch.
Definition date.c:464
+ 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 464 of file date.c.

465{
466 struct timeval tv = { 0, 0 };
467 gettimeofday(&tv, NULL);
468 /* We assume that gettimeofday doesn't modify its first argument on failure.
469 * We also kind of assume that gettimeofday does not fail. */
470 return ((uint64_t) tv.tv_sec * 1000) + (tv.tv_usec / 1000);
471}
+ 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 479 of file date.c.

480{
481#ifdef HAVE_CLOCK_GETTIME
482 if (clock_gettime(CLOCK_REALTIME, tp) != 0)
483 mutt_perror("clock_gettime");
484#else
485 struct timeval tv = { 0, 0 };
486 if (gettimeofday(&tv, NULL) != 0)
487 mutt_perror("gettimeofday");
488 tp->tv_sec = tv.tv_sec;
489 tp->tv_nsec = tv.tv_usec * 1000;
490#endif
491}
#define mutt_perror(...)
Definition logging2.h:94
+ 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 506 of file date.c.

507{
508 const char *ptr = str;
509 int v = 0;
510 while ((ptr < end) && (ptr < (str + 5)) && (*ptr >= '0') && (*ptr <= '9'))
511 {
512 v = (v * 10) + (*ptr - '0');
513 ptr++;
514 }
515 *val = v;
516 return ptr - str;
517}
+ 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 536 of file date.c.

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

716{
717 if (!s)
718 return -1;
719
720 const time_t strict_t = mutt_date_parse_rfc5322_strict(s, tz_out);
721 if (strict_t != -1)
722 return strict_t;
723
724 const regmatch_t *match = mutt_prex_capture(PREX_RFC5322_DATE_LAX, s);
725 if (!match)
726 {
727 mutt_debug(LL_DEBUG1, "Could not parse date: <%s>\n", s);
728 return -1;
729 }
730 mutt_debug(LL_DEBUG2, "Fallback regex for date: <%s>\n", s);
731
732 struct tm tm = { 0 };
733
734 // clang-format off
735 const regmatch_t *mday = &match[PREX_RFC5322_DATE_LAX_MATCH_DAY];
736 const regmatch_t *mmonth = &match[PREX_RFC5322_DATE_LAX_MATCH_MONTH];
737 const regmatch_t *myear = &match[PREX_RFC5322_DATE_LAX_MATCH_YEAR];
738 const regmatch_t *mhour = &match[PREX_RFC5322_DATE_LAX_MATCH_HOUR];
739 const regmatch_t *mminute = &match[PREX_RFC5322_DATE_LAX_MATCH_MINUTE];
740 const regmatch_t *msecond = &match[PREX_RFC5322_DATE_LAX_MATCH_SECOND];
741 const regmatch_t *mtz = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ];
742 const regmatch_t *mtzobs = &match[PREX_RFC5322_DATE_LAX_MATCH_TZ_OBS];
743 // clang-format on
744
745 /* Day */
746 sscanf(s + mutt_regmatch_start(mday), "%d", &tm.tm_mday);
747 if (tm.tm_mday > 31)
748 return -1;
749
750 /* Month */
751 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
752
753 /* Year */
754 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
755 if (tm.tm_year < 50)
756 tm.tm_year += 100;
757 else if (tm.tm_year >= 1900)
758 tm.tm_year -= 1900;
759
760 /* Time */
761 int hour = 0, min = 0, sec = 0;
762 sscanf(s + mutt_regmatch_start(mhour), "%d", &hour);
763 sscanf(s + mutt_regmatch_start(mminute), "%d", &min);
764 if (mutt_regmatch_start(msecond) != -1)
765 sscanf(s + mutt_regmatch_start(msecond), "%d", &sec);
766 if ((hour > 23) || (min > 59) || (sec > 60))
767 return -1;
768 tm.tm_hour = hour;
769 tm.tm_min = min;
770 tm.tm_sec = sec;
771
772 /* Time zone */
773 int zhours = 0;
774 int zminutes = 0;
775 bool zoccident = false;
776 if (mutt_regmatch_start(mtz) != -1)
777 {
778 char direction = '\0';
779 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
780 zoccident = (direction == '-');
781 }
782 else if (mutt_regmatch_start(mtzobs) != -1)
783 {
784 const struct Tz *tz = find_tz(s + mutt_regmatch_start(mtzobs),
785 mutt_regmatch_len(mtzobs));
786 if (tz)
787 {
788 zhours = tz->zhours;
789 zminutes = tz->zminutes;
790 zoccident = tz->zoccident;
791 }
792 }
793
794 if (tz_out)
795 {
796 tz_out->zhours = zhours;
797 tz_out->zminutes = zminutes;
798 tz_out->zoccident = zoccident;
799 }
800
802}
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:44
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:536
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition prex.c:297
@ 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:77
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition regex3.h:57
+ 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 810 of file date.c.

811{
812 if (!buf)
813 return -1;
814
815 struct tm tm = mutt_date_localtime(timestamp);
817
818 tz /= 60;
819
820 return buf_printf(buf, "%02d-%s-%d %02d:%02d:%02d %+03d%02d", tm.tm_mday,
821 Months[tm.tm_mon], tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
822 tm.tm_sec, tz / 60, abs(tz) % 60);
823}
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:78
+ 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 836 of file date.c.

837{
838 if (!buf)
839 return -1;
840
841 struct tm tm = mutt_date_gmtime(timestamp);
842 return snprintf(buf, buflen, "%s, %d %s %d %02d:%02d:%02d UTC",
843 Weekdays[tm.tm_wday], tm.tm_mday, Months[tm.tm_mon],
844 tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
845}
+ 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 853 of file date.c.

854{
855 const regmatch_t *match = mutt_prex_capture(PREX_IMAP_DATE, s);
856 if (!match)
857 return 0;
858
859 const regmatch_t *mday = &match[PREX_IMAP_DATE_MATCH_DAY];
860 const regmatch_t *mmonth = &match[PREX_IMAP_DATE_MATCH_MONTH];
861 const regmatch_t *myear = &match[PREX_IMAP_DATE_MATCH_YEAR];
862 const regmatch_t *mtime = &match[PREX_IMAP_DATE_MATCH_TIME];
863 const regmatch_t *mtz = &match[PREX_IMAP_DATE_MATCH_TZ];
864
865 struct tm tm = { 0 };
866
867 sscanf(s + mutt_regmatch_start(mday), " %d", &tm.tm_mday);
868 tm.tm_mon = mutt_date_check_month(s + mutt_regmatch_start(mmonth));
869 sscanf(s + mutt_regmatch_start(myear), "%d", &tm.tm_year);
870 tm.tm_year -= 1900;
871 sscanf(s + mutt_regmatch_start(mtime), "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
872
873 char direction = '\0';
874 int zhours = 0, zminutes = 0;
875 sscanf(s + mutt_regmatch_start(mtz), "%c%02d%02d", &direction, &zhours, &zminutes);
876 bool zoccident = (direction == '-');
877
878 return add_tz_offset(mutt_date_make_time(&tm, false), zoccident, zhours, zminutes);
879}
@ 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 889 of file date.c.

890{
891 if (timeout < 0)
892 return now;
893
894 if ((TIME_T_MAX - now) < timeout)
895 return TIME_T_MAX;
896
897 return now + timeout;
898}
+ 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 905 of file date.c.

906{
907 struct tm tm = { 0 };
908
909 struct tm *ret = localtime_r(&t, &tm);
910 if (!ret)
911 {
912 mutt_debug(LL_DEBUG1, "Could not convert time_t via localtime_r() to struct tm: time_t = %jd\n",
913 (intmax_t) t);
914 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
915 mktime(&default_tm); // update derived fields making tm into a valid tm.
916 tm = default_tm;
917 }
918 return tm;
919}
+ 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 926 of file date.c.

927{
928 struct tm tm = { 0 };
929
930 struct tm *ret = gmtime_r(&t, &tm);
931 if (!ret)
932 {
933 mutt_debug(LL_DEBUG1, "Could not convert time_t via gmtime_r() to struct tm: time_t = %jd\n",
934 (intmax_t) t);
935 struct tm default_tm = { 0 }; // 1970-01-01 00:00:00
936 mktime(&default_tm); // update derived fields making tm into a valid tm.
937 tm = default_tm;
938 }
939 return tm;
940}
+ 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 950 of file date.c.

951{
952 if (!buf || !format)
953 return 0;
954
955 struct tm tm = mutt_date_localtime(t);
956 return strftime(buf, buflen, format, &tm);
957}
+ 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 968 of file date.c.

970{
971 if (!buf || !format)
972 return 0;
973
974 struct tm tm = mutt_date_localtime(t);
975 return strftime_l(buf, buflen, format, &tm, loc);
976}
+ 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 982 of file date.c.

983{
984 const struct timespec sleep = {
985 .tv_sec = ms / 1000,
986 .tv_nsec = (ms % 1000) * 1000000UL,
987 };
988 nanosleep(&sleep, NULL);
989}
+ 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 59 of file date.c.

59 {
60 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
61};

◆ 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 66 of file date.c.

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

◆ TimeZones

const struct Tz TimeZones[]
static

Lookup table of Time Zones.

Note
Keep in alphabetical order

Definition at line 76 of file date.c.

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