diff --git a/boards/PICO_R1_3.py b/boards/PICO_R1_3.py index 3cff7a380a..e396c5265c 100644 --- a/boards/PICO_R1_3.py +++ b/boards/PICO_R1_3.py @@ -46,6 +46,7 @@ 'DEFINES+=-DUSE_USB_OTG_FS=1 -DPICO -DPICO_1V3', 'DEFINES+=-DPIN_NAMES_DIRECT=1', # Package skips out some pins, so we can't assume each port starts from 0 'DEFINES += -DESPR_USE_STEPPER_TIMER=1', # Build in the code for stepping using the timer + 'DEFINES += -DESPR_LIMIT_DATE_RANGE', # not enough code memory left for the full range of Date() 'STLIB=STM32F401xE', 'PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f4/lib/startup_stm32f401xx.o' ] diff --git a/libs/filesystem/jswrap_fs.c b/libs/filesystem/jswrap_fs.c index 89a3d6b849..5403091b8c 100755 --- a/libs/filesystem/jswrap_fs.c +++ b/libs/filesystem/jswrap_fs.c @@ -358,7 +358,7 @@ JsVar *jswrap_fs_stat(JsVar *path) { date.month = (int)(((info.fdate>>5)&15)-1); // TomWS: Month is 0 based. date.day = (int)((info.fdate)&31); TimeInDay td; - td.daysSinceEpoch = fromCalenderDate(&date); + td.daysSinceEpoch = fromCalendarDate(&date); td.hour = (int)((info.ftime>>11)&31); td.min = (int)((info.ftime>>5)&63); td.sec = (int)((info.ftime)&63); diff --git a/libs/misc/nmea.c b/libs/misc/nmea.c index df1ab2e4c4..f0ea3becba 100644 --- a/libs/misc/nmea.c +++ b/libs/misc/nmea.c @@ -184,7 +184,7 @@ JsVar *nmea_to_jsVar(NMEAFixInfo *gpsFix) { date.month = gpsFix->month-1; // 1 based to 0 based date.year = 2000+gpsFix->year; TimeInDay td; - td.daysSinceEpoch = fromCalenderDate(&date); + td.daysSinceEpoch = fromCalendarDate(&date); td.hour = gpsFix->hour; td.min = gpsFix->min; td.sec = gpsFix->sec; diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 7697eed866..b087bc6ec7 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -23,17 +23,38 @@ const int BASE_DOW = 4; const char *MONTHNAMES = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; const char *DAYNAMES = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; +#ifdef ESPR_LIMIT_DATE_RANGE +// This rounds towards zero - which is not what the algorithm needs. Hence the range for Date() is further limited when ESPR_LIMIT_DATE_RANGE is set +#define INTEGER_DIVIDE_FLOOR(a,b) ((a)/(b)) +#else +// This rounds down, which is what the algorithm needs +int integerDivideFloor(int a, int b) { + return (a < 0 ? a-b+1 : a)/b; +} +#define INTEGER_DIVIDE_FLOOR(a,b) integerDivideFloor((a),(b)) +#endif + + // Convert y,m,d into a number of days since 1970, where 0<=m<=11 // https://github.com/deirdreobyrne/CalendarAndDST int getDayNumberFromDate(int y, int m, int d) { int ans; - - if (m < 2) { + +#ifdef ESPR_LIMIT_DATE_RANGE + if (y < 1500 || y >= 1250000) { // Should actually work down to 1101, but since the Gregorian calendar started in 1582 . . . +#else + if (y < -1250000 || y >= 1250000) { +#endif + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return 0; // Need to head off any overflow error + } + while (m < 2) { y--; m+=12; } - ans = (y/100); - return 365*y + (y>>2) - ans + (ans>>2) + 30*m + ((3*m+6)/5) + d - 719531; + // #2456 was created by integer division rounding towards zero, rather than the FLOOR-behaviour required by the algorithm. + ans = INTEGER_DIVIDE_FLOOR(y,100); + return 365*y + INTEGER_DIVIDE_FLOOR(y,4) - ans + INTEGER_DIVIDE_FLOOR(ans,4) + 30*m + ((3*m+6)/5) + d - 719531; } // Convert a number of days since 1970 into y,m,d. 0<=m<=11 @@ -41,11 +62,13 @@ int getDayNumberFromDate(int y, int m, int d) { void getDateFromDayNumber(int day, int *y, int *m, int *date) { int a = day + 135081; int b,c,d; - a = (a-(a/146097)+146095)/36524; - a = day + a - (a>>2); - b = ((a<<2)+2877911)/1461; - c = a + 719600 - 365*b - (b>>2); - d = (5*c-1)/153; + + // Bug #2456 fixed here too + a = INTEGER_DIVIDE_FLOOR(a - INTEGER_DIVIDE_FLOOR(a,146097) + 146095,36524); + a = day + a - INTEGER_DIVIDE_FLOOR(a,4); + b = INTEGER_DIVIDE_FLOOR((a<<2)+2877911,1461); + c = a + 719600 - 365*b - INTEGER_DIVIDE_FLOOR(b,4); + d = (5*c-1)/153; // Floor behaviour not needed, as c is always positive if (date) *date=c-30*d-((3*d)/5); if (m) { if (d<14) @@ -178,7 +201,7 @@ CalendarDate getCalendarDate(int d) { return date; }; -int fromCalenderDate(CalendarDate *date) { +int fromCalendarDate(CalendarDate *date) { while (date->month < 0) { date->year--; date->month += 12; @@ -298,7 +321,7 @@ JsVar *jswrap_date_constructor(JsVar *args) { date.month = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 1))); date.day = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 2))); TimeInDay td; - td.daysSinceEpoch = fromCalenderDate(&date); + td.daysSinceEpoch = fromCalendarDate(&date); td.hour = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 3))); td.min = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 4))); td.sec = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 5))); @@ -376,6 +399,14 @@ JsVarFloat jswrap_date_getTime(JsVar *date) { Set the time/date of this Date class */ JsVarFloat jswrap_date_setTime(JsVar *date, JsVarFloat timeValue) { +#ifdef ESPR_LIMIT_DATE_RANGE + if (timeValue < -1.48317696e13 || timeValue >= 3.93840543168E+016) { // This should actually work down to 1101AD . . . +#else + if (timeValue < -3.95083256832E+016 || timeValue >= 3.93840543168E+016) { +#endif + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return 0.0; + } if (date) jsvObjectSetChildAndUnLock(date, "ms", jsvNewFromFloat(timeValue)); return timeValue; @@ -607,7 +638,7 @@ JsVarFloat jswrap_date_setDate(JsVar *parent, int dayValue) { TimeInDay td = getTimeFromDateVar(parent, false/*system timezone*/); CalendarDate d = getCalendarDate(td.daysSinceEpoch); d.day = dayValue; - td.daysSinceEpoch = fromCalenderDate(&d); + td.daysSinceEpoch = fromCalendarDate(&d); setCorrectTimeZone(&td); return jswrap_date_setTime(parent, fromTimeInDay(&td)); } @@ -620,11 +651,11 @@ JsVarFloat jswrap_date_setDate(JsVar *parent, int dayValue) { "name" : "setMonth", "generate" : "jswrap_date_setMonth", "params" : [ - ["yearValue","int","The month, between 0 and 11"], + ["monthValue","int","The month, between 0 and 11"], ["dayValue","JsVar","[optional] the day, between 0 and 31"] ], "return" : ["float","The number of milliseconds since 1970"], - "typescript" : "setMonth(yearValue: number, dayValue?: number): number;" + "typescript" : "setMonth(monthValue: number, dayValue?: number): number;" } Month of the year 0..11 */ @@ -634,7 +665,7 @@ JsVarFloat jswrap_date_setMonth(JsVar *parent, int monthValue, JsVar *dayValue) d.month = monthValue; if (jsvIsNumeric(dayValue)) d.day = jsvGetInteger(dayValue); - td.daysSinceEpoch = fromCalenderDate(&d); + td.daysSinceEpoch = fromCalendarDate(&d); setCorrectTimeZone(&td); return jswrap_date_setTime(parent, fromTimeInDay(&td)); } @@ -662,7 +693,7 @@ JsVarFloat jswrap_date_setFullYear(JsVar *parent, int yearValue, JsVar *monthVal d.month = jsvGetInteger(monthValue); if (jsvIsNumeric(dayValue)) d.day = jsvGetInteger(dayValue); - td.daysSinceEpoch = fromCalenderDate(&d); + td.daysSinceEpoch = fromCalendarDate(&d); setCorrectTimeZone(&td); return jswrap_date_setTime(parent, fromTimeInDay(&td)); } @@ -874,7 +905,7 @@ JsVarFloat jswrap_date_parse(JsVar *str) { jslGetNextToken(); if (lex.tk == LEX_INT) { date.year = _parse_int(); - time.daysSinceEpoch = fromCalenderDate(&date); + time.daysSinceEpoch = fromCalendarDate(&date); jslGetNextToken(); if (lex.tk == LEX_INT) { _parse_time(&time, 0); @@ -897,7 +928,7 @@ JsVarFloat jswrap_date_parse(JsVar *str) { jslGetNextToken(); if (lex.tk == LEX_INT) { date.year = _parse_int(); - time.daysSinceEpoch = fromCalenderDate(&date); + time.daysSinceEpoch = fromCalendarDate(&date); jslGetNextToken(); if (lex.tk == LEX_INT) { _parse_time(&time, 0); @@ -924,7 +955,7 @@ JsVarFloat jswrap_date_parse(JsVar *str) { jslGetNextToken(); if (lex.tk == LEX_INT) { date.day = _parse_int(); - time.daysSinceEpoch = fromCalenderDate(&date); + time.daysSinceEpoch = fromCalendarDate(&date); jslGetNextToken(); if (lex.tk == LEX_ID && jslGetTokenValueAsString()[0]=='T') { _parse_time(&time, 1); diff --git a/src/jswrap_date.h b/src/jswrap_date.h index 95bbb4eafc..0f6eee732b 100644 --- a/src/jswrap_date.h +++ b/src/jswrap_date.h @@ -41,7 +41,7 @@ void setCorrectTimeZone(TimeInDay *td); TimeInDay getTimeFromMilliSeconds(JsVarFloat ms_in, bool forceGMT); JsVarFloat fromTimeInDay(TimeInDay *td); CalendarDate getCalendarDate(int d); -int fromCalenderDate(CalendarDate *date); +int fromCalendarDate(CalendarDate *date); JsVarFloat jswrap_date_now(); JsVar *jswrap_date_from_milliseconds(JsVarFloat time); diff --git a/targets/nrf5x/bluetooth_ancs.c b/targets/nrf5x/bluetooth_ancs.c index 348c2acfb2..0da1c950c6 100644 --- a/targets/nrf5x/bluetooth_ancs.c +++ b/targets/nrf5x/bluetooth_ancs.c @@ -289,7 +289,7 @@ void ble_cts_handle_time(BLEPending blep, char *buffer, size_t bufferLen) { date.month = time.month-1; // JS months are 0-11, but CTS uses 1-12 date.day = time.day; TimeInDay td; - td.daysSinceEpoch = fromCalenderDate(&date); + td.daysSinceEpoch = fromCalendarDate(&date); td.hour = time.hours; td.min = time.minutes; td.sec = time.seconds; diff --git a/targets/stm32/jshardware.c b/targets/stm32/jshardware.c index 853a887963..9e48f9878b 100644 --- a/targets/stm32/jshardware.c +++ b/targets/stm32/jshardware.c @@ -1536,7 +1536,7 @@ JsSysTime jshGetRTCSystemTime() { cdate.month = date.RTC_Month-1; // 1..12 -> 0..11 cdate.year = 2000+date.RTC_Year; // 0..99 -> 2000..2099 cdate.dow = date.RTC_WeekDay%7; // 1(monday)..7 -> 0(sunday)..6 - ctime.daysSinceEpoch = fromCalenderDate(&cdate); + ctime.daysSinceEpoch = fromCalendarDate(&cdate); ctime.zone = 0; ctime.ms = 0; ctime.sec = time.RTC_Seconds; diff --git a/tests/test_date.js b/tests/test_date.js index 71ddd81300..905a963277 100644 --- a/tests/test_date.js +++ b/tests/test_date.js @@ -1,5 +1,12 @@ pass=0; +try { new Date(4.1e16); pass--; } catch(e) { }; +try { new Date(-4.1e16); pass--; } catch(e) { }; +try { new Date(2000000,0); pass--; } catch(e) { }; +try { new Date(-2000000,0); pass--; } catch(e) { }; +// This next Date() constructor use to give a segfault. However it is now out of range for the PICO_R1_3 +try { a = new Date(-12,8).toString(); if (!(a== "Wed Aug 31 -12 00:00:00 GMT+0000")) pass--; } catch (e) { }; + var gmt = [ [ Date.parse("2011-10-20") , 1319068800000.0 ], [ Date.parse("2011-10-20T14:48:12.345") , 1319122092345.0 ], @@ -11,7 +18,10 @@ var gmt = [ [ new Date(807926400000.0).toString() , "Wed Aug 9 1995 00:00:00 GMT+0000" ], [ new Date("Fri, 20 Jun 2014 15:27:22 GMT").toString(), "Fri Jun 20 2014 15:27:22 GMT+0000"], [ new Date("Fri, 20 Jun 2014 15:27:22 GMT").toISOString(), "2014-06-20T15:27:22.000Z"], -[ new Date("Fri, 20 Jun 2014 17:27:22 GMT+0200").toISOString(), "2014-06-20T15:27:22.000Z"] +[ new Date("Fri, 20 Jun 2014 17:27:22 GMT+0200").toISOString(), "2014-06-20T15:27:22.000Z"], +[ new Date("5000-1-1").toString(), "Wed Jan 1 5000 00:00:00 GMT+0000"], +[ new Date(1500,0,1).toString(), "Mon Jan 1 1500 00:00:00 GMT+0000"], +[ new Date(1500,0,1).getTime(), -14831769600000] ]; gmt.forEach(function(n) { if (n[0]==n[1]) pass++; });