xref: /third_party/curl/lib/parsedate.c (revision 13498266)
113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci/*
2513498266Sopenharmony_ci  A brief summary of the date string formats this parser groks:
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci  RFC 2616 3.3.1
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci  Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
3013498266Sopenharmony_ci  Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
3113498266Sopenharmony_ci  Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
3213498266Sopenharmony_ci
3313498266Sopenharmony_ci  we support dates without week day name:
3413498266Sopenharmony_ci
3513498266Sopenharmony_ci  06 Nov 1994 08:49:37 GMT
3613498266Sopenharmony_ci  06-Nov-94 08:49:37 GMT
3713498266Sopenharmony_ci  Nov  6 08:49:37 1994
3813498266Sopenharmony_ci
3913498266Sopenharmony_ci  without the time zone:
4013498266Sopenharmony_ci
4113498266Sopenharmony_ci  06 Nov 1994 08:49:37
4213498266Sopenharmony_ci  06-Nov-94 08:49:37
4313498266Sopenharmony_ci
4413498266Sopenharmony_ci  weird order:
4513498266Sopenharmony_ci
4613498266Sopenharmony_ci  1994 Nov 6 08:49:37  (GNU date fails)
4713498266Sopenharmony_ci  GMT 08:49:37 06-Nov-94 Sunday
4813498266Sopenharmony_ci  94 6 Nov 08:49:37    (GNU date fails)
4913498266Sopenharmony_ci
5013498266Sopenharmony_ci  time left out:
5113498266Sopenharmony_ci
5213498266Sopenharmony_ci  1994 Nov 6
5313498266Sopenharmony_ci  06-Nov-94
5413498266Sopenharmony_ci  Sun Nov 6 94
5513498266Sopenharmony_ci
5613498266Sopenharmony_ci  unusual separators:
5713498266Sopenharmony_ci
5813498266Sopenharmony_ci  1994.Nov.6
5913498266Sopenharmony_ci  Sun/Nov/6/94/GMT
6013498266Sopenharmony_ci
6113498266Sopenharmony_ci  commonly used time zone names:
6213498266Sopenharmony_ci
6313498266Sopenharmony_ci  Sun, 06 Nov 1994 08:49:37 CET
6413498266Sopenharmony_ci  06 Nov 1994 08:49:37 EST
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci  time zones specified using RFC822 style:
6713498266Sopenharmony_ci
6813498266Sopenharmony_ci  Sun, 12 Sep 2004 15:05:58 -0700
6913498266Sopenharmony_ci  Sat, 11 Sep 2004 21:32:11 +0200
7013498266Sopenharmony_ci
7113498266Sopenharmony_ci  compact numerical date strings:
7213498266Sopenharmony_ci
7313498266Sopenharmony_ci  20040912 15:05:58 -0700
7413498266Sopenharmony_ci  20040911 +0200
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci*/
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci#include "curl_setup.h"
7913498266Sopenharmony_ci
8013498266Sopenharmony_ci#include <limits.h>
8113498266Sopenharmony_ci
8213498266Sopenharmony_ci#include <curl/curl.h>
8313498266Sopenharmony_ci#include "strcase.h"
8413498266Sopenharmony_ci#include "warnless.h"
8513498266Sopenharmony_ci#include "parsedate.h"
8613498266Sopenharmony_ci
8713498266Sopenharmony_ci/*
8813498266Sopenharmony_ci * parsedate()
8913498266Sopenharmony_ci *
9013498266Sopenharmony_ci * Returns:
9113498266Sopenharmony_ci *
9213498266Sopenharmony_ci * PARSEDATE_OK     - a fine conversion
9313498266Sopenharmony_ci * PARSEDATE_FAIL   - failed to convert
9413498266Sopenharmony_ci * PARSEDATE_LATER  - time overflow at the far end of time_t
9513498266Sopenharmony_ci * PARSEDATE_SOONER - time underflow at the low end of time_t
9613498266Sopenharmony_ci */
9713498266Sopenharmony_ci
9813498266Sopenharmony_cistatic int parsedate(const char *date, time_t *output);
9913498266Sopenharmony_ci
10013498266Sopenharmony_ci#define PARSEDATE_OK     0
10113498266Sopenharmony_ci#define PARSEDATE_FAIL   -1
10213498266Sopenharmony_ci#define PARSEDATE_LATER  1
10313498266Sopenharmony_ci#define PARSEDATE_SOONER 2
10413498266Sopenharmony_ci
10513498266Sopenharmony_ci#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \
10613498266Sopenharmony_ci  !defined(CURL_DISABLE_FILE)
10713498266Sopenharmony_ci/* These names are also used by FTP and FILE code */
10813498266Sopenharmony_ciconst char * const Curl_wkday[] =
10913498266Sopenharmony_ci{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
11013498266Sopenharmony_ciconst char * const Curl_month[]=
11113498266Sopenharmony_ci{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
11213498266Sopenharmony_ci  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
11313498266Sopenharmony_ci#endif
11413498266Sopenharmony_ci
11513498266Sopenharmony_ci#ifndef CURL_DISABLE_PARSEDATE
11613498266Sopenharmony_cistatic const char * const weekday[] =
11713498266Sopenharmony_ci{ "Monday", "Tuesday", "Wednesday", "Thursday",
11813498266Sopenharmony_ci  "Friday", "Saturday", "Sunday" };
11913498266Sopenharmony_ci
12013498266Sopenharmony_cistruct tzinfo {
12113498266Sopenharmony_ci  char name[5];
12213498266Sopenharmony_ci  int offset; /* +/- in minutes */
12313498266Sopenharmony_ci};
12413498266Sopenharmony_ci
12513498266Sopenharmony_ci/* Here's a bunch of frequently used time zone names. These were supported
12613498266Sopenharmony_ci   by the old getdate parser. */
12713498266Sopenharmony_ci#define tDAYZONE -60       /* offset for daylight savings time */
12813498266Sopenharmony_cistatic const struct tzinfo tz[]= {
12913498266Sopenharmony_ci  {"GMT", 0},              /* Greenwich Mean */
13013498266Sopenharmony_ci  {"UT",  0},              /* Universal Time */
13113498266Sopenharmony_ci  {"UTC", 0},              /* Universal (Coordinated) */
13213498266Sopenharmony_ci  {"WET", 0},              /* Western European */
13313498266Sopenharmony_ci  {"BST", 0 tDAYZONE},     /* British Summer */
13413498266Sopenharmony_ci  {"WAT", 60},             /* West Africa */
13513498266Sopenharmony_ci  {"AST", 240},            /* Atlantic Standard */
13613498266Sopenharmony_ci  {"ADT", 240 tDAYZONE},   /* Atlantic Daylight */
13713498266Sopenharmony_ci  {"EST", 300},            /* Eastern Standard */
13813498266Sopenharmony_ci  {"EDT", 300 tDAYZONE},   /* Eastern Daylight */
13913498266Sopenharmony_ci  {"CST", 360},            /* Central Standard */
14013498266Sopenharmony_ci  {"CDT", 360 tDAYZONE},   /* Central Daylight */
14113498266Sopenharmony_ci  {"MST", 420},            /* Mountain Standard */
14213498266Sopenharmony_ci  {"MDT", 420 tDAYZONE},   /* Mountain Daylight */
14313498266Sopenharmony_ci  {"PST", 480},            /* Pacific Standard */
14413498266Sopenharmony_ci  {"PDT", 480 tDAYZONE},   /* Pacific Daylight */
14513498266Sopenharmony_ci  {"YST", 540},            /* Yukon Standard */
14613498266Sopenharmony_ci  {"YDT", 540 tDAYZONE},   /* Yukon Daylight */
14713498266Sopenharmony_ci  {"HST", 600},            /* Hawaii Standard */
14813498266Sopenharmony_ci  {"HDT", 600 tDAYZONE},   /* Hawaii Daylight */
14913498266Sopenharmony_ci  {"CAT", 600},            /* Central Alaska */
15013498266Sopenharmony_ci  {"AHST", 600},           /* Alaska-Hawaii Standard */
15113498266Sopenharmony_ci  {"NT",  660},            /* Nome */
15213498266Sopenharmony_ci  {"IDLW", 720},           /* International Date Line West */
15313498266Sopenharmony_ci  {"CET", -60},            /* Central European */
15413498266Sopenharmony_ci  {"MET", -60},            /* Middle European */
15513498266Sopenharmony_ci  {"MEWT", -60},           /* Middle European Winter */
15613498266Sopenharmony_ci  {"MEST", -60 tDAYZONE},  /* Middle European Summer */
15713498266Sopenharmony_ci  {"CEST", -60 tDAYZONE},  /* Central European Summer */
15813498266Sopenharmony_ci  {"MESZ", -60 tDAYZONE},  /* Middle European Summer */
15913498266Sopenharmony_ci  {"FWT", -60},            /* French Winter */
16013498266Sopenharmony_ci  {"FST", -60 tDAYZONE},   /* French Summer */
16113498266Sopenharmony_ci  {"EET", -120},           /* Eastern Europe, USSR Zone 1 */
16213498266Sopenharmony_ci  {"WAST", -420},          /* West Australian Standard */
16313498266Sopenharmony_ci  {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
16413498266Sopenharmony_ci  {"CCT", -480},           /* China Coast, USSR Zone 7 */
16513498266Sopenharmony_ci  {"JST", -540},           /* Japan Standard, USSR Zone 8 */
16613498266Sopenharmony_ci  {"EAST", -600},          /* Eastern Australian Standard */
16713498266Sopenharmony_ci  {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
16813498266Sopenharmony_ci  {"GST", -600},           /* Guam Standard, USSR Zone 9 */
16913498266Sopenharmony_ci  {"NZT", -720},           /* New Zealand */
17013498266Sopenharmony_ci  {"NZST", -720},          /* New Zealand Standard */
17113498266Sopenharmony_ci  {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
17213498266Sopenharmony_ci  {"IDLE", -720},          /* International Date Line East */
17313498266Sopenharmony_ci  /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
17413498266Sopenharmony_ci     RFC 1123) had their signs wrong. Here we use the correct signs to match
17513498266Sopenharmony_ci     actual military usage.
17613498266Sopenharmony_ci   */
17713498266Sopenharmony_ci  {"A",  1 * 60},         /* Alpha */
17813498266Sopenharmony_ci  {"B",  2 * 60},         /* Bravo */
17913498266Sopenharmony_ci  {"C",  3 * 60},         /* Charlie */
18013498266Sopenharmony_ci  {"D",  4 * 60},         /* Delta */
18113498266Sopenharmony_ci  {"E",  5 * 60},         /* Echo */
18213498266Sopenharmony_ci  {"F",  6 * 60},         /* Foxtrot */
18313498266Sopenharmony_ci  {"G",  7 * 60},         /* Golf */
18413498266Sopenharmony_ci  {"H",  8 * 60},         /* Hotel */
18513498266Sopenharmony_ci  {"I",  9 * 60},         /* India */
18613498266Sopenharmony_ci  /* "J", Juliet is not used as a timezone, to indicate the observer's local
18713498266Sopenharmony_ci     time */
18813498266Sopenharmony_ci  {"K", 10 * 60},         /* Kilo */
18913498266Sopenharmony_ci  {"L", 11 * 60},         /* Lima */
19013498266Sopenharmony_ci  {"M", 12 * 60},         /* Mike */
19113498266Sopenharmony_ci  {"N",  -1 * 60},         /* November */
19213498266Sopenharmony_ci  {"O",  -2 * 60},         /* Oscar */
19313498266Sopenharmony_ci  {"P",  -3 * 60},         /* Papa */
19413498266Sopenharmony_ci  {"Q",  -4 * 60},         /* Quebec */
19513498266Sopenharmony_ci  {"R",  -5 * 60},         /* Romeo */
19613498266Sopenharmony_ci  {"S",  -6 * 60},         /* Sierra */
19713498266Sopenharmony_ci  {"T",  -7 * 60},         /* Tango */
19813498266Sopenharmony_ci  {"U",  -8 * 60},         /* Uniform */
19913498266Sopenharmony_ci  {"V",  -9 * 60},         /* Victor */
20013498266Sopenharmony_ci  {"W", -10 * 60},         /* Whiskey */
20113498266Sopenharmony_ci  {"X", -11 * 60},         /* X-ray */
20213498266Sopenharmony_ci  {"Y", -12 * 60},         /* Yankee */
20313498266Sopenharmony_ci  {"Z", 0},                /* Zulu, zero meridian, a.k.a. UTC */
20413498266Sopenharmony_ci};
20513498266Sopenharmony_ci
20613498266Sopenharmony_ci/* returns:
20713498266Sopenharmony_ci   -1 no day
20813498266Sopenharmony_ci   0 monday - 6 sunday
20913498266Sopenharmony_ci*/
21013498266Sopenharmony_ci
21113498266Sopenharmony_cistatic int checkday(const char *check, size_t len)
21213498266Sopenharmony_ci{
21313498266Sopenharmony_ci  int i;
21413498266Sopenharmony_ci  const char * const *what;
21513498266Sopenharmony_ci  if(len > 3)
21613498266Sopenharmony_ci    what = &weekday[0];
21713498266Sopenharmony_ci  else if(len == 3)
21813498266Sopenharmony_ci    what = &Curl_wkday[0];
21913498266Sopenharmony_ci  else
22013498266Sopenharmony_ci    return -1; /* too short */
22113498266Sopenharmony_ci  for(i = 0; i<7; i++) {
22213498266Sopenharmony_ci    size_t ilen = strlen(what[0]);
22313498266Sopenharmony_ci    if((ilen == len) &&
22413498266Sopenharmony_ci       strncasecompare(check, what[0], len))
22513498266Sopenharmony_ci      return i;
22613498266Sopenharmony_ci    what++;
22713498266Sopenharmony_ci  }
22813498266Sopenharmony_ci  return -1;
22913498266Sopenharmony_ci}
23013498266Sopenharmony_ci
23113498266Sopenharmony_cistatic int checkmonth(const char *check, size_t len)
23213498266Sopenharmony_ci{
23313498266Sopenharmony_ci  int i;
23413498266Sopenharmony_ci  const char * const *what = &Curl_month[0];
23513498266Sopenharmony_ci  if(len != 3)
23613498266Sopenharmony_ci    return -1; /* not a month */
23713498266Sopenharmony_ci
23813498266Sopenharmony_ci  for(i = 0; i<12; i++) {
23913498266Sopenharmony_ci    if(strncasecompare(check, what[0], 3))
24013498266Sopenharmony_ci      return i;
24113498266Sopenharmony_ci    what++;
24213498266Sopenharmony_ci  }
24313498266Sopenharmony_ci  return -1; /* return the offset or -1, no real offset is -1 */
24413498266Sopenharmony_ci}
24513498266Sopenharmony_ci
24613498266Sopenharmony_ci/* return the time zone offset between GMT and the input one, in number
24713498266Sopenharmony_ci   of seconds or -1 if the timezone wasn't found/legal */
24813498266Sopenharmony_ci
24913498266Sopenharmony_cistatic int checktz(const char *check, size_t len)
25013498266Sopenharmony_ci{
25113498266Sopenharmony_ci  unsigned int i;
25213498266Sopenharmony_ci  const struct tzinfo *what = tz;
25313498266Sopenharmony_ci  if(len > 4) /* longer than any valid timezone */
25413498266Sopenharmony_ci    return -1;
25513498266Sopenharmony_ci
25613498266Sopenharmony_ci  for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) {
25713498266Sopenharmony_ci    size_t ilen = strlen(what->name);
25813498266Sopenharmony_ci    if((ilen == len) &&
25913498266Sopenharmony_ci       strncasecompare(check, what->name, len))
26013498266Sopenharmony_ci      return what->offset*60;
26113498266Sopenharmony_ci    what++;
26213498266Sopenharmony_ci  }
26313498266Sopenharmony_ci  return -1;
26413498266Sopenharmony_ci}
26513498266Sopenharmony_ci
26613498266Sopenharmony_cistatic void skip(const char **date)
26713498266Sopenharmony_ci{
26813498266Sopenharmony_ci  /* skip everything that aren't letters or digits */
26913498266Sopenharmony_ci  while(**date && !ISALNUM(**date))
27013498266Sopenharmony_ci    (*date)++;
27113498266Sopenharmony_ci}
27213498266Sopenharmony_ci
27313498266Sopenharmony_cienum assume {
27413498266Sopenharmony_ci  DATE_MDAY,
27513498266Sopenharmony_ci  DATE_YEAR,
27613498266Sopenharmony_ci  DATE_TIME
27713498266Sopenharmony_ci};
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci/*
28013498266Sopenharmony_ci * time2epoch: time stamp to seconds since epoch in GMT time zone.  Similar to
28113498266Sopenharmony_ci * mktime but for GMT only.
28213498266Sopenharmony_ci */
28313498266Sopenharmony_cistatic time_t time2epoch(int sec, int min, int hour,
28413498266Sopenharmony_ci                         int mday, int mon, int year)
28513498266Sopenharmony_ci{
28613498266Sopenharmony_ci  static const int month_days_cumulative [12] =
28713498266Sopenharmony_ci    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
28813498266Sopenharmony_ci  int leap_days = year - (mon <= 1);
28913498266Sopenharmony_ci  leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
29013498266Sopenharmony_ci               - (1969 / 4) + (1969 / 100) - (1969 / 400));
29113498266Sopenharmony_ci  return ((((time_t) (year - 1970) * 365
29213498266Sopenharmony_ci            + leap_days + month_days_cumulative[mon] + mday - 1) * 24
29313498266Sopenharmony_ci           + hour) * 60 + min) * 60 + sec;
29413498266Sopenharmony_ci}
29513498266Sopenharmony_ci
29613498266Sopenharmony_ci/* Returns the value of a single-digit or two-digit decimal number, return
29713498266Sopenharmony_ci   then pointer to after the number. The 'date' pointer is known to point to a
29813498266Sopenharmony_ci   digit. */
29913498266Sopenharmony_cistatic int oneortwodigit(const char *date, const char **endp)
30013498266Sopenharmony_ci{
30113498266Sopenharmony_ci  int num = date[0] - '0';
30213498266Sopenharmony_ci  if(ISDIGIT(date[1])) {
30313498266Sopenharmony_ci    *endp = &date[2];
30413498266Sopenharmony_ci    return num*10 + (date[1] - '0');
30513498266Sopenharmony_ci  }
30613498266Sopenharmony_ci  *endp = &date[1];
30713498266Sopenharmony_ci  return num;
30813498266Sopenharmony_ci}
30913498266Sopenharmony_ci
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci/* HH:MM:SS or HH:MM and accept single-digits too */
31213498266Sopenharmony_cistatic bool match_time(const char *date,
31313498266Sopenharmony_ci                       int *h, int *m, int *s, char **endp)
31413498266Sopenharmony_ci{
31513498266Sopenharmony_ci  const char *p;
31613498266Sopenharmony_ci  int hh, mm, ss = 0;
31713498266Sopenharmony_ci  hh = oneortwodigit(date, &p);
31813498266Sopenharmony_ci  if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) {
31913498266Sopenharmony_ci    mm = oneortwodigit(&p[1], &p);
32013498266Sopenharmony_ci    if(mm < 60) {
32113498266Sopenharmony_ci      if((*p == ':') && ISDIGIT(p[1])) {
32213498266Sopenharmony_ci        ss = oneortwodigit(&p[1], &p);
32313498266Sopenharmony_ci        if(ss <= 60) {
32413498266Sopenharmony_ci          /* valid HH:MM:SS */
32513498266Sopenharmony_ci          goto match;
32613498266Sopenharmony_ci        }
32713498266Sopenharmony_ci      }
32813498266Sopenharmony_ci      else {
32913498266Sopenharmony_ci        /* valid HH:MM */
33013498266Sopenharmony_ci        goto match;
33113498266Sopenharmony_ci      }
33213498266Sopenharmony_ci    }
33313498266Sopenharmony_ci  }
33413498266Sopenharmony_ci  return FALSE; /* not a time string */
33513498266Sopenharmony_cimatch:
33613498266Sopenharmony_ci  *h = hh;
33713498266Sopenharmony_ci  *m = mm;
33813498266Sopenharmony_ci  *s = ss;
33913498266Sopenharmony_ci  *endp = (char *)p;
34013498266Sopenharmony_ci  return TRUE;
34113498266Sopenharmony_ci}
34213498266Sopenharmony_ci
34313498266Sopenharmony_ci/*
34413498266Sopenharmony_ci * parsedate()
34513498266Sopenharmony_ci *
34613498266Sopenharmony_ci * Returns:
34713498266Sopenharmony_ci *
34813498266Sopenharmony_ci * PARSEDATE_OK     - a fine conversion
34913498266Sopenharmony_ci * PARSEDATE_FAIL   - failed to convert
35013498266Sopenharmony_ci * PARSEDATE_LATER  - time overflow at the far end of time_t
35113498266Sopenharmony_ci * PARSEDATE_SOONER - time underflow at the low end of time_t
35213498266Sopenharmony_ci */
35313498266Sopenharmony_ci
35413498266Sopenharmony_ci/* Wednesday is the longest name this parser knows about */
35513498266Sopenharmony_ci#define NAME_LEN 12
35613498266Sopenharmony_ci
35713498266Sopenharmony_cistatic int parsedate(const char *date, time_t *output)
35813498266Sopenharmony_ci{
35913498266Sopenharmony_ci  time_t t = 0;
36013498266Sopenharmony_ci  int wdaynum = -1;  /* day of the week number, 0-6 (mon-sun) */
36113498266Sopenharmony_ci  int monnum = -1;   /* month of the year number, 0-11 */
36213498266Sopenharmony_ci  int mdaynum = -1; /* day of month, 1 - 31 */
36313498266Sopenharmony_ci  int hournum = -1;
36413498266Sopenharmony_ci  int minnum = -1;
36513498266Sopenharmony_ci  int secnum = -1;
36613498266Sopenharmony_ci  int yearnum = -1;
36713498266Sopenharmony_ci  int tzoff = -1;
36813498266Sopenharmony_ci  enum assume dignext = DATE_MDAY;
36913498266Sopenharmony_ci  const char *indate = date; /* save the original pointer */
37013498266Sopenharmony_ci  int part = 0; /* max 6 parts */
37113498266Sopenharmony_ci
37213498266Sopenharmony_ci  while(*date && (part < 6)) {
37313498266Sopenharmony_ci    bool found = FALSE;
37413498266Sopenharmony_ci
37513498266Sopenharmony_ci    skip(&date);
37613498266Sopenharmony_ci
37713498266Sopenharmony_ci    if(ISALPHA(*date)) {
37813498266Sopenharmony_ci      /* a name coming up */
37913498266Sopenharmony_ci      size_t len = 0;
38013498266Sopenharmony_ci      const char *p = date;
38113498266Sopenharmony_ci      while(ISALPHA(*p) && (len < NAME_LEN)) {
38213498266Sopenharmony_ci        p++;
38313498266Sopenharmony_ci        len++;
38413498266Sopenharmony_ci      }
38513498266Sopenharmony_ci
38613498266Sopenharmony_ci      if(len != NAME_LEN) {
38713498266Sopenharmony_ci        if(wdaynum == -1) {
38813498266Sopenharmony_ci          wdaynum = checkday(date, len);
38913498266Sopenharmony_ci          if(wdaynum != -1)
39013498266Sopenharmony_ci            found = TRUE;
39113498266Sopenharmony_ci        }
39213498266Sopenharmony_ci        if(!found && (monnum == -1)) {
39313498266Sopenharmony_ci          monnum = checkmonth(date, len);
39413498266Sopenharmony_ci          if(monnum != -1)
39513498266Sopenharmony_ci            found = TRUE;
39613498266Sopenharmony_ci        }
39713498266Sopenharmony_ci
39813498266Sopenharmony_ci        if(!found && (tzoff == -1)) {
39913498266Sopenharmony_ci          /* this just must be a time zone string */
40013498266Sopenharmony_ci          tzoff = checktz(date, len);
40113498266Sopenharmony_ci          if(tzoff != -1)
40213498266Sopenharmony_ci            found = TRUE;
40313498266Sopenharmony_ci        }
40413498266Sopenharmony_ci      }
40513498266Sopenharmony_ci      if(!found)
40613498266Sopenharmony_ci        return PARSEDATE_FAIL; /* bad string */
40713498266Sopenharmony_ci
40813498266Sopenharmony_ci      date += len;
40913498266Sopenharmony_ci    }
41013498266Sopenharmony_ci    else if(ISDIGIT(*date)) {
41113498266Sopenharmony_ci      /* a digit */
41213498266Sopenharmony_ci      int val;
41313498266Sopenharmony_ci      char *end;
41413498266Sopenharmony_ci      if((secnum == -1) &&
41513498266Sopenharmony_ci         match_time(date, &hournum, &minnum, &secnum, &end)) {
41613498266Sopenharmony_ci        /* time stamp */
41713498266Sopenharmony_ci        date = end;
41813498266Sopenharmony_ci      }
41913498266Sopenharmony_ci      else {
42013498266Sopenharmony_ci        long lval;
42113498266Sopenharmony_ci        int error;
42213498266Sopenharmony_ci        int old_errno;
42313498266Sopenharmony_ci
42413498266Sopenharmony_ci        old_errno = errno;
42513498266Sopenharmony_ci        errno = 0;
42613498266Sopenharmony_ci        lval = strtol(date, &end, 10);
42713498266Sopenharmony_ci        error = errno;
42813498266Sopenharmony_ci        if(errno != old_errno)
42913498266Sopenharmony_ci          errno = old_errno;
43013498266Sopenharmony_ci
43113498266Sopenharmony_ci        if(error)
43213498266Sopenharmony_ci          return PARSEDATE_FAIL;
43313498266Sopenharmony_ci
43413498266Sopenharmony_ci#if LONG_MAX != INT_MAX
43513498266Sopenharmony_ci        if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
43613498266Sopenharmony_ci          return PARSEDATE_FAIL;
43713498266Sopenharmony_ci#endif
43813498266Sopenharmony_ci
43913498266Sopenharmony_ci        val = curlx_sltosi(lval);
44013498266Sopenharmony_ci
44113498266Sopenharmony_ci        if((tzoff == -1) &&
44213498266Sopenharmony_ci           ((end - date) == 4) &&
44313498266Sopenharmony_ci           (val <= 1400) &&
44413498266Sopenharmony_ci           (indate< date) &&
44513498266Sopenharmony_ci           ((date[-1] == '+' || date[-1] == '-'))) {
44613498266Sopenharmony_ci          /* four digits and a value less than or equal to 1400 (to take into
44713498266Sopenharmony_ci             account all sorts of funny time zone diffs) and it is preceded
44813498266Sopenharmony_ci             with a plus or minus. This is a time zone indication.  1400 is
44913498266Sopenharmony_ci             picked since +1300 is frequently used and +1400 is mentioned as
45013498266Sopenharmony_ci             an edge number in the document "ISO C 200X Proposal: Timezone
45113498266Sopenharmony_ci             Functions" at http://david.tribble.com/text/c0xtimezone.html If
45213498266Sopenharmony_ci             anyone has a more authoritative source for the exact maximum time
45313498266Sopenharmony_ci             zone offsets, please speak up! */
45413498266Sopenharmony_ci          found = TRUE;
45513498266Sopenharmony_ci          tzoff = (val/100 * 60 + val%100)*60;
45613498266Sopenharmony_ci
45713498266Sopenharmony_ci          /* the + and - prefix indicates the local time compared to GMT,
45813498266Sopenharmony_ci             this we need their reversed math to get what we want */
45913498266Sopenharmony_ci          tzoff = date[-1]=='+'?-tzoff:tzoff;
46013498266Sopenharmony_ci        }
46113498266Sopenharmony_ci
46213498266Sopenharmony_ci        if(((end - date) == 8) &&
46313498266Sopenharmony_ci           (yearnum == -1) &&
46413498266Sopenharmony_ci           (monnum == -1) &&
46513498266Sopenharmony_ci           (mdaynum == -1)) {
46613498266Sopenharmony_ci          /* 8 digits, no year, month or day yet. This is YYYYMMDD */
46713498266Sopenharmony_ci          found = TRUE;
46813498266Sopenharmony_ci          yearnum = val/10000;
46913498266Sopenharmony_ci          monnum = (val%10000)/100-1; /* month is 0 - 11 */
47013498266Sopenharmony_ci          mdaynum = val%100;
47113498266Sopenharmony_ci        }
47213498266Sopenharmony_ci
47313498266Sopenharmony_ci        if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
47413498266Sopenharmony_ci          if((val > 0) && (val<32)) {
47513498266Sopenharmony_ci            mdaynum = val;
47613498266Sopenharmony_ci            found = TRUE;
47713498266Sopenharmony_ci          }
47813498266Sopenharmony_ci          dignext = DATE_YEAR;
47913498266Sopenharmony_ci        }
48013498266Sopenharmony_ci
48113498266Sopenharmony_ci        if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
48213498266Sopenharmony_ci          yearnum = val;
48313498266Sopenharmony_ci          found = TRUE;
48413498266Sopenharmony_ci          if(yearnum < 100) {
48513498266Sopenharmony_ci            if(yearnum > 70)
48613498266Sopenharmony_ci              yearnum += 1900;
48713498266Sopenharmony_ci            else
48813498266Sopenharmony_ci              yearnum += 2000;
48913498266Sopenharmony_ci          }
49013498266Sopenharmony_ci          if(mdaynum == -1)
49113498266Sopenharmony_ci            dignext = DATE_MDAY;
49213498266Sopenharmony_ci        }
49313498266Sopenharmony_ci
49413498266Sopenharmony_ci        if(!found)
49513498266Sopenharmony_ci          return PARSEDATE_FAIL;
49613498266Sopenharmony_ci
49713498266Sopenharmony_ci        date = end;
49813498266Sopenharmony_ci      }
49913498266Sopenharmony_ci    }
50013498266Sopenharmony_ci
50113498266Sopenharmony_ci    part++;
50213498266Sopenharmony_ci  }
50313498266Sopenharmony_ci
50413498266Sopenharmony_ci  if(-1 == secnum)
50513498266Sopenharmony_ci    secnum = minnum = hournum = 0; /* no time, make it zero */
50613498266Sopenharmony_ci
50713498266Sopenharmony_ci  if((-1 == mdaynum) ||
50813498266Sopenharmony_ci     (-1 == monnum) ||
50913498266Sopenharmony_ci     (-1 == yearnum))
51013498266Sopenharmony_ci    /* lacks vital info, fail */
51113498266Sopenharmony_ci    return PARSEDATE_FAIL;
51213498266Sopenharmony_ci
51313498266Sopenharmony_ci#ifdef HAVE_TIME_T_UNSIGNED
51413498266Sopenharmony_ci  if(yearnum < 1970) {
51513498266Sopenharmony_ci    /* only positive numbers cannot return earlier */
51613498266Sopenharmony_ci    *output = TIME_T_MIN;
51713498266Sopenharmony_ci    return PARSEDATE_SOONER;
51813498266Sopenharmony_ci  }
51913498266Sopenharmony_ci#endif
52013498266Sopenharmony_ci
52113498266Sopenharmony_ci#if (SIZEOF_TIME_T < 5)
52213498266Sopenharmony_ci
52313498266Sopenharmony_ci#ifdef HAVE_TIME_T_UNSIGNED
52413498266Sopenharmony_ci  /* an unsigned 32 bit time_t can only hold dates to 2106 */
52513498266Sopenharmony_ci  if(yearnum > 2105) {
52613498266Sopenharmony_ci    *output = TIME_T_MAX;
52713498266Sopenharmony_ci    return PARSEDATE_LATER;
52813498266Sopenharmony_ci  }
52913498266Sopenharmony_ci#else
53013498266Sopenharmony_ci  /* a signed 32 bit time_t can only hold dates to the beginning of 2038 */
53113498266Sopenharmony_ci  if(yearnum > 2037) {
53213498266Sopenharmony_ci    *output = TIME_T_MAX;
53313498266Sopenharmony_ci    return PARSEDATE_LATER;
53413498266Sopenharmony_ci  }
53513498266Sopenharmony_ci  if(yearnum < 1903) {
53613498266Sopenharmony_ci    *output = TIME_T_MIN;
53713498266Sopenharmony_ci    return PARSEDATE_SOONER;
53813498266Sopenharmony_ci  }
53913498266Sopenharmony_ci#endif
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci#else
54213498266Sopenharmony_ci  /* The Gregorian calendar was introduced 1582 */
54313498266Sopenharmony_ci  if(yearnum < 1583)
54413498266Sopenharmony_ci    return PARSEDATE_FAIL;
54513498266Sopenharmony_ci#endif
54613498266Sopenharmony_ci
54713498266Sopenharmony_ci  if((mdaynum > 31) || (monnum > 11) ||
54813498266Sopenharmony_ci     (hournum > 23) || (minnum > 59) || (secnum > 60))
54913498266Sopenharmony_ci    return PARSEDATE_FAIL; /* clearly an illegal date */
55013498266Sopenharmony_ci
55113498266Sopenharmony_ci  /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on
55213498266Sopenharmony_ci     architectures that feature 64 bit 'long' but ultimately time_t is the
55313498266Sopenharmony_ci     correct data type to use.
55413498266Sopenharmony_ci  */
55513498266Sopenharmony_ci  t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum);
55613498266Sopenharmony_ci
55713498266Sopenharmony_ci  /* Add the time zone diff between local time zone and GMT. */
55813498266Sopenharmony_ci  if(tzoff == -1)
55913498266Sopenharmony_ci    tzoff = 0;
56013498266Sopenharmony_ci
56113498266Sopenharmony_ci  if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) {
56213498266Sopenharmony_ci    *output = TIME_T_MAX;
56313498266Sopenharmony_ci    return PARSEDATE_LATER; /* time_t overflow */
56413498266Sopenharmony_ci  }
56513498266Sopenharmony_ci
56613498266Sopenharmony_ci  t += tzoff;
56713498266Sopenharmony_ci
56813498266Sopenharmony_ci  *output = t;
56913498266Sopenharmony_ci
57013498266Sopenharmony_ci  return PARSEDATE_OK;
57113498266Sopenharmony_ci}
57213498266Sopenharmony_ci#else
57313498266Sopenharmony_ci/* disabled */
57413498266Sopenharmony_cistatic int parsedate(const char *date, time_t *output)
57513498266Sopenharmony_ci{
57613498266Sopenharmony_ci  (void)date;
57713498266Sopenharmony_ci  *output = 0;
57813498266Sopenharmony_ci  return PARSEDATE_OK; /* a lie */
57913498266Sopenharmony_ci}
58013498266Sopenharmony_ci#endif
58113498266Sopenharmony_ci
58213498266Sopenharmony_citime_t curl_getdate(const char *p, const time_t *now)
58313498266Sopenharmony_ci{
58413498266Sopenharmony_ci  time_t parsed = -1;
58513498266Sopenharmony_ci  int rc = parsedate(p, &parsed);
58613498266Sopenharmony_ci  (void)now; /* legacy argument from the past that we ignore */
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci  if(rc == PARSEDATE_OK) {
58913498266Sopenharmony_ci    if(parsed == -1)
59013498266Sopenharmony_ci      /* avoid returning -1 for a working scenario */
59113498266Sopenharmony_ci      parsed++;
59213498266Sopenharmony_ci    return parsed;
59313498266Sopenharmony_ci  }
59413498266Sopenharmony_ci  /* everything else is fail */
59513498266Sopenharmony_ci  return -1;
59613498266Sopenharmony_ci}
59713498266Sopenharmony_ci
59813498266Sopenharmony_ci/* Curl_getdate_capped() differs from curl_getdate() in that this will return
59913498266Sopenharmony_ci   TIME_T_MAX in case the parsed time value was too big, instead of an
60013498266Sopenharmony_ci   error. */
60113498266Sopenharmony_ci
60213498266Sopenharmony_citime_t Curl_getdate_capped(const char *p)
60313498266Sopenharmony_ci{
60413498266Sopenharmony_ci  time_t parsed = -1;
60513498266Sopenharmony_ci  int rc = parsedate(p, &parsed);
60613498266Sopenharmony_ci
60713498266Sopenharmony_ci  switch(rc) {
60813498266Sopenharmony_ci  case PARSEDATE_OK:
60913498266Sopenharmony_ci    if(parsed == -1)
61013498266Sopenharmony_ci      /* avoid returning -1 for a working scenario */
61113498266Sopenharmony_ci      parsed++;
61213498266Sopenharmony_ci    return parsed;
61313498266Sopenharmony_ci  case PARSEDATE_LATER:
61413498266Sopenharmony_ci    /* this returns the maximum time value */
61513498266Sopenharmony_ci    return parsed;
61613498266Sopenharmony_ci  default:
61713498266Sopenharmony_ci    return -1; /* everything else is fail */
61813498266Sopenharmony_ci  }
61913498266Sopenharmony_ci  /* UNREACHABLE */
62013498266Sopenharmony_ci}
62113498266Sopenharmony_ci
62213498266Sopenharmony_ci/*
62313498266Sopenharmony_ci * Curl_gmtime() is a gmtime() replacement for portability. Do not use the
62413498266Sopenharmony_ci * gmtime_r() or gmtime() functions anywhere else but here.
62513498266Sopenharmony_ci *
62613498266Sopenharmony_ci */
62713498266Sopenharmony_ci
62813498266Sopenharmony_ciCURLcode Curl_gmtime(time_t intime, struct tm *store)
62913498266Sopenharmony_ci{
63013498266Sopenharmony_ci  const struct tm *tm;
63113498266Sopenharmony_ci#ifdef HAVE_GMTIME_R
63213498266Sopenharmony_ci  /* thread-safe version */
63313498266Sopenharmony_ci  tm = (struct tm *)gmtime_r(&intime, store);
63413498266Sopenharmony_ci#else
63513498266Sopenharmony_ci  /* !checksrc! disable BANNEDFUNC 1 */
63613498266Sopenharmony_ci  tm = gmtime(&intime);
63713498266Sopenharmony_ci  if(tm)
63813498266Sopenharmony_ci    *store = *tm; /* copy the pointed struct to the local copy */
63913498266Sopenharmony_ci#endif
64013498266Sopenharmony_ci
64113498266Sopenharmony_ci  if(!tm)
64213498266Sopenharmony_ci    return CURLE_BAD_FUNCTION_ARGUMENT;
64313498266Sopenharmony_ci  return CURLE_OK;
64413498266Sopenharmony_ci}
645