#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef HAVE_SYS_TIMEB_H
#define HAVE_SYS_TIMEB_H 0
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
# include <time.h>
# endif
#endif
#if HAVE_SYS_TIMEB_H
#include <sys/timeb.h>
#endif
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif // HAVE_SYS_PARAM_H
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <ctype.h>
#include "date_object.h"
#include "error_object.h"
#include "operations.h"
#include "date_object.lut.h"
const time_t invalidDate = -1;
const double hoursPerDay = 24;
const double minutesPerHour = 60;
const double secondsPerMinute = 60;
const double msPerSecond = 1000;
const double msPerMinute = msPerSecond * secondsPerMinute;
const double msPerHour = msPerMinute * minutesPerHour;
const double msPerDay = msPerHour * hoursPerDay;
#if APPLE_CHANGES
#include <notify.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
using KJS::UChar;
using KJS::UString;
#define gmtime(x) gmtimeUsingCF(x)
#define localtime(x) localtimeUsingCF(x)
#define mktime(x) mktimeUsingCF(x)
#define timegm(x) timegmUsingCF(x)
#define time(x) timeUsingCF(x)
#define ctime(x) NotAllowedToCallThis()
#define strftime(a, b, c, d) NotAllowedToCallThis()
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 struct tm *tmUsingCF(time_t clock, CFTimeZoneRef timeZone)
{
static struct tm result;
static char timeZoneCString[128];
CFAbsoluteTime absoluteTime = clock - kCFAbsoluteTimeIntervalSince1970;
CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(absoluteTime, timeZone);
CFStringRef abbreviation = CFTimeZoneCopyAbbreviation(timeZone, absoluteTime);
CFStringGetCString(abbreviation, timeZoneCString, sizeof(timeZoneCString), kCFStringEncodingASCII);
CFRelease(abbreviation);
result.tm_sec = (int)date.second;
result.tm_min = date.minute;
result.tm_hour = date.hour;
result.tm_mday = date.day;
result.tm_mon = date.month - 1;
result.tm_year = date.year - 1900;
result.tm_wday = CFAbsoluteTimeGetDayOfWeek(absoluteTime, timeZone) % 7;
result.tm_yday = CFAbsoluteTimeGetDayOfYear(absoluteTime, timeZone) - 1;
result.tm_isdst = CFTimeZoneIsDaylightSavingTime(timeZone, absoluteTime);
result.tm_gmtoff = (int)CFTimeZoneGetSecondsFromGMT(timeZone, absoluteTime);
result.tm_zone = timeZoneCString;
return &result;
}
static CFTimeZoneRef UTCTimeZone()
{
static CFTimeZoneRef zone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
return zone;
}
static CFTimeZoneRef CopyLocalTimeZone()
{
static bool registered = false;
static int notificationToken;
if (!registered) {
uint32_t status = notify_register_check("com.apple.system.timezone", ¬ificationToken);
if (status == NOTIFY_STATUS_OK) {
registered = true;
}
}
if (registered) {
int notified;
uint32_t status = notify_check(notificationToken, ¬ified);
if (status == NOTIFY_STATUS_OK && notified) {
CFTimeZoneResetSystem();
}
}
CFTimeZoneRef zone = CFTimeZoneCopyDefault();
if (zone) {
return zone;
}
zone = UTCTimeZone();
CFRetain(zone);
return zone;
}
static struct tm *gmtimeUsingCF(const time_t *clock)
{
return tmUsingCF(*clock, UTCTimeZone());
}
static struct tm *localtimeUsingCF(const time_t *clock)
{
CFTimeZoneRef timeZone = CopyLocalTimeZone();
struct tm *result = tmUsingCF(*clock, timeZone);
CFRelease(timeZone);
return result;
}
static time_t timetUsingCF(struct tm *tm, CFTimeZoneRef timeZone)
{
CFGregorianDate date;
date.second = tm->tm_sec;
date.minute = tm->tm_min;
date.hour = tm->tm_hour;
date.day = tm->tm_mday;
date.month = tm->tm_mon + 1;
date.year = tm->tm_year + 1900;
if (date.year < -2500 || date.year > 2500) {
return invalidDate;
}
CFAbsoluteTime absoluteTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
if (tm->tm_isdst >= 0) {
if (CFTimeZoneIsDaylightSavingTime(timeZone, absoluteTime) && !tm->tm_isdst)
absoluteTime += 3600;
else if (!CFTimeZoneIsDaylightSavingTime(timeZone, absoluteTime) && tm->tm_isdst)
absoluteTime -= 3600;
}
CFTimeInterval interval = absoluteTime + kCFAbsoluteTimeIntervalSince1970;
if (interval > LONG_MAX) {
interval = LONG_MAX;
}
return (time_t) interval;
}
static time_t mktimeUsingCF(struct tm *tm)
{
CFTimeZoneRef timeZone = CopyLocalTimeZone();
time_t result = timetUsingCF(tm, timeZone);
CFRelease(timeZone);
return result;
}
static time_t timegmUsingCF(struct tm *tm)
{
return timetUsingCF(tm, UTCTimeZone());
}
static time_t timeUsingCF(time_t *clock)
{
time_t result = (time_t)(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970);
if (clock) {
*clock = result;
}
return result;
}
static UString formatDate(struct tm &tm)
{
char buffer[100];
snprintf(buffer, sizeof(buffer), "%s %s %02d %04d",
weekdayName[(tm.tm_wday + 6) % 7],
monthName[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
return buffer;
}
static UString formatDateUTCVariant(struct tm &tm)
{
char buffer[100];
snprintf(buffer, sizeof(buffer), "%s, %02d %s %04d",
weekdayName[(tm.tm_wday + 6) % 7],
tm.tm_mday, monthName[tm.tm_mon], tm.tm_year + 1900);
return buffer;
}
static UString formatTime(struct tm &tm)
{
char buffer[100];
if (tm.tm_gmtoff == 0) {
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", tm.tm_hour, tm.tm_min, tm.tm_sec);
} else {
int offset = tm.tm_gmtoff;
if (offset < 0) {
offset = -offset;
}
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_gmtoff < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60);
}
return UString(buffer);
}
static CFDateFormatterStyle styleFromArgString(const UString& string,CFDateFormatterStyle defaultStyle)
{
CFDateFormatterStyle retVal = defaultStyle;
if (string == "short")
retVal = kCFDateFormatterShortStyle;
else if (string == "medium")
retVal = kCFDateFormatterMediumStyle;
else if (string == "long")
retVal = kCFDateFormatterLongStyle;
else if (string == "full")
retVal = kCFDateFormatterFullStyle;
return retVal;
}
static UString formatLocaleDate(KJS::ExecState *exec, double time, bool includeDate, bool includeTime, const KJS::List &args)
{
CFLocaleRef locale = CFLocaleCopyCurrent();
int argCount = args.size();
CFDateFormatterStyle dateStyle = (includeDate ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
CFDateFormatterStyle timeStyle = (includeTime ? kCFDateFormatterLongStyle : kCFDateFormatterNoStyle);
UString arg0String;
UString arg1String;
bool useCustomFormat = false;
UString customFormatString;
arg0String = args[0].toString(exec);
if ((arg0String == "custom") && (argCount >= 2)) {
useCustomFormat = true;
customFormatString = args[1].toString(exec);
} else if (includeDate && includeTime && (argCount >= 2)) {
arg1String = args[1].toString(exec);
dateStyle = styleFromArgString(arg0String,dateStyle);
timeStyle = styleFromArgString(arg1String,timeStyle);
} else if (includeDate && (argCount >= 1)) {
dateStyle = styleFromArgString(arg0String,dateStyle);
} else if (includeTime && (argCount >= 1)) {
timeStyle = styleFromArgString(arg0String,timeStyle);
}
CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, dateStyle, timeStyle);
if (useCustomFormat) {
CFStringRef customFormatCFString = CFStringCreateWithCharacters(NULL,(UniChar*)customFormatString.data(),customFormatString.size());
CFDateFormatterSetFormat(formatter,customFormatCFString);
CFRelease(customFormatCFString);
}
CFStringRef string = CFDateFormatterCreateStringWithAbsoluteTime(NULL, formatter, time - kCFAbsoluteTimeIntervalSince1970);
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);
CFRelease(formatter);
CFRelease(locale);
return UString(buffer, length);
}
#endif // APPLE_CHANGES
using namespace KJS;
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;
else if (year % 400 == 0)
return 366;
else if (year % 100 == 0)
return 365;
else
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 long timeZoneOffset(const struct tm *t)
{
#if defined BSD || defined(__linux__) || defined(__APPLE__)
return -(t->tm_gmtoff / 60);
#else
# if defined(__BORLANDC__) || defined(__CYGWIN__)
#if !defined(__CYGWIN__)
#error please add daylight savings offset here!
#endif
return _timezone / 60 - (t->tm_isdst > 0 ? 60 : 0);
# else
return timezone / 60 - (t->tm_isdst > 0 ? 60 : 0 );
# endif
#endif
}
const ClassInfo DateInstanceImp::info = {"Date", 0, 0, 0};
DateInstanceImp::DateInstanceImp(ObjectImp *proto)
: ObjectImp(proto)
{
}
const ClassInfo DatePrototypeImp::info = {"Date", 0, &dateTable, 0};
DatePrototypeImp::DatePrototypeImp(ExecState *,
ObjectPrototypeImp *objectProto)
: DateInstanceImp(objectProto)
{
Value protect(this);
setInternalValue(NumberImp::create(NaN));
}
Value DatePrototypeImp::get(ExecState *exec, const Identifier &propertyName) const
{
return lookupGetFunction<DateProtoFuncImp, ObjectImp>( exec, propertyName, &dateTable, this );
}
DateProtoFuncImp::DateProtoFuncImp(ExecState *exec, int i, int len)
: InternalFunctionImp(
static_cast<FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
), id(abs(i)), utc(i<0)
{
Value protect(this);
putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
}
bool DateProtoFuncImp::implementsCall() const
{
return true;
}
Value DateProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
{
if ((id == ToString || id == ValueOf || id == GetTime || id == SetTime) &&
!thisObj.inherits(&DateInstanceImp::info)) {
Object err = Error::create(exec,TypeError);
exec->setException(err);
return err;
}
Value result;
UString s;
#if !APPLE_CHANGES
const int bufsize=100;
char timebuffer[bufsize];
CString oldlocale = setlocale(LC_TIME,NULL);
if (!oldlocale.c_str())
oldlocale = setlocale(LC_ALL, NULL);
#endif
Value 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 String("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 Number(NaN);
}
}
int realYearOffset = 0;
double milliOffset = 0.0;
double secs = floor(milli / 1000.0);
if (milli < 0 || milli >= timeFromYear(2038)) {
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 / 1000.0);
int ms = int(milli - tv * 1000.0);
struct tm *t = utc ? gmtime(&tv) : localtime(&tv);
if (realYearOffset != 0) {
t->tm_year += realYearOffset;
milli -= milliOffset;
double m = milli;
if (!utc)
m -= timeZoneOffset(t) * msPerMinute;
t->tm_wday = weekDay(m);
}
switch (id) {
#if APPLE_CHANGES
case ToString:
result = String(formatDate(*t) + " " + formatTime(*t));
break;
case ToDateString:
result = String(formatDate(*t));
break;
case ToTimeString:
result = String(formatTime(*t));
break;
case ToGMTString:
case ToUTCString:
result = String(formatDateUTCVariant(*t) + " " + formatTime(*t));
break;
case ToLocaleString:
result = String(formatLocaleDate(exec, secs, true, true, args));
break;
case ToLocaleDateString:
result = String(formatLocaleDate(exec, secs, true, false, args));
break;
case ToLocaleTimeString:
result = String(formatLocaleDate(exec, secs, false, true, args));
break;
#else
case ToString:
s = ctime(&tv);
result = String(s.substr(0, s.size() - 1));
break;
case ToDateString:
case ToTimeString:
case ToGMTString:
case ToUTCString:
setlocale(LC_TIME,"C");
if (id == DateProtoFuncImp::ToDateString) {
strftime(timebuffer, bufsize, "%x",t);
} else if (id == DateProtoFuncImp::ToTimeString) {
strftime(timebuffer, bufsize, "%X",t);
} else { strftime(timebuffer, bufsize, "%a, %d %b %Y %H:%M:%S %Z", t);
}
setlocale(LC_TIME,oldlocale.c_str());
result = String(timebuffer);
break;
case ToLocaleString:
strftime(timebuffer, bufsize, "%c", t);
result = String(timebuffer);
break;
case ToLocaleDateString:
strftime(timebuffer, bufsize, "%x", t);
result = String(timebuffer);
break;
case ToLocaleTimeString:
strftime(timebuffer, bufsize, "%X", t);
result = String(timebuffer);
break;
#endif
case ValueOf:
result = Number(milli);
break;
case GetTime:
result = Number(milli);
break;
case GetYear:
if ( exec->dynamicInterpreter()->compatMode() == Interpreter::IECompat )
result = Number(1900 + t->tm_year);
else
result = Number(t->tm_year);
break;
case GetFullYear:
result = Number(1900 + t->tm_year);
break;
case GetMonth:
result = Number(t->tm_mon);
break;
case GetDate:
result = Number(t->tm_mday);
break;
case GetDay:
result = Number(t->tm_wday);
break;
case GetHours:
result = Number(t->tm_hour);
break;
case GetMinutes:
result = Number(t->tm_min);
break;
case GetSeconds:
result = Number(t->tm_sec);
break;
case GetMilliSeconds:
result = Number(ms);
break;
case GetTimezoneOffset:
#if defined BSD || defined(__APPLE__)
result = Number(-t->tm_gmtoff / 60);
#else
# if defined(__BORLANDC__)
#error please add daylight savings offset here!
result = Number(_timezone / 60 - (_daylight ? 60 : 0));
# else
result = Number(( timezone / 60 - ( daylight ? 60 : 0 )));
# endif
#endif
break;
case SetTime:
milli = roundValue(exec,args[0]);
result = Number(milli);
thisObj.setInternalValue(result);
break;
case SetMilliSeconds:
ms = args[0].toInt32(exec);
break;
case SetSeconds:
t->tm_sec = args[0].toInt32(exec);
if (args.size() >= 2)
ms = args[1].toInt32(exec);
break;
case SetMinutes:
t->tm_min = args[0].toInt32(exec);
if (args.size() >= 2)
t->tm_sec = args[1].toInt32(exec);
if (args.size() >= 3)
ms = args[2].toInt32(exec);
break;
case SetHours:
t->tm_hour = args[0].toInt32(exec);
if (args.size() >= 2)
t->tm_min = args[1].toInt32(exec);
if (args.size() >= 3)
t->tm_sec = args[2].toInt32(exec);
if (args.size() >= 4)
ms = args[3].toInt32(exec);
break;
case SetDate:
t->tm_mday = args[0].toInt32(exec);
break;
case SetMonth:
t->tm_mon = args[0].toInt32(exec);
if (args.size() >= 2)
t->tm_mday = args[1].toInt32(exec);
break;
case SetFullYear:
t->tm_year = args[0].toInt32(exec) - 1900;
if (args.size() >= 2)
t->tm_mon = args[1].toInt32(exec);
if (args.size() >= 3)
t->tm_mday = args[2].toInt32(exec);
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 = Number(makeTime(t, ms, utc));
thisObj.setInternalValue(result);
}
return result;
}
DateObjectImp::DateObjectImp(ExecState *exec,
FunctionPrototypeImp *funcProto,
DatePrototypeImp *dateProto)
: InternalFunctionImp(funcProto)
{
Value protect(this);
putDirect(prototypePropertyName, dateProto, DontEnum|DontDelete|ReadOnly);
static const Identifier parsePropertyName("parse");
putDirect(parsePropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::Parse, 1), DontEnum);
static const Identifier UTCPropertyName("UTC");
putDirect(UTCPropertyName, new DateObjectFuncImp(exec,funcProto,DateObjectFuncImp::UTC, 7), DontEnum);
putDirect(lengthPropertyName, 7, ReadOnly|DontDelete|DontEnum);
}
bool DateObjectImp::implementsConstruct() const
{
return true;
}
Object DateObjectImp::construct(ExecState *exec, const List &args)
{
int numArgs = args.size();
#ifdef KJS_VERBOSE
fprintf(stderr,"DateObjectImp::construct - %d args\n", numArgs);
#endif
double value;
if (numArgs == 0) { #if HAVE_SYS_TIMEB_H
# if defined(__BORLANDC__)
struct timeb timebuffer;
ftime(&timebuffer);
# else
struct _timeb timebuffer;
_ftime(&timebuffer);
# endif
double utc = floor((double)timebuffer.time * 1000.0 + (double)timebuffer.millitm);
#else
struct timeval tv;
gettimeofday(&tv, 0L);
double utc = floor((double)tv.tv_sec * 1000.0 + (double)tv.tv_usec / 1000.0);
#endif
value = utc;
} else if (numArgs == 1) {
UString s = args[0].toString(exec);
double d = s.toDouble();
if (isNaN(d))
value = parseDate(s);
else
value = d;
} else {
struct tm t;
memset(&t, 0, sizeof(t));
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 {
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;
int ms = (numArgs >= 7) ? args[6].toInt32(exec) : 0;
value = makeTime(&t, ms, false);
}
}
Object proto = exec->lexicalInterpreter()->builtinDatePrototype();
Object ret(new DateInstanceImp(proto.imp()));
ret.setInternalValue(Number(timeClip(value)));
return ret;
}
bool DateObjectImp::implementsCall() const
{
return true;
}
Value DateObjectImp::call(ExecState *, Object &, const List &)
{
#ifdef KJS_VERBOSE
fprintf(stderr,"DateObjectImp::call - current time\n");
#endif
time_t t = time(0L);
#if APPLE_CHANGES
struct tm *tm = localtime(&t);
return String(formatDate(*tm) + " " + formatTime(*tm));
#else
UString s(ctime(&t));
return String(s.substr(0, s.size() - 1));
#endif
}
DateObjectFuncImp::DateObjectFuncImp(ExecState *exec, FunctionPrototypeImp *funcProto,
int i, int len)
: InternalFunctionImp(funcProto), id(i)
{
Value protect(this);
putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
}
bool DateObjectFuncImp::implementsCall() const
{
return true;
}
Value DateObjectFuncImp::call(ExecState *exec, Object &, const List &args)
{
if (id == Parse) {
return Number(parseDate(args[0].toString(exec)));
}
else { struct tm t;
memset(&t, 0, sizeof(t));
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 Number(NaN);
}
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;
int ms = (n >= 7) ? args[6].toInt32(exec) : 0;
time_t mktimeResult = timegm(&t);
if (mktimeResult == invalidDate)
return Number(NaN);
return Number(mktimeResult * 1000.0 + ms);
}
}
double KJS::parseDate(const UString &u)
{
#ifdef KJS_VERBOSE
fprintf(stderr,"KJS::parseDate %s\n",u.ascii());
#endif
double seconds = KRFCDate_parseDate( u );
return seconds == invalidDate ? NaN : seconds * 1000.0;
}
static double ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
{
double ret = (day - 32075)
+ 1461L * (year + 4800L + (mon - 14) / 12) / 4
+ 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
- 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
- 2440588;
ret = 24*ret + hour;
ret = 60*ret + minute;
ret = 60*ret + second;
return ret;
}
static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
static const struct KnownZone {
#ifdef _WIN32
char tzName[4];
#else
const char tzName[4];
#endif
int tzOffset;
} known_zones[] = {
{ "UT", 0 },
{ "GMT", 0 },
{ "EST", -300 },
{ "EDT", -240 },
{ "CST", -360 },
{ "CDT", -300 },
{ "MST", -420 },
{ "MDT", -360 },
{ "PST", -480 },
{ "PDT", -420 }
};
double KJS::makeTime(struct tm *t, int ms, bool utc)
{
int utcOffset;
if (utc) {
time_t zero = 0;
#if defined BSD || defined(__linux__) || defined(__APPLE__)
struct tm t3;
localtime_r(&zero, &t3);
utcOffset = t3.tm_gmtoff;
t->tm_isdst = t3.tm_isdst;
#else
(void)localtime(&zero);
# if defined(__BORLANDC__) || defined(__CYGWIN__)
utcOffset = - _timezone;
# else
utcOffset = - timezone;
# endif
t->tm_isdst = 0;
#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;
const double baseTime = timeFromYear(baseYear);
yearOffset = timeFromYear(y) - baseTime;
t->tm_year = baseYear - 1900;
}
return (mktime(t) + utcOffset) * 1000.0 + ms + yearOffset;
}
static int findMonth(const char *monthStr)
{
assert(monthStr);
static const char haystack[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
char needle[4];
for (int i = 0; i < 3; ++i) {
if (!*monthStr)
return -1;
needle[i] = tolower(*monthStr++);
}
needle[3] = '\0';
const char *str = strstr(haystack, needle);
if (str) {
int position = str - haystack;
if (position % 3 == 0) {
return position / 3;
}
}
return -1;
}
double KJS::KRFCDate_parseDate(const UString &_date)
{
double result = -1;
int offset = 0;
bool have_tz = false;
char *newPosStr;
const char *dateString = _date.ascii();
int day = 0;
int month = -1; int year = 0;
int hour = 0;
int minute = 0;
int second = 0;
bool have_time = false;
errno = 0;
while(isspace(*dateString))
dateString++;
const char *wordStart = dateString;
while(*dateString && !isdigit(*dateString))
{
if ( isspace(*dateString) && dateString - wordStart >= 3 )
{
month = findMonth(wordStart);
while(isspace(*dateString))
dateString++;
wordStart = dateString;
}
else
dateString++;
}
if (month == -1 && dateString && wordStart != dateString) {
month = findMonth(wordStart);
}
while(isspace(*dateString))
dateString++;
if (!*dateString)
return invalidDate;
day = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
if (!*dateString)
return invalidDate;
if (day < 1)
return invalidDate;
if (day > 31) {
if (*dateString == '/' && day >= 1000) {
if (!*++dateString)
return invalidDate;
year = day;
month = strtol(dateString, &newPosStr, 10) - 1;
if (errno)
return invalidDate;
dateString = newPosStr;
if (*dateString++ != '/' || !*dateString)
return invalidDate;
day = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
} else {
return invalidDate;
}
} else if (*dateString == '/' && day <= 12 && month == -1) {
dateString++;
month = day - 1; day = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
if (*dateString == '/')
dateString++;
if (!*dateString)
return invalidDate;
}
else
{
if (*dateString == '-')
dateString++;
while(isspace(*dateString))
dateString++;
if (*dateString == ',')
dateString++;
if ( month == -1 ) {
month = findMonth(dateString);
if (month == -1)
return invalidDate;
while(*dateString && (*dateString != '-') && !isspace(*dateString))
dateString++;
if (!*dateString)
return invalidDate;
if ((*dateString != '-') && (*dateString != '/') && !isspace(*dateString))
return invalidDate;
dateString++;
}
if ((month < 0) || (month > 11))
return invalidDate;
}
if (year <= 0 && *dateString) {
year = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
}
if (*newPosStr)
{
if (!isspace(*newPosStr)) {
if ( *newPosStr == ':' ) year = -1;
else
return invalidDate;
} else dateString = ++newPosStr;
hour = strtol(dateString, &newPosStr, 10);
if (newPosStr != dateString) {
have_time = true;
dateString = newPosStr;
if ((hour < 0) || (hour > 23))
return invalidDate;
if (!*dateString)
return invalidDate;
if (*dateString++ != ':')
return invalidDate;
minute = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
if ((minute < 0) || (minute > 59))
return invalidDate;
if (*dateString && *dateString != ':' && !isspace(*dateString))
return invalidDate;
if (*dateString ==':') {
dateString++;
second = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
if ((second < 0) || (second > 59))
return invalidDate;
}
while(isspace(*dateString))
dateString++;
if (strncasecmp(dateString, "AM", 2) == 0) {
if (hour > 12)
return invalidDate;
if (hour == 12)
hour = 0;
dateString += 2;
while (isspace(*dateString))
dateString++;
} else if (strncasecmp(dateString, "PM", 2) == 0) {
if (hour > 12)
return invalidDate;
if (hour != 12)
hour += 12;
dateString += 2;
while (isspace(*dateString))
dateString++;
}
}
} else {
dateString = newPosStr;
}
if (*dateString) {
if (strncasecmp(dateString, "GMT", 3) == 0 ||
strncasecmp(dateString, "UTC", 3) == 0) {
dateString += 3;
have_tz = true;
}
while (isspace(*dateString))
++dateString;
if (strncasecmp(dateString, "GMT", 3) == 0) {
dateString += 3;
}
if ((*dateString == '+') || (*dateString == '-')) {
offset = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
if ((offset < -9959) || (offset > 9959))
return invalidDate;
int sgn = (offset < 0)? -1:1;
offset = abs(offset);
if ( *dateString == ':' ) { int offset2 = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
offset = (offset*60 + offset2)*sgn;
}
else
offset = ((offset / 100)*60 + (offset % 100))*sgn;
have_tz = 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);
have_tz = true;
break;
}
}
}
}
while(isspace(*dateString))
dateString++;
if ( *dateString && year == -1 ) {
year = strtol(dateString, &newPosStr, 10);
if (errno)
return invalidDate;
dateString = newPosStr;
}
while (isspace(*dateString))
dateString++;
if (*dateString != '\0')
return invalidDate;
if ((year >= 0) && (year < 50))
year += 2000;
if ((year >= 50) && (year < 100))
year += 1900;
if (!have_tz) {
struct tm t;
memset(&t, 0, sizeof(tm));
t.tm_mday = day;
t.tm_mon = month;
t.tm_year = year - 1900;
t.tm_isdst = -1;
if (have_time) {
t.tm_sec = second;
t.tm_min = minute;
t.tm_hour = hour;
}
return makeTime(&t, 0, false) / 1000.0;
}
result = ymdhms_to_seconds(year, month + 1, day, hour, minute, second) - (offset * 60);
return result;
}
double KJS::timeClip(double t)
{
if (!isfinite(t))
return NaN;
double at = fabs(t);
if (at > 8.64E15)
return NaN;
return copysign(floor(at), t);
}