jsdate.c
来自「一个基于alice开发的机器人」· C语言 代码 · 共 2,235 行 · 第 1/4 页
C
2,235 行
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JS date methods.
*/
/*
* "For example, OS/360 devotes 26 bytes of the permanently
* resident date-turnover routine to the proper handling of
* December 31 on leap years (when it is Day 366). That
* might have been left to the operator."
*
* Frederick Brooks, 'The Second-System Effect'.
*/
#include "jsstddef.h"
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsprf.h"
#include "prmjtime.h"
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsconfig.h"
#include "jscntxt.h"
#include "jsdate.h"
#include "jsinterp.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsstr.h"
/*
* The JS 'Date' object is patterned after the Java 'Date' object.
* Here is an script:
*
* today = new Date();
*
* print(today.toLocaleString());
*
* weekDay = today.getDay();
*
*
* These Java (and ECMA-262) methods are supported:
*
* UTC
* getDate (getUTCDate)
* getDay (getUTCDay)
* getHours (getUTCHours)
* getMinutes (getUTCMinutes)
* getMonth (getUTCMonth)
* getSeconds (getUTCSeconds)
* getMilliseconds (getUTCMilliseconds)
* getTime
* getTimezoneOffset
* getYear
* getFullYear (getUTCFullYear)
* parse
* setDate (setUTCDate)
* setHours (setUTCHours)
* setMinutes (setUTCMinutes)
* setMonth (setUTCMonth)
* setSeconds (setUTCSeconds)
* setMilliseconds (setUTCMilliseconds)
* setTime
* setYear (setFullYear, setUTCFullYear)
* toGMTString (toUTCString)
* toLocaleString
* toString
*
*
* These Java methods are not supported
*
* setDay
* before
* after
* equals
* hashCode
*/
/*
* 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
* definition and reduce dependence on NSPR. NSPR is used to get the current
* time in milliseconds, the time zone offset, and the daylight savings time
* offset for a given time. NSPR is also used for Date.toLocaleString(), for
* locale-specific formatting, and to get a string representing the timezone.
* (Which turns out to be platform-dependent.)
*
* To do:
* (I did some performance tests by timing how long it took to run what
* I had of the js ECMA conformance tests.)
*
* - look at saving results across multiple calls to supporting
* functions; the toString functions compute some of the same values
* multiple times. Although - I took a quick stab at this, and I lost
* rather than gained. (Fractionally.) Hard to tell what compilers/processors
* are doing these days.
*
* - look at tweaking function return types to return double instead
* of int; this seems to make things run slightly faster sometimes.
* (though it could be architecture-dependent.) It'd be good to see
* how this does on win32. (Tried it on irix.) Types could use a
* general going-over.
*/
/*
* Supporting functions - ECMA 15.9.1.*
*/
#define HalfTimeDomain 8.64e15
#define HoursPerDay 24.0
#define MinutesPerDay (HoursPerDay * MinutesPerHour)
#define MinutesPerHour 60.0
#define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
#define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
#define SecondsPerMinute 60.0
#if defined(XP_WIN) || defined(XP_OS2)
/* Work around msvc double optimization bug by making these runtime values; if
* they're available at compile time, msvc optimizes division by them by
* computing the reciprocal and multiplying instead of dividing - this loses
* when the reciprocal isn't representable in a double.
*/
static jsdouble msPerSecond = 1000.0;
static jsdouble msPerDay = SecondsPerDay * 1000.0;
static jsdouble msPerHour = SecondsPerHour * 1000.0;
static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
#else
#define msPerDay (SecondsPerDay * msPerSecond)
#define msPerHour (SecondsPerHour * msPerSecond)
#define msPerMinute (SecondsPerMinute * msPerSecond)
#define msPerSecond 1000.0
#endif
#define Day(t) floor((t) / msPerDay)
static jsdouble
TimeWithinDay(jsdouble t)
{
jsdouble result;
result = fmod(t, msPerDay);
if (result < 0)
result += msPerDay;
return result;
}
#define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
? 366 : 365)
/* math here has to be f.p, because we need
* floor((1968 - 1969) / 4) == -1
*/
#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
- floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
#define TimeFromYear(y) (DayFromYear(y) * msPerDay)
static jsint
YearFromTime(jsdouble t)
{
jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
jsdouble t2 = (jsdouble) TimeFromYear(y);
if (t2 > t) {
y--;
} else {
if (t2 + msPerDay * DaysInYear(y) <= t)
y++;
}
return y;
}
#define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
/*
* The following array contains the day of year for the first day of
* each month, where index 0 is January, and day 0 is January 1.
*/
static jsdouble firstDayOfMonth[2][12] = {
{0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
{0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
};
#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
static intN
MonthFromTime(jsdouble t)
{
intN d, step;
jsint year = YearFromTime(t);
d = DayWithinYear(t, year);
if (d < (step = 31))
return 0;
step += (InLeapYear(t) ? 29 : 28);
if (d < step)
return 1;
if (d < (step += 31))
return 2;
if (d < (step += 30))
return 3;
if (d < (step += 31))
return 4;
if (d < (step += 30))
return 5;
if (d < (step += 31))
return 6;
if (d < (step += 31))
return 7;
if (d < (step += 30))
return 8;
if (d < (step += 31))
return 9;
if (d < (step += 30))
return 10;
return 11;
}
static intN
DateFromTime(jsdouble t)
{
intN d, step, next;
jsint year = YearFromTime(t);
d = DayWithinYear(t, year);
if (d <= (next = 30))
return d + 1;
step = next;
next += (InLeapYear(t) ? 29 : 28);
if (d <= next)
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
if (d <= (next += 31))
return d - step;
step = next;
if (d <= (next += 30))
return d - step;
step = next;
return d - step;
}
static intN
WeekDay(jsdouble t)
{
jsint result;
result = (jsint) Day(t) + 4;
result = result % 7;
if (result < 0)
result += 7;
return (intN) result;
}
/* LocalTZA gets set by js_InitDateClass() */
static jsdouble LocalTZA;
static jsdouble
DaylightSavingTA(jsdouble t)
{
volatile int64 PR_t;
int64 ms2us;
int64 offset;
jsdouble result;
/* abort if NaN */
if (JSDOUBLE_IS_NaN(t))
return t;
/* put our t in an LL, and map it to usec for prtime */
JSLL_D2L(PR_t, t);
JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
JSLL_MUL(PR_t, PR_t, ms2us);
offset = PRMJ_DSTOffset(PR_t);
JSLL_DIV(offset, offset, ms2us);
JSLL_L2D(result, offset);
return result;
}
#define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
#define LocalTime(t) ((t) + AdjustTime(t))
static jsdouble
UTC(jsdouble t)
{
return t - AdjustTime(t - LocalTZA);
}
static intN
HourFromTime(jsdouble t)
{
intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
if (result < 0)
result += (intN)HoursPerDay;
return result;
}
static intN
MinFromTime(jsdouble t)
{
intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
if (result < 0)
result += (intN)MinutesPerHour;
return result;
}
static intN
SecFromTime(jsdouble t)
{
intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
if (result < 0)
result += (intN)SecondsPerMinute;
return result;
}
static intN
msFromTime(jsdouble t)
{
intN result = (intN) fmod(t, msPerSecond);
if (result < 0)
result += (intN)msPerSecond;
return result;
}
#define MakeTime(hour, min, sec, ms) \
(((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)
static jsdouble
MakeDay(jsdouble year, jsdouble month, jsdouble date)
{
jsdouble result;
JSBool leap;
jsdouble yearday;
jsdouble monthday;
year += floor(month / 12);
month = fmod(month, 12.0);
if (month < 0)
month += 12;
leap = (DaysInYear((jsint) year) == 366);
yearday = floor(TimeFromYear(year) / msPerDay);
monthday = DayFromMonth(month, leap);
result = yearday
+ monthday
+ date - 1;
return result;
}
#define MakeDate(day, time) (day * msPerDay + time)
#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
&& !((d < 0 ? -d : d) > HalfTimeDomain)) \
? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
/**
* end of ECMA 'support' functions
*/
/*
* Other Support routines and definitions
*/
static JSClass date_class = {
js_Date_str,
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
/* for use by date_parse */
static const char* wtb[] = {
"am", "pm",
"monday", "tuesday", "wednesday", "thursday", "friday",
"saturday", "sunday",
"january", "february", "march", "april", "may", "june",
"july", "august", "september", "october", "november", "december",
"gmt", "ut", "utc",
"est", "edt",
"cst", "cdt",
"mst", "mdt",
"pst", "pdt"
/* time zone table needs to be expanded */
};
static int ttb[] = {
-1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
};
/* helper for date_parse */
static JSBool
date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
int count, int ignoreCase)
{
JSBool result = JS_FALSE;
/* return true if matches, otherwise, false */
while (count > 0 && s1[s1off] && s2[s2off]) {
if (ignoreCase) {
if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
break;
}
} else {
if ((jschar)s1[s1off] != s2[s2off]) {
break;
}
}
s1off++;
s2off++;
count--;
}
if (count == 0) {
result = JS_TRUE;
}
return result;
}
/* find UTC time from given date... no 1900 correction! */
static jsdouble
date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
jsdouble min, jsdouble sec, jsdouble msec)
{
jsdouble day;
jsdouble msec_time;
jsdouble result;
day = MakeDay(year, mon, mday);
msec_time = MakeTime(hour, min, sec, msec);
result = MakeDate(day, msec_time);
return result;
}
/*
* See ECMA 15.9.4.[3-10];
*/
/* XXX this function must be above date_parseString to avoid a
horrid bug in the Win16 1.52 compiler */
#define MAXARGS 7
static JSBool
date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble array[MAXARGS];
uintN loop;
jsdouble d;
for (loop = 0; loop < MAXARGS; loop++) {
if (loop < argc) {
if (!js_ValueToNumber(cx, argv[loop], &d))
return JS_FALSE;
/* return NaN if any arg is NaN */
if (!JSDOUBLE_IS_FINITE(d)) {
return js_NewNumberValue(cx, d, rval);
}
array[loop] = floor(d);
} else {
array[loop] = 0;
}
}
/* adjust 2-digit years into the 20th century */
if (array[0] >= 0 && array[0] <= 99)
array[0] += 1900;
/* if we got a 0 for 'date' (which is out of range)
* pretend it's a 1. (So Date.UTC(1972, 5) works) */
if (array[2] < 1)
array[2] = 1;
d = date_msecFromDate(array[0], array[1], array[2],
array[3], array[4], array[5], array[6]);
d = TIMECLIP(d);
return js_NewNumberValue(cx, d, rval);
}
static JSBool
date_parseString(JSString *str, jsdouble *result)
{
jsdouble msec;
const jschar *s = JSSTRING_CHARS(str);
size_t limit = JSSTRING_LENGTH(str);
size_t i = 0;
int year = -1;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?