#include "config.h"
#include "date_object.h"
#include "date_object.lut.h"
#if HAVE(ERRNO_H)
#include <errno.h>
#endif
#if HAVE(SYS_PARAM_H)
#include <sys/param.h>
#endif
#if HAVE(SYS_TIME_H)
#include <sys/time.h>
#endif
#if HAVE(SYS_TIMEB_H)
#include <sys/timeb.h>
#endif
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "error_object.h"
#include "operations.h"
#if PLATFORM(MAC)
#include <CoreFoundation/CoreFoundation.h>
#endif
#if PLATFORM(WIN_OS)
#define copysign(x, y) _copysign(x, y)
#define isfinite(x) _finite(x)
#define strncasecmp(x, y, z) strnicmp(x, y, z)
#define snprintf _snprintf
#endif
inline int gmtoffset(const tm& t)
{
#if PLATFORM(WIN_OS)
return -(_timezone / 60 - (t.tm_isdst > 0 ? 60 : 0 )) * 60;
#else
return t.tm_gmtoff;
#endif
}
namespace KJS {
class DateObjectFuncImp : public InternalFunctionImp {
public:
DateObjectFuncImp(ExecState *, FunctionPrototype *, int i, int len, const Identifier& );
virtual JSValue *callAsFunction(ExecState *, JSObject *thisObj, const List &args);
enum { Parse, UTC };
private:
int id;
};
const double hoursPerDay = 24;
const double minutesPerHour = 60;
const double secondsPerMinute = 60;
const double msPerSecond = 1000;
const double msPerMinute = 60 * 1000;
const double msPerHour = 60 * 60 * 1000;
const double msPerDay = 24 * 60 * 60 * 1000;
static const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
static const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static double makeTime(tm *, double ms, bool utc);
static double parseDate(const UString &);
static double timeClip(double);
static void millisecondsToTM(double milli, bool utc, tm *t);
#if PLATFORM(MAC)
static CFDateFormatterStyle styleFromArgString(const UString& string, CFDateFormatterStyle defaultStyle)
{
if (string == "short")
return kCFDateFormatterShortStyle;
if (string == "medium")
return kCFDateFormatterMediumStyle;
if (string == "long")
return kCFDateFormatterLongStyle;
if (string == "full")
return kCFDateFormatterFullStyle;
return defaultStyle;
}
static UString formatLocaleDate(ExecState *exec, double time, bool includeDate, bool includeTime, const List &args)
{
CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
bool useCustomFormat = false;
UString customFormatString;
UString arg0String = args[0]->toString(exec);
if (arg0String == "custom" && !args[1]->isUndefined()) {
useCustomFormat = true;
customFormatString = args[1]->toString(exec);
} else if (includeDate && includeTime && !args[1]->isUndefined()) {
dateStyle = styleFromArgString(arg0String, dateStyle);
timeStyle = styleFromArgString(args[1]->toString(exec), timeStyle);
} else if (includeDate && !args[0]->isUndefined()) {
dateStyle = styleFromArgString(arg0String, dateStyle);
} else if (includeTime && !args[0]->isUndefined()) {
timeStyle = styleFromArgString(arg0String, timeStyle);
}
CFLocaleRef locale = CFLocaleCopyCurrent();
CFDateFormatterRef formatter = CFDateFormatterCreate(0, locale, dateStyle, timeStyle);
CFRelease(locale);
if (useCustomFormat) {
CFStringRef customFormatCFString = CFStringCreateWithCharacters(0, (UniChar *)customFormatString.data(), customFormatString.size());
CFDateFormatterSetFormat(formatter, customFormatCFString);
CFRelease(customFormatCFString);
}
CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(0, formatter, time - kCFAbsoluteTimeIntervalSince1970);
CFRelease(formatter);
UChar buffer[200];
const size_t bufferLength = sizeof(buffer) / sizeof(buffer[0]);
size_t length = CFStringGetLength(string);
assert(length <= bufferLength);
if (length > bufferLength)
length = bufferLength;
CFStringGetCharacters(string, CFRangeMake(0, length), reinterpret_cast<UniChar *>(buffer));
CFRelease(string);
return UString(buffer, length);
}
#endif // PLATFORM(MAC)
static UString formatDate(const tm &t)
{
char buffer[100];
snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
weekdayName[(t.tm_wday + 6) % 7],
monthName[t.tm_mon], t.tm_mday, t.tm_year + 1900);
return buffer;
}
static UString formatDateUTCVariant(const tm &t)
{
char buffer[100];
snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
weekdayName[(t.tm_wday + 6) % 7],
t.tm_mday, monthName[t.tm_mon], t.tm_year + 1900);
return buffer;
}
static UString formatTime(const tm &t, bool utc)
{
char buffer[100];
if (utc) {
#if !PLATFORM(WIN_OS)
ASSERT(t.tm_gmtoff == 0);
#endif
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.tm_hour, t.tm_min, t.tm_sec);
} else {
int offset = abs(gmtoffset(t));
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
t.tm_hour, t.tm_min, t.tm_sec,
gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
}
return UString(buffer);
}
static int day(double t)
{
return int(floor(t / msPerDay));
}
static double dayFromYear(int year)
{
return 365.0 * (year - 1970)
+ floor((year - 1969) / 4.0)
- floor((year - 1901) / 100.0)
+ floor((year - 1601) / 400.0);
}
static int daysInYear(int year)
{
if (year % 4 != 0)
return 365;
if (year % 400 == 0)
return 366;
if (year % 100 == 0)
return 365;
return 366;
}
static double timeFromYear(int year)
{
return msPerDay * dayFromYear(year);
}
static int yearFromTime(double t)
{
int y = 1970 + int(t / (365.25 * msPerDay));
if (timeFromYear(y) > t) {
do
--y;
while (timeFromYear(y) > t);
} else {
while (timeFromYear(y + 1) < t)
++y;
}
return y;
}
static int weekDay(double t)
{
int wd = (day(t) + 4) % 7;
if (wd < 0)
wd += 7;
return wd;
}
static void fillStructuresUsingTimeArgs(ExecState *exec, const List &args, int maxArgs, double *ms, tm *t)
{
double milliseconds = 0;
int idx = 0;
int numArgs = args.size();
if (numArgs > maxArgs)
numArgs = maxArgs;
if (maxArgs >= 4 && idx < numArgs) {
t->tm_hour = 0;
milliseconds += args[idx++]->toInt32(exec) * msPerHour;
}
if (maxArgs >= 3 && idx < numArgs) {
t->tm_min = 0;
milliseconds += args[idx++]->toInt32(exec) * msPerMinute;
}
if (maxArgs >= 2 && idx < numArgs) {
t->tm_sec = 0;
milliseconds += args[idx++]->toInt32(exec) * msPerSecond;
}
if (idx < numArgs) {
milliseconds += roundValue(exec, args[idx]);
} else {
milliseconds += *ms;
}
*ms = milliseconds;
}
static void fillStructuresUsingDateArgs(ExecState *exec, const List &args, int maxArgs, double *ms, tm *t)
{
int idx = 0;
int numArgs = args.size();
if (numArgs > maxArgs)
numArgs = maxArgs;
if (maxArgs >= 3 && idx < numArgs)
t->tm_year = args[idx++]->toInt32(exec) - 1900;
if (maxArgs >= 2 && idx < numArgs)
t->tm_mon = args[idx++]->toInt32(exec);
if (idx < numArgs) {
t->tm_mday = 0;
*ms += args[idx]->toInt32(exec) * msPerDay;
}
}
const ClassInfo DateInstance::info = {"Date", 0, 0, 0};
DateInstance::DateInstance(JSObject *proto)
: JSObject(proto)
{
}
bool DateInstance::getTime(tm &t, int &offset) const
{
double milli = internalValue()->getNumber();
if (isNaN(milli))
return false;
millisecondsToTM(milli, false, &t);
offset = gmtoffset(t);
return true;
}
bool DateInstance::getUTCTime(tm &t) const
{
double milli = internalValue()->getNumber();
if (isNaN(milli))
return false;
millisecondsToTM(milli, true, &t);
return true;
}
bool DateInstance::getTime(double &milli, int &offset) const
{
milli = internalValue()->getNumber();
if (isNaN(milli))
return false;
tm t;
millisecondsToTM(milli, false, &t);
offset = gmtoffset(t);
return true;
}
bool DateInstance::getUTCTime(double &milli) const
{
milli = internalValue()->getNumber();
if (isNaN(milli))
return false;
return true;
}
static inline bool isTime_tSigned()
{
time_t minusOne = (time_t)(-1);
return minusOne < 0;
}
static void millisecondsToTM(double milli, bool utc, tm *t)
{
static bool time_tIsSigned = isTime_tSigned();
static double time_tMin = (time_tIsSigned ? - (double)(1ULL << (8 * sizeof(time_t) - 1)) : 0);
static double time_tMax = (time_tIsSigned ? (1ULL << 8 * sizeof(time_t) - 1) - 1 : 2 * (double)(1ULL << 8 * sizeof(time_t) - 1) - 1);
int realYearOffset = 0;
double milliOffset = 0.0;
double secs = floor(milli / msPerSecond);
if (secs < time_tMin || secs > time_tMax) {
int realYear = yearFromTime(milli);
int base = daysInYear(realYear) == 365 ? 2001 : 2000;
milliOffset = timeFromYear(base) - timeFromYear(realYear);
milli += milliOffset;
realYearOffset = realYear - base;
}
time_t tv = (time_t) floor(milli / msPerSecond);
*t = *(utc ? gmtime(&tv) : localtime(&tv));
if (realYearOffset != 0) {
t->tm_year += realYearOffset;
milli -= milliOffset;
double m = milli;
if (!utc)
m += gmtoffset(*t) * msPerSecond;
t->tm_wday = weekDay(m);
}
}
const ClassInfo DatePrototype::info = {"Date", &DateInstance::info, &dateTable, 0};
DatePrototype::DatePrototype(ExecState *, ObjectPrototype *objectProto)
: DateInstance(objectProto)
{
setInternalValue(jsNaN());
}
bool DatePrototype::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticFunctionSlot<DateProtoFunc, JSObject>(exec, &dateTable, this, propertyName, slot);
}
DateProtoFunc::DateProtoFunc(ExecState *exec, int i, int len, const Identifier& name)
: InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name)
, id(abs(i))
, utc(i < 0)
{
putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
}
JSValue *DateProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
{
if (!thisObj->inherits(&DateInstance::info))
return throwError(exec, TypeError);
JSValue *result = 0;
UString s;
#if !PLATFORM(DARWIN)
const int bufsize=100;
char timebuffer[bufsize];
CString oldlocale = setlocale(LC_TIME, 0);
if (!oldlocale.size())
oldlocale = setlocale(LC_ALL, 0);
#endif
JSValue *v = thisObj->internalValue();
double milli = v->toNumber(exec);
if (isNaN(milli)) {
switch (id) {
case ToString:
case ToDateString:
case ToTimeString:
case ToGMTString:
case ToUTCString:
case ToLocaleString:
case ToLocaleDateString:
case ToLocaleTimeString:
return jsString("Invalid Date");
case ValueOf:
case GetTime:
case GetYear:
case GetFullYear:
case GetMonth:
case GetDate:
case GetDay:
case GetHours:
case GetMinutes:
case GetSeconds:
case GetMilliSeconds:
case GetTimezoneOffset:
return jsNaN();
}
}
double secs = floor(milli / msPerSecond);
double ms = milli - secs * msPerSecond;
tm t;
millisecondsToTM(milli, utc, &t);
switch (id) {
case ToString:
return jsString(formatDate(t) + " " + formatTime(t, utc));
case ToDateString:
return jsString(formatDate(t));
break;
case ToTimeString:
return jsString(formatTime(t, utc));
break;
case ToGMTString:
case ToUTCString:
return jsString(formatDateUTCVariant(t) + " " + formatTime(t, utc));
break;
#if PLATFORM(MAC)
case ToLocaleString:
return jsString(formatLocaleDate(exec, secs, true, true, args));
break;
case ToLocaleDateString:
return jsString(formatLocaleDate(exec, secs, true, false, args));
break;
case ToLocaleTimeString:
return jsString(formatLocaleDate(exec, secs, false, true, args));
break;
#else
case ToLocaleString:
strftime(timebuffer, bufsize, "%c", &t);
return jsString(timebuffer);
break;
case ToLocaleDateString:
strftime(timebuffer, bufsize, "%x", &t);
return jsString(timebuffer);
break;
case ToLocaleTimeString:
strftime(timebuffer, bufsize, "%X", &t);
return jsString(timebuffer);
break;
#endif
case ValueOf:
case GetTime:
return jsNumber(milli);
case GetYear:
if (exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat)
return jsNumber(1900 + t.tm_year);
return jsNumber(t.tm_year);
case GetFullYear:
return jsNumber(1900 + t.tm_year);
case GetMonth:
return jsNumber(t.tm_mon);
case GetDate:
return jsNumber(t.tm_mday);
case GetDay:
return jsNumber(t.tm_wday);
case GetHours:
return jsNumber(t.tm_hour);
case GetMinutes:
return jsNumber(t.tm_min);
case GetSeconds:
return jsNumber(t.tm_sec);
case GetMilliSeconds:
return jsNumber(ms);
case GetTimezoneOffset:
return jsNumber(-gmtoffset(t) / 60);
case SetTime:
milli = roundValue(exec, args[0]);
result = jsNumber(milli);
thisObj->setInternalValue(result);
break;
case SetMilliSeconds:
fillStructuresUsingTimeArgs(exec, args, 1, &ms, &t);
break;
case SetSeconds:
fillStructuresUsingTimeArgs(exec, args, 2, &ms, &t);
break;
case SetMinutes:
fillStructuresUsingTimeArgs(exec, args, 3, &ms, &t);
break;
case SetHours:
fillStructuresUsingTimeArgs(exec, args, 4, &ms, &t);
break;
case SetDate:
fillStructuresUsingDateArgs(exec, args, 1, &ms, &t);
break;
case SetMonth:
fillStructuresUsingDateArgs(exec, args, 2, &ms, &t);
break;
case SetFullYear:
fillStructuresUsingDateArgs(exec, args, 3, &ms, &t);
break;
case SetYear:
t.tm_year = args[0]->toInt32(exec) >= 1900 ? args[0]->toInt32(exec) - 1900 : args[0]->toInt32(exec);
break;
}
if (id == SetYear || id == SetMilliSeconds || id == SetSeconds ||
id == SetMinutes || id == SetHours || id == SetDate ||
id == SetMonth || id == SetFullYear ) {
result = jsNumber(makeTime(&t, ms, utc));
thisObj->setInternalValue(result);
}
return result;
}
DateObjectImp::DateObjectImp(ExecState *exec,
FunctionPrototype *funcProto,
DatePrototype *dateProto)
: InternalFunctionImp(funcProto)
{
putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
static const Identifier parsePropertyName("parse");
putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::Parse, 1, parsePropertyName), DontEnum);
static const Identifier UTCPropertyName("UTC");
putDirectFunction(new DateObjectFuncImp(exec, funcProto, DateObjectFuncImp::UTC, 7, UTCPropertyName), DontEnum);
putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
}
bool DateObjectImp::implementsConstruct() const
{
return true;
}
JSObject *DateObjectImp::construct(ExecState *exec, const List &args)
{
int numArgs = args.size();
double value;
if (numArgs == 0) { #if PLATFORM(WIN_OS)
#if COMPILER(BORLAND)
struct timeb timebuffer;
ftime(&timebuffer);
#else
struct _timeb timebuffer;
_ftime(&timebuffer);
#endif
double utc = timebuffer.time * msPerSecond + timebuffer.millitm;
#else
struct timeval tv;
gettimeofday(&tv, 0);
double utc = floor(tv.tv_sec * msPerSecond + tv.tv_usec / 1000);
#endif
value = utc;
} else if (numArgs == 1) {
if (args[0]->isObject(&DateInstance::info))
value = static_cast<JSObject*>(args[0])->internalValue()->toNumber(exec);
else {
JSValue* primitive = args[0]->toPrimitive(exec);
if (primitive->isString())
value = parseDate(primitive->getString());
else
value = primitive->toNumber(exec);
}
} else {
if (isNaN(args[0]->toNumber(exec))
|| isNaN(args[1]->toNumber(exec))
|| (numArgs >= 3 && isNaN(args[2]->toNumber(exec)))
|| (numArgs >= 4 && isNaN(args[3]->toNumber(exec)))
|| (numArgs >= 5 && isNaN(args[4]->toNumber(exec)))
|| (numArgs >= 6 && isNaN(args[5]->toNumber(exec)))
|| (numArgs >= 7 && isNaN(args[6]->toNumber(exec)))) {
value = NaN;
} else {
tm t;
memset(&t, 0, sizeof(t));
int year = args[0]->toInt32(exec);
t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
t.tm_mon = args[1]->toInt32(exec);
t.tm_mday = (numArgs >= 3) ? args[2]->toInt32(exec) : 1;
t.tm_hour = (numArgs >= 4) ? args[3]->toInt32(exec) : 0;
t.tm_min = (numArgs >= 5) ? args[4]->toInt32(exec) : 0;
t.tm_sec = (numArgs >= 6) ? args[5]->toInt32(exec) : 0;
t.tm_isdst = -1;
double ms = (numArgs >= 7) ? roundValue(exec, args[6]) : 0;
value = makeTime(&t, ms, false);
}
}
DateInstance *ret = new DateInstance(exec->lexicalInterpreter()->builtinDatePrototype());
ret->setInternalValue(jsNumber(timeClip(value)));
return ret;
}
JSValue *DateObjectImp::callAsFunction(ExecState * , JSObject * , const List &)
{
time_t t = time(0);
tm ts = *localtime(&t);
return jsString(formatDate(ts) + " " + formatTime(ts, false));
}
DateObjectFuncImp::DateObjectFuncImp(ExecState*, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
: InternalFunctionImp(funcProto, name), id(i)
{
putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
}
JSValue *DateObjectFuncImp::callAsFunction(ExecState* exec, JSObject*, const List& args)
{
if (id == Parse) {
return jsNumber(parseDate(args[0]->toString(exec)));
}
else { int n = args.size();
if (isNaN(args[0]->toNumber(exec))
|| isNaN(args[1]->toNumber(exec))
|| (n >= 3 && isNaN(args[2]->toNumber(exec)))
|| (n >= 4 && isNaN(args[3]->toNumber(exec)))
|| (n >= 5 && isNaN(args[4]->toNumber(exec)))
|| (n >= 6 && isNaN(args[5]->toNumber(exec)))
|| (n >= 7 && isNaN(args[6]->toNumber(exec)))) {
return jsNaN();
}
tm t;
memset(&t, 0, sizeof(t));
int year = args[0]->toInt32(exec);
t.tm_year = (year >= 0 && year <= 99) ? year : year - 1900;
t.tm_mon = args[1]->toInt32(exec);
t.tm_mday = (n >= 3) ? args[2]->toInt32(exec) : 1;
t.tm_hour = (n >= 4) ? args[3]->toInt32(exec) : 0;
t.tm_min = (n >= 5) ? args[4]->toInt32(exec) : 0;
t.tm_sec = (n >= 6) ? args[5]->toInt32(exec) : 0;
double ms = (n >= 7) ? roundValue(exec, args[6]) : 0;
return jsNumber(makeTime(&t, ms, true));
}
}
static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second)
{
double days = (day - 32075)
+ floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4)
+ 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
- floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4)
- 2440588;
return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second;
}
static const struct KnownZone {
#if !PLATFORM(WIN_OS)
const
#endif
char tzName[4];
int tzOffset;
} known_zones[] = {
{ "UT", 0 },
{ "GMT", 0 },
{ "EST", -300 },
{ "EDT", -240 },
{ "CST", -360 },
{ "CDT", -300 },
{ "MST", -420 },
{ "MDT", -360 },
{ "PST", -480 },
{ "PDT", -420 }
};
static double makeTime(tm *t, double ms, bool utc)
{
int utcOffset;
if (utc) {
time_t zero = 0;
#if PLATFORM(WIN_OS)
(void)localtime(&zero);
#if COMPILER(BORLAND) || COMPILER(CYGWIN)
utcOffset = - _timezone;
#else
utcOffset = - timezone;
#endif
t->tm_isdst = 0;
#else
tm t3;
localtime_r(&zero, &t3);
utcOffset = t3.tm_gmtoff;
t->tm_isdst = t3.tm_isdst;
#endif
} else {
utcOffset = 0;
t->tm_isdst = -1;
}
double yearOffset = 0.0;
if (t->tm_year < (1970 - 1900) || t->tm_year > (2038 - 1900)) {
int y = t->tm_year + 1900;
int baseYear = daysInYear(y) == 365 ? 2001 : 2000;
double baseTime = timeFromYear(baseYear);
yearOffset = timeFromYear(y) - baseTime;
t->tm_year = baseYear - 1900;
}
if (!utc) {
time_t tval = mktime(t) + (time_t)((ms + yearOffset) / 1000);
tm t3 = *localtime(&tval);
t->tm_isdst = t3.tm_isdst;
}
return (mktime(t) + utcOffset) * msPerSecond + ms + yearOffset;
}
inline static void skipSpacesAndComments(const char *&s)
{
int nesting = 0;
char ch;
while ((ch = *s)) {
if (!isspace(ch)) {
if (ch == '(')
nesting++;
else if (ch == ')' && nesting > 0)
nesting--;
else if (nesting == 0)
break;
}
s++;
}
}
static int findMonth(const char *monthStr)
{
assert(monthStr);
char needle[4];
for (int i = 0; i < 3; ++i) {
if (!*monthStr)
return -1;
needle[i] = tolower(*monthStr++);
}
needle[3] = '\0';
const char *haystack = "janfebmaraprmayjunjulaugsepoctnovdec";
const char *str = strstr(haystack, needle);
if (str) {
int position = str - haystack;
if (position % 3 == 0)
return position / 3;
}
return -1;
}
static double parseDate(const UString &date)
{
CString dateCString = date.UTF8String();
const char *dateString = dateCString.c_str();
skipSpacesAndComments(dateString);
long month = -1;
const char *wordStart = dateString;
while (*dateString && !isdigit(*dateString)) {
if (isspace(*dateString) || *dateString == '(') {
if (dateString - wordStart >= 3)
month = findMonth(wordStart);
skipSpacesAndComments(dateString);
wordStart = dateString;
} else
dateString++;
}
if (month == -1 && dateString && wordStart != dateString)
month = findMonth(wordStart);
skipSpacesAndComments(dateString);
if (!*dateString)
return NaN;
char *newPosStr;
errno = 0;
long day = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
if (!*dateString)
return NaN;
if (day < 0)
return NaN;
long year = 0;
if (day > 31) {
if (*dateString != '/')
return NaN;
if (!*++dateString)
return NaN;
year = day;
month = strtol(dateString, &newPosStr, 10) - 1;
if (errno)
return NaN;
dateString = newPosStr;
if (*dateString++ != '/' || !*dateString)
return NaN;
day = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
} else if (*dateString == '/' && month == -1) {
dateString++;
month = day - 1; day = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
if (day < 1 || day > 31)
return NaN;
dateString = newPosStr;
if (*dateString == '/')
dateString++;
if (!*dateString)
return NaN;
} else {
if (*dateString == '-')
dateString++;
skipSpacesAndComments(dateString);
if (*dateString == ',')
dateString++;
if (month == -1) { month = findMonth(dateString);
if (month == -1)
return NaN;
while (*dateString && (*dateString != '-') && !isspace(*dateString))
dateString++;
if (!*dateString)
return NaN;
if (*dateString != '-' && *dateString != '/' && !isspace(*dateString))
return NaN;
dateString++;
}
}
if (month < 0 || month > 11)
return NaN;
if (year <= 0 && *dateString) {
year = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
}
long hour = 0;
long minute = 0;
long second = 0;
if (!*newPosStr)
dateString = newPosStr;
else {
if (!isspace(*newPosStr)) {
if (*newPosStr != ':')
return NaN;
year = -1;
} else {
dateString = ++newPosStr;
skipSpacesAndComments(dateString);
}
hour = strtol(dateString, &newPosStr, 10);
if (newPosStr != dateString) {
dateString = newPosStr;
if (hour < 0 || hour > 23)
return NaN;
if (!*dateString)
return NaN;
if (*dateString++ != ':')
return NaN;
minute = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
if (minute < 0 || minute > 59)
return NaN;
if (*dateString && *dateString != ':' && !isspace(*dateString))
return NaN;
if (*dateString ==':') {
dateString++;
second = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
if (second < 0 || second > 59)
return NaN;
}
skipSpacesAndComments(dateString);
if (strncasecmp(dateString, "AM", 2) == 0) {
if (hour > 12)
return NaN;
if (hour == 12)
hour = 0;
dateString += 2;
skipSpacesAndComments(dateString);
} else if (strncasecmp(dateString, "PM", 2) == 0) {
if (hour > 12)
return NaN;
if (hour != 12)
hour += 12;
dateString += 2;
skipSpacesAndComments(dateString);
}
}
}
bool haveTZ = false;
int offset = 0;
if (*dateString) {
if (strncasecmp(dateString, "GMT", 3) == 0 || strncasecmp(dateString, "UTC", 3) == 0) {
dateString += 3;
haveTZ = true;
}
if (*dateString == '+' || *dateString == '-') {
long o = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
if (o < -9959 || o > 9959)
return NaN;
int sgn = (o < 0) ? -1 : 1;
o = abs(o);
if (*dateString != ':') {
offset = ((o / 100) * 60 + (o % 100)) * sgn;
} else { long o2 = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
offset = (o * 60 + o2) * sgn;
}
haveTZ = true;
} else {
for (int i = 0; i < int(sizeof(known_zones) / sizeof(KnownZone)); i++) {
if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
offset = known_zones[i].tzOffset;
dateString += strlen(known_zones[i].tzName);
haveTZ = true;
break;
}
}
}
}
skipSpacesAndComments(dateString);
if (*dateString && year == -1) {
year = strtol(dateString, &newPosStr, 10);
if (errno)
return NaN;
dateString = newPosStr;
}
skipSpacesAndComments(dateString);
if (*dateString)
return NaN;
if (year >= 0 && year < 100) {
if (year < 50)
year += 2000;
else
year += 1900;
}
if (!haveTZ) {
tm t;
memset(&t, 0, sizeof(tm));
t.tm_mday = day;
t.tm_mon = month;
t.tm_year = year - 1900;
t.tm_isdst = -1;
t.tm_sec = second;
t.tm_min = minute;
t.tm_hour = hour;
return makeTime(&t, 0, false);
}
return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond;
}
double timeClip(double t)
{
if (!isfinite(t))
return NaN;
double at = fabs(t);
if (at > 8.64E15)
return NaN;
return copysign(floor(at), t);
}
}