mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-23 12:36:51 +03:00
1008 lines
28 KiB
C
1008 lines
28 KiB
C
/*
|
|
* Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
|
|
* Copyright 2004-2007 James Bursa <bursa@users.sourceforge.net>
|
|
* Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
|
|
* Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
|
|
* Copyright 2004 John Tytgat <joty@netsurf-browser.org>
|
|
*
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
*
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* NetSurf 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/>.
|
|
*/
|
|
|
|
/**
|
|
* \file utils/time.c
|
|
* \brief Implementation of time operations.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
#ifdef WITH_CURL
|
|
#include <curl/curl.h>
|
|
#endif
|
|
|
|
#include "utils/ascii.h"
|
|
#include "utils/errors.h"
|
|
#include "utils/time.h"
|
|
|
|
|
|
/**
|
|
* Weekdays
|
|
*
|
|
* Must be calender order.
|
|
*/
|
|
enum nsc_time_weekdays {
|
|
NSC_TIME_WEEKDAY_SUN,
|
|
NSC_TIME_WEEKDAY_MON,
|
|
NSC_TIME_WEEKDAY_TUE,
|
|
NSC_TIME_WEEKDAY_WED,
|
|
NSC_TIME_WEEKDAY_THU,
|
|
NSC_TIME_WEEKDAY_FRI,
|
|
NSC_TIME_WEEKDAY_SAT,
|
|
NSC_TIME_WEEKDAY__COUNT
|
|
};
|
|
/**
|
|
* Months
|
|
*
|
|
* Must be calender order.
|
|
*/
|
|
enum nsc_time_months {
|
|
NSC_TIME_MONTH_JAN,
|
|
NSC_TIME_MONTH_FEB,
|
|
NSC_TIME_MONTH_MAR,
|
|
NSC_TIME_MONTH_APR,
|
|
NSC_TIME_MONTH_MAY,
|
|
NSC_TIME_MONTH_JUN,
|
|
NSC_TIME_MONTH_JUL,
|
|
NSC_TIME_MONTH_AUG,
|
|
NSC_TIME_MONTH_SEP,
|
|
NSC_TIME_MONTH_OCT,
|
|
NSC_TIME_MONTH_NOV,
|
|
NSC_TIME_MONTH_DEC,
|
|
NSC_TIME_MONTH__COUNT,
|
|
};
|
|
|
|
|
|
/**
|
|
* Array of short weekday names.
|
|
*/
|
|
static const char * const weekdays_short[NSC_TIME_WEEKDAY__COUNT] = {
|
|
[NSC_TIME_WEEKDAY_SUN] = "Sun",
|
|
[NSC_TIME_WEEKDAY_MON] = "Mon",
|
|
[NSC_TIME_WEEKDAY_TUE] = "Tue",
|
|
[NSC_TIME_WEEKDAY_WED] = "Wed",
|
|
[NSC_TIME_WEEKDAY_THU] = "Thu",
|
|
[NSC_TIME_WEEKDAY_FRI] = "Fri",
|
|
[NSC_TIME_WEEKDAY_SAT] = "Sat"
|
|
};
|
|
/**
|
|
* Array of month names.
|
|
*/
|
|
static const char * const months[NSC_TIME_MONTH__COUNT] = {
|
|
[NSC_TIME_MONTH_JAN] = "Jan",
|
|
[NSC_TIME_MONTH_FEB] = "Feb",
|
|
[NSC_TIME_MONTH_MAR] = "Mar",
|
|
[NSC_TIME_MONTH_APR] = "Apr",
|
|
[NSC_TIME_MONTH_MAY] = "May",
|
|
[NSC_TIME_MONTH_JUN] = "Jun",
|
|
[NSC_TIME_MONTH_JUL] = "Jul",
|
|
[NSC_TIME_MONTH_AUG] = "Aug",
|
|
[NSC_TIME_MONTH_SEP] = "Sep",
|
|
[NSC_TIME_MONTH_OCT] = "Oct",
|
|
[NSC_TIME_MONTH_NOV] = "Nov",
|
|
[NSC_TIME_MONTH_DEC] = "Dec"
|
|
};
|
|
|
|
|
|
/* exported interface documented in utils/time.h */
|
|
const char *rfc1123_date(time_t t)
|
|
{
|
|
static char ret[30];
|
|
|
|
struct tm *tm = gmtime(&t);
|
|
|
|
snprintf(ret, sizeof ret, "%s, %02d %s %d %02d:%02d:%02d GMT",
|
|
weekdays_short[tm->tm_wday], tm->tm_mday,
|
|
months[tm->tm_mon], tm->tm_year + 1900,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* exported function documented in utils/time.h */
|
|
int nsc_sntimet(char *str, size_t size, time_t *timep)
|
|
{
|
|
#ifndef HAVE_STRFTIME
|
|
long long val;
|
|
val = (long long)*timep;
|
|
|
|
return snprintf(str, size, "%lld", val);
|
|
#else
|
|
struct tm *ltm;
|
|
|
|
ltm = localtime(timep);
|
|
if (ltm == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return strftime(str, size, "%s", ltm);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* exported function documented in utils/time.h */
|
|
nserror nsc_snptimet(const char *str, size_t size, time_t *timep)
|
|
{
|
|
time_t time_out;
|
|
|
|
#ifndef HAVE_STRPTIME
|
|
char *rstr;
|
|
|
|
if (size < 1) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
errno = 0;
|
|
time_out = (time_t)strtoll(str, &rstr, 10);
|
|
|
|
/* The conversion may have a range faliure or no digits were found */
|
|
if ((errno != 0) || (rstr == str)) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
#else
|
|
struct tm ltm;
|
|
|
|
if (size < 1) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
if (strptime(str, "%s", <m) == NULL) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
time_out = mktime(<m);
|
|
|
|
#endif
|
|
*timep = time_out;
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
|
|
#ifndef WITH_CURL
|
|
|
|
|
|
/**
|
|
* Array of long weekday names.
|
|
*/
|
|
static const char * const weekdays_long[NSC_TIME_WEEKDAY__COUNT] = {
|
|
[NSC_TIME_WEEKDAY_SUN] = "Sunday",
|
|
[NSC_TIME_WEEKDAY_MON] = "Monday",
|
|
[NSC_TIME_WEEKDAY_TUE] = "Tuesday",
|
|
[NSC_TIME_WEEKDAY_WED] = "Wednesday",
|
|
[NSC_TIME_WEEKDAY_THU] = "Thursday",
|
|
[NSC_TIME_WEEKDAY_FRI] = "Friday",
|
|
[NSC_TIME_WEEKDAY_SAT] = "Saturday"
|
|
};
|
|
|
|
/**
|
|
* Timezone offsets in mins
|
|
*
|
|
* Order doesn't matter.
|
|
*/
|
|
enum nsc_time_zone_offsets {
|
|
/* Timezones */
|
|
NSC_TIME_ZONE_OFFSET_IDLE = -12 * 60,
|
|
NSC_TIME_ZONE_OFFSET_NZST = -12 * 60,
|
|
NSC_TIME_ZONE_OFFSET_NZT = -12 * 60,
|
|
NSC_TIME_ZONE_OFFSET_EAST = -10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_GST = -10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_JST = - 9 * 60,
|
|
NSC_TIME_ZONE_OFFSET_CCT = - 8 * 60,
|
|
NSC_TIME_ZONE_OFFSET_WAST = - 7 * 60,
|
|
NSC_TIME_ZONE_OFFSET_EET = - 2 * 60,
|
|
NSC_TIME_ZONE_OFFSET_CET = - 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_FWT = - 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_MET = - 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_MEWT = - 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_GMT = 0,
|
|
NSC_TIME_ZONE_OFFSET_UTC = 0,
|
|
NSC_TIME_ZONE_OFFSET_WET = 0,
|
|
NSC_TIME_ZONE_OFFSET_WAT = 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_AST = 4 * 60,
|
|
NSC_TIME_ZONE_OFFSET_EST = 5 * 60,
|
|
NSC_TIME_ZONE_OFFSET_CST = 6 * 60,
|
|
NSC_TIME_ZONE_OFFSET_MST = 7 * 60,
|
|
NSC_TIME_ZONE_OFFSET_PST = 8 * 60,
|
|
NSC_TIME_ZONE_OFFSET_YST = 9 * 60,
|
|
NSC_TIME_ZONE_OFFSET_AHST = 10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_CAT = 10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_HST = 10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_IDLW = 12 * 60,
|
|
|
|
/* Daylight saving modified timezones */
|
|
NSC_TIME_ZONE_OFFSET_NZDT = NSC_TIME_ZONE_OFFSET_NZT - 60,
|
|
NSC_TIME_ZONE_OFFSET_EADT = NSC_TIME_ZONE_OFFSET_EAST - 60,
|
|
NSC_TIME_ZONE_OFFSET_WADT = NSC_TIME_ZONE_OFFSET_WAST - 60,
|
|
NSC_TIME_ZONE_OFFSET_CEST = NSC_TIME_ZONE_OFFSET_CET - 60,
|
|
NSC_TIME_ZONE_OFFSET_FST = NSC_TIME_ZONE_OFFSET_FWT - 60,
|
|
NSC_TIME_ZONE_OFFSET_MEST = NSC_TIME_ZONE_OFFSET_MET - 60,
|
|
NSC_TIME_ZONE_OFFSET_MESZ = NSC_TIME_ZONE_OFFSET_MET - 60,
|
|
NSC_TIME_ZONE_OFFSET_BST = NSC_TIME_ZONE_OFFSET_GMT - 60,
|
|
NSC_TIME_ZONE_OFFSET_ADT = NSC_TIME_ZONE_OFFSET_AST - 60,
|
|
NSC_TIME_ZONE_OFFSET_EDT = NSC_TIME_ZONE_OFFSET_EST - 60,
|
|
NSC_TIME_ZONE_OFFSET_CDT = NSC_TIME_ZONE_OFFSET_CST - 60,
|
|
NSC_TIME_ZONE_OFFSET_MDT = NSC_TIME_ZONE_OFFSET_MST - 60,
|
|
NSC_TIME_ZONE_OFFSET_PDT = NSC_TIME_ZONE_OFFSET_PST - 60,
|
|
NSC_TIME_ZONE_OFFSET_YDT = NSC_TIME_ZONE_OFFSET_YST - 60,
|
|
NSC_TIME_ZONE_OFFSET_HDT = NSC_TIME_ZONE_OFFSET_HST - 60,
|
|
|
|
/* Military timezones */
|
|
NSC_TIME_ZONE_OFFSET_Y = -12 * 60,
|
|
NSC_TIME_ZONE_OFFSET_X = -11 * 60,
|
|
NSC_TIME_ZONE_OFFSET_W = -10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_V = - 9 * 60,
|
|
NSC_TIME_ZONE_OFFSET_U = - 8 * 60,
|
|
NSC_TIME_ZONE_OFFSET_T = - 7 * 60,
|
|
NSC_TIME_ZONE_OFFSET_S = - 6 * 60,
|
|
NSC_TIME_ZONE_OFFSET_R = - 5 * 60,
|
|
NSC_TIME_ZONE_OFFSET_Q = - 4 * 60,
|
|
NSC_TIME_ZONE_OFFSET_P = - 3 * 60,
|
|
NSC_TIME_ZONE_OFFSET_O = - 2 * 60,
|
|
NSC_TIME_ZONE_OFFSET_N = - 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_Z = 0 * 60,
|
|
NSC_TIME_ZONE_OFFSET_A = 1 * 60,
|
|
NSC_TIME_ZONE_OFFSET_B = 2 * 60,
|
|
NSC_TIME_ZONE_OFFSET_C = 3 * 60,
|
|
NSC_TIME_ZONE_OFFSET_D = 4 * 60,
|
|
NSC_TIME_ZONE_OFFSET_E = 5 * 60,
|
|
NSC_TIME_ZONE_OFFSET_F = 6 * 60,
|
|
NSC_TIME_ZONE_OFFSET_G = 7 * 60,
|
|
NSC_TIME_ZONE_OFFSET_H = 8 * 60,
|
|
NSC_TIME_ZONE_OFFSET_I = 9 * 60,
|
|
NSC_TIME_ZONE_OFFSET_K = 10 * 60,
|
|
NSC_TIME_ZONE_OFFSET_L = 11 * 60,
|
|
NSC_TIME_ZONE_OFFSET_M = 12 * 60,
|
|
};
|
|
|
|
/**
|
|
* List of timezones.
|
|
*
|
|
* The order here is the order they appear in the `timezone_mins` and
|
|
* `timezones` arrays. So there is value in putting the most common
|
|
* timezones first.
|
|
*/
|
|
enum nsc_time_zones {
|
|
/** "GMT" first since its the only one I've seen in the wild. -- tlsa */
|
|
NSC_TIME_ZONE_GMT,
|
|
NSC_TIME_ZONE_IDLE,
|
|
NSC_TIME_ZONE_NZST,
|
|
NSC_TIME_ZONE_NZT,
|
|
NSC_TIME_ZONE_EAST,
|
|
NSC_TIME_ZONE_GST,
|
|
NSC_TIME_ZONE_JST,
|
|
NSC_TIME_ZONE_CCT,
|
|
NSC_TIME_ZONE_WAST,
|
|
NSC_TIME_ZONE_EET,
|
|
NSC_TIME_ZONE_CET,
|
|
NSC_TIME_ZONE_FWT,
|
|
NSC_TIME_ZONE_MET,
|
|
NSC_TIME_ZONE_MEWT,
|
|
NSC_TIME_ZONE_UTC,
|
|
NSC_TIME_ZONE_WET,
|
|
NSC_TIME_ZONE_WAT,
|
|
NSC_TIME_ZONE_AST,
|
|
NSC_TIME_ZONE_EST,
|
|
NSC_TIME_ZONE_CST,
|
|
NSC_TIME_ZONE_MST,
|
|
NSC_TIME_ZONE_PST,
|
|
NSC_TIME_ZONE_YST,
|
|
NSC_TIME_ZONE_AHST,
|
|
NSC_TIME_ZONE_CAT,
|
|
NSC_TIME_ZONE_HST,
|
|
NSC_TIME_ZONE_IDLW,
|
|
NSC_TIME_ZONE_NZDT,
|
|
NSC_TIME_ZONE_EADT,
|
|
NSC_TIME_ZONE_WADT,
|
|
NSC_TIME_ZONE_CEST,
|
|
NSC_TIME_ZONE_FST,
|
|
NSC_TIME_ZONE_MEST,
|
|
NSC_TIME_ZONE_MESZ,
|
|
NSC_TIME_ZONE_BST,
|
|
NSC_TIME_ZONE_ADT,
|
|
NSC_TIME_ZONE_EDT,
|
|
NSC_TIME_ZONE_CDT,
|
|
NSC_TIME_ZONE_MDT,
|
|
NSC_TIME_ZONE_PDT,
|
|
NSC_TIME_ZONE_YDT,
|
|
NSC_TIME_ZONE_HDT,
|
|
NSC_TIME_ZONE_Y,
|
|
NSC_TIME_ZONE_X,
|
|
NSC_TIME_ZONE_W,
|
|
NSC_TIME_ZONE_V,
|
|
NSC_TIME_ZONE_U,
|
|
NSC_TIME_ZONE_T,
|
|
NSC_TIME_ZONE_S,
|
|
NSC_TIME_ZONE_R,
|
|
NSC_TIME_ZONE_Q,
|
|
NSC_TIME_ZONE_P,
|
|
NSC_TIME_ZONE_O,
|
|
NSC_TIME_ZONE_N,
|
|
NSC_TIME_ZONE_Z,
|
|
NSC_TIME_ZONE_A,
|
|
NSC_TIME_ZONE_B,
|
|
NSC_TIME_ZONE_C,
|
|
NSC_TIME_ZONE_D,
|
|
NSC_TIME_ZONE_E,
|
|
NSC_TIME_ZONE_F,
|
|
NSC_TIME_ZONE_G,
|
|
NSC_TIME_ZONE_H,
|
|
NSC_TIME_ZONE_I,
|
|
NSC_TIME_ZONE_K,
|
|
NSC_TIME_ZONE_L,
|
|
NSC_TIME_ZONE_M,
|
|
NSC_TIME_ZONE__COUNT
|
|
};
|
|
|
|
/**
|
|
* Array of minute offsets for timezones.
|
|
*/
|
|
static const int16_t timezone_mins[NSC_TIME_ZONE__COUNT] = {
|
|
[NSC_TIME_ZONE_IDLE] = NSC_TIME_ZONE_OFFSET_IDLE,
|
|
[NSC_TIME_ZONE_NZST] = NSC_TIME_ZONE_OFFSET_NZST,
|
|
[NSC_TIME_ZONE_NZT] = NSC_TIME_ZONE_OFFSET_NZT,
|
|
[NSC_TIME_ZONE_EAST] = NSC_TIME_ZONE_OFFSET_EAST,
|
|
[NSC_TIME_ZONE_GST] = NSC_TIME_ZONE_OFFSET_GST,
|
|
[NSC_TIME_ZONE_JST] = NSC_TIME_ZONE_OFFSET_JST,
|
|
[NSC_TIME_ZONE_CCT] = NSC_TIME_ZONE_OFFSET_CCT,
|
|
[NSC_TIME_ZONE_WAST] = NSC_TIME_ZONE_OFFSET_WAST,
|
|
[NSC_TIME_ZONE_EET] = NSC_TIME_ZONE_OFFSET_EET,
|
|
[NSC_TIME_ZONE_CET] = NSC_TIME_ZONE_OFFSET_CET,
|
|
[NSC_TIME_ZONE_FWT] = NSC_TIME_ZONE_OFFSET_FWT,
|
|
[NSC_TIME_ZONE_MET] = NSC_TIME_ZONE_OFFSET_MET,
|
|
[NSC_TIME_ZONE_MEWT] = NSC_TIME_ZONE_OFFSET_MEWT,
|
|
[NSC_TIME_ZONE_GMT] = NSC_TIME_ZONE_OFFSET_GMT,
|
|
[NSC_TIME_ZONE_UTC] = NSC_TIME_ZONE_OFFSET_UTC,
|
|
[NSC_TIME_ZONE_WET] = NSC_TIME_ZONE_OFFSET_WET,
|
|
[NSC_TIME_ZONE_WAT] = NSC_TIME_ZONE_OFFSET_WAT,
|
|
[NSC_TIME_ZONE_AST] = NSC_TIME_ZONE_OFFSET_AST,
|
|
[NSC_TIME_ZONE_EST] = NSC_TIME_ZONE_OFFSET_EST,
|
|
[NSC_TIME_ZONE_CST] = NSC_TIME_ZONE_OFFSET_CST,
|
|
[NSC_TIME_ZONE_MST] = NSC_TIME_ZONE_OFFSET_MST,
|
|
[NSC_TIME_ZONE_PST] = NSC_TIME_ZONE_OFFSET_PST,
|
|
[NSC_TIME_ZONE_YST] = NSC_TIME_ZONE_OFFSET_YST,
|
|
[NSC_TIME_ZONE_AHST] = NSC_TIME_ZONE_OFFSET_AHST,
|
|
[NSC_TIME_ZONE_CAT] = NSC_TIME_ZONE_OFFSET_CAT,
|
|
[NSC_TIME_ZONE_HST] = NSC_TIME_ZONE_OFFSET_HST,
|
|
[NSC_TIME_ZONE_IDLW] = NSC_TIME_ZONE_OFFSET_IDLW,
|
|
[NSC_TIME_ZONE_NZDT] = NSC_TIME_ZONE_OFFSET_NZDT,
|
|
[NSC_TIME_ZONE_EADT] = NSC_TIME_ZONE_OFFSET_EADT,
|
|
[NSC_TIME_ZONE_WADT] = NSC_TIME_ZONE_OFFSET_WADT,
|
|
[NSC_TIME_ZONE_CEST] = NSC_TIME_ZONE_OFFSET_CEST,
|
|
[NSC_TIME_ZONE_FST] = NSC_TIME_ZONE_OFFSET_FST,
|
|
[NSC_TIME_ZONE_MEST] = NSC_TIME_ZONE_OFFSET_MEST,
|
|
[NSC_TIME_ZONE_MESZ] = NSC_TIME_ZONE_OFFSET_MESZ,
|
|
[NSC_TIME_ZONE_BST] = NSC_TIME_ZONE_OFFSET_BST,
|
|
[NSC_TIME_ZONE_ADT] = NSC_TIME_ZONE_OFFSET_ADT,
|
|
[NSC_TIME_ZONE_EDT] = NSC_TIME_ZONE_OFFSET_EDT,
|
|
[NSC_TIME_ZONE_CDT] = NSC_TIME_ZONE_OFFSET_CDT,
|
|
[NSC_TIME_ZONE_MDT] = NSC_TIME_ZONE_OFFSET_MDT,
|
|
[NSC_TIME_ZONE_PDT] = NSC_TIME_ZONE_OFFSET_PDT,
|
|
[NSC_TIME_ZONE_YDT] = NSC_TIME_ZONE_OFFSET_YDT,
|
|
[NSC_TIME_ZONE_HDT] = NSC_TIME_ZONE_OFFSET_HDT,
|
|
[NSC_TIME_ZONE_Y] = NSC_TIME_ZONE_OFFSET_Y,
|
|
[NSC_TIME_ZONE_X] = NSC_TIME_ZONE_OFFSET_X,
|
|
[NSC_TIME_ZONE_W] = NSC_TIME_ZONE_OFFSET_W,
|
|
[NSC_TIME_ZONE_V] = NSC_TIME_ZONE_OFFSET_V,
|
|
[NSC_TIME_ZONE_U] = NSC_TIME_ZONE_OFFSET_U,
|
|
[NSC_TIME_ZONE_T] = NSC_TIME_ZONE_OFFSET_T,
|
|
[NSC_TIME_ZONE_S] = NSC_TIME_ZONE_OFFSET_S,
|
|
[NSC_TIME_ZONE_R] = NSC_TIME_ZONE_OFFSET_R,
|
|
[NSC_TIME_ZONE_Q] = NSC_TIME_ZONE_OFFSET_Q,
|
|
[NSC_TIME_ZONE_P] = NSC_TIME_ZONE_OFFSET_P,
|
|
[NSC_TIME_ZONE_O] = NSC_TIME_ZONE_OFFSET_O,
|
|
[NSC_TIME_ZONE_N] = NSC_TIME_ZONE_OFFSET_N,
|
|
[NSC_TIME_ZONE_Z] = NSC_TIME_ZONE_OFFSET_Z,
|
|
[NSC_TIME_ZONE_A] = NSC_TIME_ZONE_OFFSET_A,
|
|
[NSC_TIME_ZONE_B] = NSC_TIME_ZONE_OFFSET_B,
|
|
[NSC_TIME_ZONE_C] = NSC_TIME_ZONE_OFFSET_C,
|
|
[NSC_TIME_ZONE_D] = NSC_TIME_ZONE_OFFSET_D,
|
|
[NSC_TIME_ZONE_E] = NSC_TIME_ZONE_OFFSET_E,
|
|
[NSC_TIME_ZONE_F] = NSC_TIME_ZONE_OFFSET_F,
|
|
[NSC_TIME_ZONE_G] = NSC_TIME_ZONE_OFFSET_G,
|
|
[NSC_TIME_ZONE_H] = NSC_TIME_ZONE_OFFSET_H,
|
|
[NSC_TIME_ZONE_I] = NSC_TIME_ZONE_OFFSET_I,
|
|
[NSC_TIME_ZONE_K] = NSC_TIME_ZONE_OFFSET_K,
|
|
[NSC_TIME_ZONE_L] = NSC_TIME_ZONE_OFFSET_L,
|
|
[NSC_TIME_ZONE_M] = NSC_TIME_ZONE_OFFSET_M
|
|
};
|
|
|
|
/**
|
|
* Array of timezone names. Order does not matter.
|
|
*/
|
|
static const char * const timezones[NSC_TIME_ZONE__COUNT] = {
|
|
[NSC_TIME_ZONE_IDLE] = "IDLE",
|
|
[NSC_TIME_ZONE_NZST] = "NZST",
|
|
[NSC_TIME_ZONE_NZT] = "NZT",
|
|
[NSC_TIME_ZONE_EAST] = "EAST",
|
|
[NSC_TIME_ZONE_GST] = "GST",
|
|
[NSC_TIME_ZONE_JST] = "JST",
|
|
[NSC_TIME_ZONE_CCT] = "CCT",
|
|
[NSC_TIME_ZONE_WAST] = "WAST",
|
|
[NSC_TIME_ZONE_EET] = "EET",
|
|
[NSC_TIME_ZONE_CET] = "CET",
|
|
[NSC_TIME_ZONE_FWT] = "FWT",
|
|
[NSC_TIME_ZONE_MET] = "MET",
|
|
[NSC_TIME_ZONE_MEWT] = "MEWT",
|
|
[NSC_TIME_ZONE_GMT] = "GMT",
|
|
[NSC_TIME_ZONE_UTC] = "UTC",
|
|
[NSC_TIME_ZONE_WET] = "WET",
|
|
[NSC_TIME_ZONE_WAT] = "WAT",
|
|
[NSC_TIME_ZONE_AST] = "AST",
|
|
[NSC_TIME_ZONE_EST] = "EST",
|
|
[NSC_TIME_ZONE_CST] = "CST",
|
|
[NSC_TIME_ZONE_MST] = "MST",
|
|
[NSC_TIME_ZONE_PST] = "PST",
|
|
[NSC_TIME_ZONE_YST] = "YST",
|
|
[NSC_TIME_ZONE_AHST] = "AHST",
|
|
[NSC_TIME_ZONE_CAT] = "CAT",
|
|
[NSC_TIME_ZONE_HST] = "HST",
|
|
[NSC_TIME_ZONE_IDLW] = "IDLW",
|
|
[NSC_TIME_ZONE_NZDT] = "NZDT",
|
|
[NSC_TIME_ZONE_EADT] = "EADT",
|
|
[NSC_TIME_ZONE_WADT] = "WADT",
|
|
[NSC_TIME_ZONE_CEST] = "CEST",
|
|
[NSC_TIME_ZONE_FST] = "FST",
|
|
[NSC_TIME_ZONE_MEST] = "MEST",
|
|
[NSC_TIME_ZONE_MESZ] = "MESZ",
|
|
[NSC_TIME_ZONE_BST] = "BST",
|
|
[NSC_TIME_ZONE_ADT] = "ADT",
|
|
[NSC_TIME_ZONE_EDT] = "EDT",
|
|
[NSC_TIME_ZONE_CDT] = "CDT",
|
|
[NSC_TIME_ZONE_MDT] = "MDT",
|
|
[NSC_TIME_ZONE_PDT] = "PDT",
|
|
[NSC_TIME_ZONE_YDT] = "YDT",
|
|
[NSC_TIME_ZONE_HDT] = "HDT",
|
|
[NSC_TIME_ZONE_Y] = "Y",
|
|
[NSC_TIME_ZONE_X] = "X",
|
|
[NSC_TIME_ZONE_W] = "W",
|
|
[NSC_TIME_ZONE_V] = "V",
|
|
[NSC_TIME_ZONE_U] = "U",
|
|
[NSC_TIME_ZONE_T] = "T",
|
|
[NSC_TIME_ZONE_S] = "S",
|
|
[NSC_TIME_ZONE_R] = "R",
|
|
[NSC_TIME_ZONE_Q] = "Q",
|
|
[NSC_TIME_ZONE_P] = "P",
|
|
[NSC_TIME_ZONE_O] = "O",
|
|
[NSC_TIME_ZONE_N] = "N",
|
|
[NSC_TIME_ZONE_Z] = "Z",
|
|
[NSC_TIME_ZONE_A] = "A",
|
|
[NSC_TIME_ZONE_B] = "B",
|
|
[NSC_TIME_ZONE_C] = "C",
|
|
[NSC_TIME_ZONE_D] = "D",
|
|
[NSC_TIME_ZONE_E] = "E",
|
|
[NSC_TIME_ZONE_F] = "F",
|
|
[NSC_TIME_ZONE_G] = "G",
|
|
[NSC_TIME_ZONE_H] = "H",
|
|
[NSC_TIME_ZONE_I] = "I",
|
|
[NSC_TIME_ZONE_K] = "K",
|
|
[NSC_TIME_ZONE_L] = "L",
|
|
[NSC_TIME_ZONE_M] = "M"
|
|
};
|
|
|
|
/**
|
|
* Flags for tracking the components of a date that have been parsed.
|
|
*/
|
|
enum nsc_date_component_flags {
|
|
NSC_COMPONENT_FLAGS_NONE = 0,
|
|
NSC_COMPONENT_FLAGS_HAVE_YEARS = (1 << 0),
|
|
NSC_COMPONENT_FLAGS_HAVE_MONTHS = (1 << 1),
|
|
NSC_COMPONENT_FLAGS_HAVE_DAYS = (1 << 2),
|
|
NSC_COMPONENT_FLAGS_HAVE_HOURS = (1 << 3),
|
|
NSC_COMPONENT_FLAGS_HAVE_MINS = (1 << 4),
|
|
NSC_COMPONENT_FLAGS_HAVE_SECS = (1 << 5),
|
|
NSC_COMPONENT_FLAGS_HAVE_TIMEZONE = (1 << 6),
|
|
NSC_COMPONENT_FLAGS_HAVE_WEEKDAY = (1 << 7),
|
|
NSC_COMPONENT_FLAGS__HAVE_YYYYMMDD =
|
|
NSC_COMPONENT_FLAGS_HAVE_YEARS |
|
|
NSC_COMPONENT_FLAGS_HAVE_MONTHS |
|
|
NSC_COMPONENT_FLAGS_HAVE_DAYS,
|
|
NSC_COMPONENT_FLAGS__HAVE_HHMMSS =
|
|
NSC_COMPONENT_FLAGS_HAVE_HOURS |
|
|
NSC_COMPONENT_FLAGS_HAVE_MINS |
|
|
NSC_COMPONENT_FLAGS_HAVE_SECS,
|
|
NSC_COMPONENT_FLAGS__HAVE_ALL =
|
|
NSC_COMPONENT_FLAGS_HAVE_YEARS |
|
|
NSC_COMPONENT_FLAGS_HAVE_MONTHS |
|
|
NSC_COMPONENT_FLAGS_HAVE_DAYS |
|
|
NSC_COMPONENT_FLAGS_HAVE_HOURS |
|
|
NSC_COMPONENT_FLAGS_HAVE_MINS |
|
|
NSC_COMPONENT_FLAGS_HAVE_SECS |
|
|
NSC_COMPONENT_FLAGS_HAVE_TIMEZONE
|
|
};
|
|
|
|
/**
|
|
* Context for date parsing.
|
|
*/
|
|
struct nsc_date_parse_ctx {
|
|
char prev; /**< Used for handling neumenrical timezone */
|
|
uint8_t secs;
|
|
uint8_t mins;
|
|
uint8_t hours;
|
|
uint8_t day;
|
|
uint8_t month;
|
|
uint16_t years;
|
|
int16_t timezone_offset_mins;
|
|
};
|
|
|
|
|
|
/**
|
|
* Helper for testing whether any of the flags in mask are set.
|
|
*
|
|
* \param[in] flags Flags to to check.
|
|
* \param[in] mask The set of flags to check for in `flags`.
|
|
* \return true iff any flags in `mask` are set in `flags`, else false.
|
|
*/
|
|
static bool flags_chk(
|
|
enum nsc_date_component_flags flags,
|
|
enum nsc_date_component_flags mask)
|
|
{
|
|
return flags & mask;
|
|
}
|
|
|
|
/**
|
|
* Helper for testing whether all of the flags in mask are set.
|
|
*
|
|
* \param[in] flags Flags to to check.
|
|
* \param[in] mask The set of flags to check for in `flags`.
|
|
* \return true iff all flags in `mask` are set in `flags`, else false.
|
|
*/
|
|
static bool flags_chk_all(
|
|
enum nsc_date_component_flags flags,
|
|
enum nsc_date_component_flags mask)
|
|
{
|
|
return (flags & mask) == mask;
|
|
}
|
|
|
|
|
|
/**
|
|
* Test for a weekday name in a string (case insensitive).
|
|
*
|
|
* \param[in] str String to parse a weekday name in.
|
|
* \param[in] len Number of consecutive alphabetical characters.
|
|
* \param[in,out] flags Flags indicating which date components have been
|
|
* found in `str` already. If a weekday component
|
|
* is found, the weekday flag is set.
|
|
* \return true iff weekday component is found, else false.
|
|
*/
|
|
static inline bool time__parse_weekday(
|
|
const char *str,
|
|
size_t len,
|
|
enum nsc_date_component_flags *flags)
|
|
{
|
|
const char * const *names = (len == 3) ?
|
|
weekdays_short : weekdays_long;
|
|
|
|
if (flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_WEEKDAY)) {
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < NSC_TIME_WEEKDAY__COUNT; i++) {
|
|
if (ascii_strings_count_equal_caseless(names[i], str) == len) {
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_WEEKDAY;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Attempt to parse a month name in a string (case insensitive).
|
|
*
|
|
* \param[in] str String to parse a month name in.
|
|
* \param[in] len Number of consecutive alphabetical characters.
|
|
* \param[in,out] flags Flags indicating which date components have been
|
|
* found in `str` already. If a month component
|
|
* is found, the month flag is set.
|
|
* \param[in,out] ctx Current date parsing context. If month component
|
|
* is found, the context's month value is set.
|
|
* \return true iff month name component is found, else false.
|
|
*/
|
|
static inline bool time__parse_month(
|
|
const char *str,
|
|
size_t len,
|
|
enum nsc_date_component_flags *flags,
|
|
struct nsc_date_parse_ctx *ctx)
|
|
{
|
|
if (flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_MONTHS)) {
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < NSC_TIME_MONTH__COUNT; i++) {
|
|
if (ascii_strings_count_equal_caseless(months[i], str) == len) {
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_MONTHS;
|
|
ctx->month = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Attempt to parse a timezone name in a string (case insensitive).
|
|
*
|
|
* \param[in] str String to parse a timezone name in.
|
|
* \param[in] len Number of consecutive alphabetical characters.
|
|
* \param[in,out] flags Flags indicating which date components have been
|
|
* found in `str` already. If a timezone component
|
|
* is found, the timezone flag is set.
|
|
* \param[in,out] ctx Current date parsing context. If timezone component
|
|
* is found, the context's timezone value is set.
|
|
* \return true iff timezone name component is found, else false.
|
|
*/
|
|
static inline bool time__parse_timezone(
|
|
const char *str,
|
|
size_t len,
|
|
enum nsc_date_component_flags *flags,
|
|
struct nsc_date_parse_ctx *ctx)
|
|
{
|
|
if (flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_TIMEZONE)) {
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < NSC_TIME_ZONE__COUNT; i++) {
|
|
if (ascii_strings_count_equal_caseless(
|
|
timezones[i], str) == len) {
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE;
|
|
ctx->timezone_offset_mins = timezone_mins[i];
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Attempt to parse an "hh:mm:ss" time from a string.
|
|
*
|
|
* \param[in] str String to parse a time in.
|
|
* \param[in,out] len The number of characters until the first non-digit.
|
|
* Iff a time component is found, updated to the number
|
|
* of comsumend characters.
|
|
* \param[in,out] flags Flags indicating which date components have been
|
|
* found in `str` already. If a time component
|
|
* is found, the hours, mins and secs flags are set.
|
|
* \param[in,out] ctx Current date parsing context. If time component
|
|
* is found, the context's time values are set.
|
|
* \return true iff time component is found, else false.
|
|
*/
|
|
static inline bool time__parse_hh_mm_ss(
|
|
const char *str,
|
|
size_t *len,
|
|
enum nsc_date_component_flags *flags,
|
|
struct nsc_date_parse_ctx *ctx)
|
|
{
|
|
size_t l;
|
|
|
|
if (*len != 2 || flags_chk(*flags, NSC_COMPONENT_FLAGS__HAVE_HHMMSS)) {
|
|
return false;
|
|
}
|
|
|
|
l = *len + ascii_count_digit_or_colon(str + *len);
|
|
if (l == 8) {
|
|
int h, m, s, count;
|
|
count = sscanf(str, "%02d:%02d:%02d", &h, &m, &s);
|
|
if (count == 3) {
|
|
ctx->hours = h;
|
|
ctx->mins = m;
|
|
ctx->secs = s;
|
|
*flags |= NSC_COMPONENT_FLAGS__HAVE_HHMMSS;
|
|
*len = l;
|
|
return true;
|
|
}
|
|
} else if (l == 5) {
|
|
int h, m, count;
|
|
count = sscanf(str, "%02d:%02d", &h, &m);
|
|
if (count == 2) {
|
|
ctx->hours = h;
|
|
ctx->mins = m;
|
|
ctx->secs = 0;
|
|
*flags |= NSC_COMPONENT_FLAGS__HAVE_HHMMSS;
|
|
*len = l;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Attempt to parse a number from a date string.
|
|
*
|
|
* How the number is treated depends on various things:
|
|
*
|
|
* - its character length,
|
|
* - its value,
|
|
* - which date components have already been parsed
|
|
*
|
|
* \param[in] str String to parse a time in.
|
|
* \param[in] len The number of characters until the first non-digit.
|
|
* \param[in,out] flags Flags indicating which date components have been
|
|
* found in `str` already. If any component is found,
|
|
* their flags are set.
|
|
* \param[in,out] ctx Current date parsing context. If any component
|
|
* is found, the appropriate context values are set.
|
|
* \return true iff a component is found, else false.
|
|
*/
|
|
static inline bool time__parse_number(
|
|
const char *str,
|
|
size_t len,
|
|
enum nsc_date_component_flags *flags,
|
|
struct nsc_date_parse_ctx *ctx)
|
|
{
|
|
int value;
|
|
|
|
if (len != ascii_string_to_int(str, &value)) {
|
|
return false;
|
|
}
|
|
|
|
switch (len) {
|
|
case 8:
|
|
if (!flags_chk(*flags, NSC_COMPONENT_FLAGS__HAVE_YYYYMMDD)) {
|
|
ctx->years = value / 10000;
|
|
ctx->month = (value % 10000) / 100 - 1;
|
|
ctx->day = value % 100 - 1;
|
|
*flags |= NSC_COMPONENT_FLAGS__HAVE_YYYYMMDD;
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_TIMEZONE)) {
|
|
if (ascii_is_sign(ctx->prev) && value <= 1400) {
|
|
ctx->timezone_offset_mins =
|
|
value / 100 * 60 +
|
|
value % 100;
|
|
if (ctx->prev == '+') {
|
|
ctx->timezone_offset_mins *= -1;
|
|
}
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE;
|
|
return true;
|
|
}
|
|
}
|
|
if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_YEARS)) {
|
|
ctx->years = value;
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_YEARS;
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
case 1:
|
|
if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_DAYS) &&
|
|
value > 0 && value <= 31) {
|
|
ctx->day = value - 1;
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_DAYS;
|
|
return true;
|
|
}
|
|
if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_YEARS)) {
|
|
ctx->years = (value > 70) ?
|
|
value + 1900 :
|
|
value + 2000;
|
|
*flags |= NSC_COMPONENT_FLAGS_HAVE_YEARS;
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get number of leap days up until end of given year.
|
|
*
|
|
* \param[in] year Year to count leap years up to.
|
|
* \return Number of leap days up to end of `year`.
|
|
*/
|
|
static inline int time__get_leap_days(int year)
|
|
{
|
|
return (year / 4) - (year / 100) + (year / 400);
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper to convert a date string context to a time_t.
|
|
*
|
|
* \param[in] ctx Current date parsing context.
|
|
* \param[in] flags Flags indicating which date components have been set.
|
|
* \param[out] time Returns the number of seconds since 1 Jan 1970 00:00 UTC.
|
|
* \return NSERROR_OK on success, appropriate error otherwise.
|
|
*/
|
|
static nserror time__ctx_to_time_t(
|
|
const struct nsc_date_parse_ctx *ctx,
|
|
enum nsc_date_component_flags flags,
|
|
time_t *time)
|
|
{
|
|
enum {
|
|
NSC_MONTH_DAYS_JAN = 31,
|
|
NSC_MONTH_DAYS_FEB = 28, /**< Leap years handled separatly */
|
|
NSC_MONTH_DAYS_MAR = 31,
|
|
NSC_MONTH_DAYS_APR = 30,
|
|
NSC_MONTH_DAYS_MAY = 31,
|
|
NSC_MONTH_DAYS_JUN = 30,
|
|
NSC_MONTH_DAYS_JUL = 31,
|
|
NSC_MONTH_DAYS_AUG = 31,
|
|
NSC_MONTH_DAYS_SEP = 30,
|
|
NSC_MONTH_DAYS_OCT = 31,
|
|
NSC_MONTH_DAYS_NOV = 30,
|
|
NSC_MONTH_DAYS_DEC = 31
|
|
};
|
|
enum {
|
|
NSC_MONTH_OFF_JAN = 0,
|
|
NSC_MONTH_OFF_FEB = NSC_MONTH_OFF_JAN + NSC_MONTH_DAYS_JAN,
|
|
NSC_MONTH_OFF_MAR = NSC_MONTH_OFF_FEB + NSC_MONTH_DAYS_FEB,
|
|
NSC_MONTH_OFF_APR = NSC_MONTH_OFF_MAR + NSC_MONTH_DAYS_MAR,
|
|
NSC_MONTH_OFF_MAY = NSC_MONTH_OFF_APR + NSC_MONTH_DAYS_APR,
|
|
NSC_MONTH_OFF_JUN = NSC_MONTH_OFF_MAY + NSC_MONTH_DAYS_MAY,
|
|
NSC_MONTH_OFF_JUL = NSC_MONTH_OFF_JUN + NSC_MONTH_DAYS_JUN,
|
|
NSC_MONTH_OFF_AUG = NSC_MONTH_OFF_JUL + NSC_MONTH_DAYS_JUL,
|
|
NSC_MONTH_OFF_SEP = NSC_MONTH_OFF_AUG + NSC_MONTH_DAYS_AUG,
|
|
NSC_MONTH_OFF_OCT = NSC_MONTH_OFF_SEP + NSC_MONTH_DAYS_SEP,
|
|
NSC_MONTH_OFF_NOV = NSC_MONTH_OFF_OCT + NSC_MONTH_DAYS_OCT,
|
|
NSC_MONTH_OFF_DEC = NSC_MONTH_OFF_NOV + NSC_MONTH_DAYS_NOV
|
|
};
|
|
static const uint16_t month_offsets[NSC_TIME_MONTH__COUNT] = {
|
|
[NSC_TIME_MONTH_JAN] = NSC_MONTH_OFF_JAN,
|
|
[NSC_TIME_MONTH_FEB] = NSC_MONTH_OFF_FEB,
|
|
[NSC_TIME_MONTH_MAR] = NSC_MONTH_OFF_MAR,
|
|
[NSC_TIME_MONTH_APR] = NSC_MONTH_OFF_APR,
|
|
[NSC_TIME_MONTH_MAY] = NSC_MONTH_OFF_MAY,
|
|
[NSC_TIME_MONTH_JUN] = NSC_MONTH_OFF_JUN,
|
|
[NSC_TIME_MONTH_JUL] = NSC_MONTH_OFF_JUL,
|
|
[NSC_TIME_MONTH_AUG] = NSC_MONTH_OFF_AUG,
|
|
[NSC_TIME_MONTH_SEP] = NSC_MONTH_OFF_SEP,
|
|
[NSC_TIME_MONTH_OCT] = NSC_MONTH_OFF_OCT,
|
|
[NSC_TIME_MONTH_NOV] = NSC_MONTH_OFF_NOV,
|
|
[NSC_TIME_MONTH_DEC] = NSC_MONTH_OFF_DEC
|
|
};
|
|
int year_days = (ctx->years - 1970) * 365;
|
|
int month_days = month_offsets[ctx->month];
|
|
int year = (ctx->month < NSC_TIME_MONTH_FEB) ?
|
|
ctx->years - 1 : ctx->years;
|
|
int leap_days = time__get_leap_days(year) - time__get_leap_days(1969);
|
|
int total_days = year_days + month_days + ctx->day + leap_days;
|
|
|
|
int mins = (int)ctx->mins + (int)ctx->timezone_offset_mins;
|
|
|
|
*time = (((((time_t)(total_days)) * 24) +
|
|
ctx->hours) * 60 +
|
|
mins) * 60 +
|
|
ctx->secs;
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse a date string to a `time_t`.
|
|
*
|
|
* \param[in] str String to parse.
|
|
* \param[out] time Returns the number of seconds since 1 Jan 1970 00:00 UTC.
|
|
* \return `NSERROR_OK` on success, else
|
|
* `NSERROR_INVALID` if the string parsing failed,
|
|
* appropriate error otherwise.
|
|
*/
|
|
static nserror time__get_date(const char *str, time_t *time)
|
|
{
|
|
enum nsc_date_component_flags flags = NSC_COMPONENT_FLAGS_NONE;
|
|
struct nsc_date_parse_ctx ctx = {
|
|
.prev = '\0',
|
|
.secs = 0,
|
|
.mins = 0,
|
|
.hours = 0,
|
|
.day = 0,
|
|
.month = 0,
|
|
.years = 0,
|
|
.timezone_offset_mins = 0
|
|
};
|
|
|
|
if (str == NULL || time == NULL) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
/* Parse */
|
|
while (*str != '\0' &&
|
|
!flags_chk_all(flags, NSC_COMPONENT_FLAGS__HAVE_ALL)) {
|
|
size_t len = 1;
|
|
|
|
if (ascii_is_alpha(*str)) {
|
|
len += ascii_count_alpha(str + 1);
|
|
|
|
if (!time__parse_weekday(str, len, &flags) &&
|
|
!time__parse_month(str, len, &flags, &ctx) &&
|
|
!time__parse_timezone(str, len, &flags, &ctx)) {
|
|
return NSERROR_INVALID;
|
|
}
|
|
|
|
} else if (ascii_is_digit(*str)) {
|
|
len += ascii_count_digit(str + 1);
|
|
|
|
if (!time__parse_hh_mm_ss(str, &len, &flags, &ctx) &&
|
|
!time__parse_number(str, len, &flags, &ctx)) {
|
|
return NSERROR_INVALID;
|
|
}
|
|
}
|
|
ctx.prev = *str;
|
|
str += len;
|
|
}
|
|
|
|
/* The initial values of 0 are used if hours, mins, secs, and timezone
|
|
* are not found */
|
|
flags |= NSC_COMPONENT_FLAGS__HAVE_HHMMSS;
|
|
flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE;
|
|
|
|
/* Validate */
|
|
if (!flags_chk_all(flags, NSC_COMPONENT_FLAGS__HAVE_ALL)) {
|
|
return NSERROR_INVALID;
|
|
}
|
|
if (ctx.secs > 60 || ctx.mins > 59 || ctx.hours > 23 ||
|
|
ctx.day > 31 || ctx.month > 11) {
|
|
return NSERROR_INVALID;
|
|
}
|
|
|
|
/* Convert */
|
|
return time__ctx_to_time_t(&ctx, flags, time);
|
|
}
|
|
|
|
/* exported function documented in utils/time.h */
|
|
nserror nsc_strntimet(const char *str, size_t size, time_t *timep)
|
|
{
|
|
return time__get_date(str, timep);
|
|
}
|
|
|
|
# else
|
|
|
|
/* exported function documented in utils/time.h */
|
|
nserror nsc_strntimet(const char *str, size_t size, time_t *timep)
|
|
{
|
|
time_t result;
|
|
|
|
if (str == NULL || timep == NULL) {
|
|
return NSERROR_BAD_PARAMETER;
|
|
}
|
|
|
|
result = curl_getdate(str, NULL);
|
|
|
|
if (result == -1) {
|
|
return NSERROR_INVALID;
|
|
}
|
|
|
|
*timep = result;
|
|
|
|
return NSERROR_OK;
|
|
}
|
|
|
|
#endif
|