From 7c6e1131653d055ed619e8d61bb057e3bc0183ae Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 15:19:11 +0000 Subject: [PATCH 01/13] Limiting date library to 1601, and making sure it doesn't have a Y2K38 problem. --- src/jswrap_date.c | 5 ++++- tests/test_date.js | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 7697eed866..0d40c89031 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -27,7 +27,8 @@ const char *DAYNAMES = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; // https://github.com/deirdreobyrne/CalendarAndDST int getDayNumberFromDate(int y, int m, int d) { int ans; - + + if (y<1601) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); if (m < 2) { y--; m+=12; @@ -41,6 +42,8 @@ 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; + + if (day < -134774) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); a = (a-(a/146097)+146095)/36524; a = day + a - (a>>2); b = ((a<<2)+2877911)/1461; diff --git a/tests/test_date.js b/tests/test_date.js index 71ddd81300..ad552e8d39 100644 --- a/tests/test_date.js +++ b/tests/test_date.js @@ -11,7 +11,8 @@ 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").toISOString(), "5000-01-01T00:00:00.000Z"] ]; gmt.forEach(function(n) { if (n[0]==n[1]) pass++; }); From 9801c12edbc38173f3093f706036a2a432feaca2 Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 15:30:00 +0000 Subject: [PATCH 02/13] Fixing the integer divide rounds towards zero problem instetad. Hopefully this will compile on all platforms. --- src/jswrap_date.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 0d40c89031..18ac044daf 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -28,13 +28,15 @@ const char *DAYNAMES = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; int getDayNumberFromDate(int y, int m, int d) { int ans; - if (y<1601) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); + // if (y<1601) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); if (m < 2) { y--; m+=12; } - ans = (y/100); - return 365*y + (y>>2) - ans + (ans>>2) + 30*m + ((3*m+6)/5) + d - 719531; + ans = (y<0) ? ((y-99)/100) : (y/100); // FLOOR + d += (ans < 0) ? ((ans - 3)/4) : (ans/4); // FLOOR + d += (y<0) ? ((y-3)/4) : (y/4); // FLOOR + return 365*y - ans + 30*m + ((3*m+6)/5) + d - 719531; } // Convert a number of days since 1970 into y,m,d. 0<=m<=11 @@ -43,24 +45,26 @@ void getDateFromDayNumber(int day, int *y, int *m, int *date) { int a = day + 135081; int b,c,d; - if (day < -134774) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); - 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; - if (date) *date=c-30*d-((3*d)/5); + // if (day < -134774) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); + a -= (a<0) ? ((a-146096)/146097) : (a/146097); + a += 146095; + a = (a < 0) ? ((a-36523)/36524) : (a/36524); + a = day + a - ((a < 0) ? ((a-3)/4) : (a/4)); + b = (int)(((a<<2)+2877911-((a<0) ? 1460 : 0))/1461); + c = (int)(a + 719600 - 365*b - (((b<0) ? (b-3) : b)/4)); + d = (5*c-1)/153; // Floor not needed, as c is always positive + if (date) *date=(int)(c-30*d-((3*d)/5)); if (m) { if (d<14) - *m=d-2; + *m=(int)(d-2); else - *m=d-14; + *m=(int)(d-14); } if (y) { if (d>13) - *y=b+1; + *y=(int)(b+1); else - *y=b; + *y=(int)b; } } From 131b91a86e916724a9161c83c6f545a86dee5a3a Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 17:10:14 +0000 Subject: [PATCH 03/13] Bugfix for #2456. Integer division rounds towards zero instead of down. Also need to limit the scope of the calendar to avoid overflows and maybe other nastiness. --- src/jswrap_date.c | 211 ++++++++++++++++++++++++++++----------------- tests/test_date.js | 5 ++ 2 files changed, 135 insertions(+), 81 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 18ac044daf..ce782b2963 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -23,16 +23,41 @@ 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"; +int checkDaySinceEpoch(int d) { + if (d<-462962961 || d>462962961) { + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return 0; + } + return -1; +} + +int checkYear(int y) { + if (y<-1265579 || y>1269518) { + jsExceptionHere(JSET_ERROR, "Year out of bounds"); + return 0; + } + return -1; +} + +int checkTime(JsVarFloat ms) { + if (ms < -4.0e-16 || ms > 4.0e16) { + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return 0; + } + return -1; +} + // 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 (y<1601) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); if (m < 2) { y--; m+=12; } + // #2456 was created by integer division rounding towards zero, rather than the FLOOR-behaviour required by the algorithm. + // We create the same effect as FLOOR by subtracting the divisor-minus-one from the dividend if the dividend is negative ans = (y<0) ? ((y-99)/100) : (y/100); // FLOOR d += (ans < 0) ? ((ans - 3)/4) : (ans/4); // FLOOR d += (y<0) ? ((y-3)/4) : (y/4); // FLOOR @@ -45,14 +70,14 @@ void getDateFromDayNumber(int day, int *y, int *m, int *date) { int a = day + 135081; int b,c,d; - // if (day < -134774) jsExceptionHere(JSET_ERROR, "Date library starts in 1601"); + // Bug #2456 fixed here too a -= (a<0) ? ((a-146096)/146097) : (a/146097); a += 146095; a = (a < 0) ? ((a-36523)/36524) : (a/36524); a = day + a - ((a < 0) ? ((a-3)/4) : (a/4)); b = (int)(((a<<2)+2877911-((a<0) ? 1460 : 0))/1461); c = (int)(a + 719600 - 365*b - (((b<0) ? (b-3) : b)/4)); - d = (5*c-1)/153; // Floor not needed, as c is always positive + d = (5*c-1)/153; // Floor behaviour not needed, as c is always positive if (date) *date=(int)(c-30*d-((3*d)/5)); if (m) { if (d<14) @@ -102,38 +127,40 @@ JsVarFloat getDstChangeTime(int y, int dow_number, int dow, int month, int day_o // https://github.com/deirdreobyrne/CalendarAndDST int jsdGetEffectiveTimeZone(JsVarFloat ms, bool is_local_time, bool *is_dst) { #ifndef ESPR_NO_DAYLIGHT_SAVING - JsVar *dst = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JS_DST_SETTINGS_VAR); - if ((dst) && (jsvIsArrayBuffer(dst)) && (jsvGetLength(dst) == 12) && (dst->varData.arraybuffer.type == ARRAYBUFFERVIEW_INT16)) { - int y; - JsVarInt dstSetting[12]; - JsvArrayBufferIterator it; - - jsvArrayBufferIteratorNew(&it, dst, 0); - y = 0; - while (y < 12) { - dstSetting[y++]=jsvArrayBufferIteratorGetIntegerValue(&it); - jsvArrayBufferIteratorNext(&it); - } - jsvArrayBufferIteratorFree(&it); - jsvUnLock(dst); - if (dstSetting[0]) { - JsVarFloat sec = ms/1000; - JsVarFloat dstStart,dstEnd; - bool dstActive; - - getDateFromDayNumber((int)(sec/86400),&y,0,0); - dstStart = getDstChangeTime(y, dstSetting[2], dstSetting[3], dstSetting[4], dstSetting[5], dstSetting[6], 1, dstSetting[0], dstSetting[1], is_local_time); - dstEnd = getDstChangeTime(y, dstSetting[7], dstSetting[8], dstSetting[9], dstSetting[10], dstSetting[11], 0, dstSetting[0], dstSetting[1], is_local_time); - if (dstStart < dstEnd) { // Northern hemisphere - dstActive = (sec >= dstStart) && (sec < dstEnd); - } else { // Southern hemisphere - dstActive = (sec < dstEnd) || (sec >= dstStart); + if (checkTime(ms)) { + JsVar *dst = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JS_DST_SETTINGS_VAR); + if ((dst) && (jsvIsArrayBuffer(dst)) && (jsvGetLength(dst) == 12) && (dst->varData.arraybuffer.type == ARRAYBUFFERVIEW_INT16)) { + int y; + JsVarInt dstSetting[12]; + JsvArrayBufferIterator it; + + jsvArrayBufferIteratorNew(&it, dst, 0); + y = 0; + while (y < 12) { + dstSetting[y++]=jsvArrayBufferIteratorGetIntegerValue(&it); + jsvArrayBufferIteratorNext(&it); } - if (is_dst) *is_dst = dstActive; - return dstActive ? dstSetting[0]+dstSetting[1] : dstSetting[1]; + jsvArrayBufferIteratorFree(&it); + jsvUnLock(dst); + if (dstSetting[0]) { + JsVarFloat sec = ms/1000; + JsVarFloat dstStart,dstEnd; + bool dstActive; + + getDateFromDayNumber((int)(sec/86400),&y,0,0); + dstStart = getDstChangeTime(y, dstSetting[2], dstSetting[3], dstSetting[4], dstSetting[5], dstSetting[6], 1, dstSetting[0], dstSetting[1], is_local_time); + dstEnd = getDstChangeTime(y, dstSetting[7], dstSetting[8], dstSetting[9], dstSetting[10], dstSetting[11], 0, dstSetting[0], dstSetting[1], is_local_time); + if (dstStart < dstEnd) { // Northern hemisphere + dstActive = (sec >= dstStart) && (sec < dstEnd); + } else { // Southern hemisphere + dstActive = (sec < dstEnd) || (sec >= dstStart); + } + if (is_dst) *is_dst = dstActive; + return dstActive ? dstSetting[0]+dstSetting[1] : dstSetting[1]; + } + } else { + jsvUnLock(dst); } - } else { - jsvUnLock(dst); } #endif if (is_dst) *is_dst = false; @@ -150,22 +177,25 @@ void setCorrectTimeZone(TimeInDay *td) { * condense them into one op. */ TimeInDay getTimeFromMilliSeconds(JsVarFloat ms_in, bool forceGMT) { TimeInDay t; - t.zone = forceGMT ? 0 : jsdGetEffectiveTimeZone(ms_in, false, &(t.is_dst)); - ms_in += t.zone*60000; - t.daysSinceEpoch = (int)(ms_in / MSDAY); - - if (forceGMT) t.is_dst = false; - int ms = (int)(ms_in - ((JsVarFloat)t.daysSinceEpoch * MSDAY)); - if (ms<0) { - ms += MSDAY; - t.daysSinceEpoch--; + if (checkTime(ms_in)) { + t.zone = forceGMT ? 0 : jsdGetEffectiveTimeZone(ms_in, false, &(t.is_dst)); + ms_in += t.zone*60000; + t.daysSinceEpoch = (int)(ms_in / MSDAY); + + if (ms_in < -4.0e-16 || ms_in > 4.0e16) jsExceptionHere(JSET_ERROR, "Date out of bounds"); + if (forceGMT) t.is_dst = false; + int ms = (int)(ms_in - ((JsVarFloat)t.daysSinceEpoch * MSDAY)); + if (ms<0) { + ms += MSDAY; + t.daysSinceEpoch--; + } + int s = ms / 1000; + t.ms = ms % 1000; + t.hour = s / 3600; + s = s % 3600; + t.min = s/60; + t.sec = s%60; } - int s = ms / 1000; - t.ms = ms % 1000; - t.hour = s / 3600; - s = s % 3600; - t.min = s/60; - t.sec = s%60; return t; } @@ -177,24 +207,29 @@ JsVarFloat fromTimeInDay(TimeInDay *td) { CalendarDate getCalendarDate(int d) { CalendarDate date; - getDateFromDayNumber(d, &date.year, &date.month, &date.day); - date.daysSinceEpoch = d; - // Calculate day of week. Sunday is 0 - date.dow=(date.daysSinceEpoch+BASE_DOW)%7; - if (date.dow<0) date.dow+=7; + if (checkDaySinceEpoch(d)) { + getDateFromDayNumber(d, &date.year, &date.month, &date.day); + date.daysSinceEpoch = d; + // Calculate day of week. Sunday is 0 + date.dow=(date.daysSinceEpoch+BASE_DOW)%7; + if (date.dow<0) date.dow+=7; + } return date; }; -int fromCalenderDate(CalendarDate *date) { - while (date->month < 0) { - date->year--; - date->month += 12; - } - while (date->month > 11) { - date->year++; - date->month -= 12; +int fromCalendarDate(CalendarDate *date) { + if (checkYear(date->year)) { + while (date->month < 0) { + date->year--; + date->month += 12; + } + while (date->month > 11) { + date->year++; + date->month -= 12; + } + return getDayNumberFromDate(date->year, date->month, date->day); } - return getDayNumberFromDate(date->year, date->month, date->day); + return 0; }; @@ -260,9 +295,12 @@ JsVarFloat jswrap_date_now() { JsVar *jswrap_date_from_milliseconds(JsVarFloat time) { - JsVar *d = jspNewObject(0,"Date"); - jswrap_date_setTime(d, time); - return d; + if (checkTime(time)) { + JsVar *d = jspNewObject(0,"Date"); + jswrap_date_setTime(d, time); + return d; + } + return jsvNewNull(); } @@ -299,13 +337,21 @@ JsVar *jswrap_date_constructor(JsVar *args) { else jsExceptionHere(JSET_TYPEERROR, "Variables of type %t are not supported in date constructor", arg); jsvUnLock(arg); + if (!checkTime(time)) { + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return jsvNewNull(); + } } else { CalendarDate date; date.year = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 0)); + if (!checkYear(date.year)) { + jsExceptionHere(JSET_ERROR,"Year out of bounds"); + return jsvNewNull(); + } 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))); @@ -614,7 +660,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)); } @@ -641,7 +687,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,16 +708,19 @@ JsVarFloat jswrap_date_setMonth(JsVar *parent, int monthValue, JsVar *dayValue) } */ JsVarFloat jswrap_date_setFullYear(JsVar *parent, int yearValue, JsVar *monthValue, JsVar *dayValue) { - TimeInDay td = getTimeFromDateVar(parent, false/*system timezone*/); - CalendarDate d = getCalendarDate(td.daysSinceEpoch); - d.year = yearValue; - if (jsvIsNumeric(monthValue)) - d.month = jsvGetInteger(monthValue); - if (jsvIsNumeric(dayValue)) - d.day = jsvGetInteger(dayValue); - td.daysSinceEpoch = fromCalenderDate(&d); - setCorrectTimeZone(&td); - return jswrap_date_setTime(parent, fromTimeInDay(&td)); + if (checkYear(yearValue)) { + TimeInDay td = getTimeFromDateVar(parent, false/*system timezone*/); + CalendarDate d = getCalendarDate(td.daysSinceEpoch); + d.year = yearValue; + if (jsvIsNumeric(monthValue)) + d.month = jsvGetInteger(monthValue); + if (jsvIsNumeric(dayValue)) + d.day = jsvGetInteger(dayValue); + td.daysSinceEpoch = fromCalendarDate(&d); + setCorrectTimeZone(&td); + return jswrap_date_setTime(parent, fromTimeInDay(&td)); + } + return jswrap_date_getTime(parent); } @@ -881,7 +930,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); @@ -904,7 +953,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); @@ -931,7 +980,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/tests/test_date.js b/tests/test_date.js index ad552e8d39..560e23e7a9 100644 --- a/tests/test_date.js +++ b/tests/test_date.js @@ -1,5 +1,10 @@ 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) { }; + var gmt = [ [ Date.parse("2011-10-20") , 1319068800000.0 ], [ Date.parse("2011-10-20T14:48:12.345") , 1319122092345.0 ], From a24ef40dd28cfc77e586da745606055d7a6345ba Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 17:14:38 +0000 Subject: [PATCH 04/13] Speling fix --- libs/filesystem/jswrap_fs.c | 2 +- libs/misc/nmea.c | 2 +- src/jswrap_date.h | 2 +- targets/nrf5x/bluetooth_ancs.c | 2 +- targets/stm32/jshardware.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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.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; From 68e1207617486af9676bf853a9a72d8773f2c570 Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 19:48:46 +0000 Subject: [PATCH 05/13] Trying to reduce memory usage --- src/jswrap_date.c | 204 +++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 122 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index ce782b2963..0d39d38f58 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -23,35 +23,15 @@ 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"; -int checkDaySinceEpoch(int d) { - if (d<-462962961 || d>462962961) { - jsExceptionHere(JSET_ERROR, "Date out of bounds"); - return 0; - } - return -1; -} - -int checkYear(int y) { - if (y<-1265579 || y>1269518) { - jsExceptionHere(JSET_ERROR, "Year out of bounds"); - return 0; - } - return -1; -} - -int checkTime(JsVarFloat ms) { - if (ms < -4.0e-16 || ms > 4.0e16) { - jsExceptionHere(JSET_ERROR, "Date out of bounds"); - return 0; - } - return -1; -} - // 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; + int ans = y<0 ? -y : y; + if (ans>1265579) { + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return 0; + } if (m < 2) { y--; m+=12; @@ -127,40 +107,38 @@ JsVarFloat getDstChangeTime(int y, int dow_number, int dow, int month, int day_o // https://github.com/deirdreobyrne/CalendarAndDST int jsdGetEffectiveTimeZone(JsVarFloat ms, bool is_local_time, bool *is_dst) { #ifndef ESPR_NO_DAYLIGHT_SAVING - if (checkTime(ms)) { - JsVar *dst = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JS_DST_SETTINGS_VAR); - if ((dst) && (jsvIsArrayBuffer(dst)) && (jsvGetLength(dst) == 12) && (dst->varData.arraybuffer.type == ARRAYBUFFERVIEW_INT16)) { - int y; - JsVarInt dstSetting[12]; - JsvArrayBufferIterator it; - - jsvArrayBufferIteratorNew(&it, dst, 0); - y = 0; - while (y < 12) { - dstSetting[y++]=jsvArrayBufferIteratorGetIntegerValue(&it); - jsvArrayBufferIteratorNext(&it); - } - jsvArrayBufferIteratorFree(&it); - jsvUnLock(dst); - if (dstSetting[0]) { - JsVarFloat sec = ms/1000; - JsVarFloat dstStart,dstEnd; - bool dstActive; - - getDateFromDayNumber((int)(sec/86400),&y,0,0); - dstStart = getDstChangeTime(y, dstSetting[2], dstSetting[3], dstSetting[4], dstSetting[5], dstSetting[6], 1, dstSetting[0], dstSetting[1], is_local_time); - dstEnd = getDstChangeTime(y, dstSetting[7], dstSetting[8], dstSetting[9], dstSetting[10], dstSetting[11], 0, dstSetting[0], dstSetting[1], is_local_time); - if (dstStart < dstEnd) { // Northern hemisphere - dstActive = (sec >= dstStart) && (sec < dstEnd); - } else { // Southern hemisphere - dstActive = (sec < dstEnd) || (sec >= dstStart); - } - if (is_dst) *is_dst = dstActive; - return dstActive ? dstSetting[0]+dstSetting[1] : dstSetting[1]; + JsVar *dst = jsvObjectGetChildIfExists(execInfo.hiddenRoot, JS_DST_SETTINGS_VAR); + if ((dst) && (jsvIsArrayBuffer(dst)) && (jsvGetLength(dst) == 12) && (dst->varData.arraybuffer.type == ARRAYBUFFERVIEW_INT16)) { + int y; + JsVarInt dstSetting[12]; + JsvArrayBufferIterator it; + + jsvArrayBufferIteratorNew(&it, dst, 0); + y = 0; + while (y < 12) { + dstSetting[y++]=jsvArrayBufferIteratorGetIntegerValue(&it); + jsvArrayBufferIteratorNext(&it); + } + jsvArrayBufferIteratorFree(&it); + jsvUnLock(dst); + if (dstSetting[0]) { + JsVarFloat sec = ms/1000; + JsVarFloat dstStart,dstEnd; + bool dstActive; + + getDateFromDayNumber((int)(sec/86400),&y,0,0); + dstStart = getDstChangeTime(y, dstSetting[2], dstSetting[3], dstSetting[4], dstSetting[5], dstSetting[6], 1, dstSetting[0], dstSetting[1], is_local_time); + dstEnd = getDstChangeTime(y, dstSetting[7], dstSetting[8], dstSetting[9], dstSetting[10], dstSetting[11], 0, dstSetting[0], dstSetting[1], is_local_time); + if (dstStart < dstEnd) { // Northern hemisphere + dstActive = (sec >= dstStart) && (sec < dstEnd); + } else { // Southern hemisphere + dstActive = (sec < dstEnd) || (sec >= dstStart); } - } else { - jsvUnLock(dst); + if (is_dst) *is_dst = dstActive; + return dstActive ? dstSetting[0]+dstSetting[1] : dstSetting[1]; } + } else { + jsvUnLock(dst); } #endif if (is_dst) *is_dst = false; @@ -177,25 +155,22 @@ void setCorrectTimeZone(TimeInDay *td) { * condense them into one op. */ TimeInDay getTimeFromMilliSeconds(JsVarFloat ms_in, bool forceGMT) { TimeInDay t; - if (checkTime(ms_in)) { - t.zone = forceGMT ? 0 : jsdGetEffectiveTimeZone(ms_in, false, &(t.is_dst)); - ms_in += t.zone*60000; - t.daysSinceEpoch = (int)(ms_in / MSDAY); - - if (ms_in < -4.0e-16 || ms_in > 4.0e16) jsExceptionHere(JSET_ERROR, "Date out of bounds"); - if (forceGMT) t.is_dst = false; - int ms = (int)(ms_in - ((JsVarFloat)t.daysSinceEpoch * MSDAY)); - if (ms<0) { - ms += MSDAY; - t.daysSinceEpoch--; - } - int s = ms / 1000; - t.ms = ms % 1000; - t.hour = s / 3600; - s = s % 3600; - t.min = s/60; - t.sec = s%60; + t.zone = forceGMT ? 0 : jsdGetEffectiveTimeZone(ms_in, false, &(t.is_dst)); + ms_in += t.zone*60000; + t.daysSinceEpoch = (int)(ms_in / MSDAY); + + if (forceGMT) t.is_dst = false; + int ms = (int)(ms_in - ((JsVarFloat)t.daysSinceEpoch * MSDAY)); + if (ms<0) { + ms += MSDAY; + t.daysSinceEpoch--; } + int s = ms / 1000; + t.ms = ms % 1000; + t.hour = s / 3600; + s = s % 3600; + t.min = s/60; + t.sec = s%60; return t; } @@ -207,29 +182,24 @@ JsVarFloat fromTimeInDay(TimeInDay *td) { CalendarDate getCalendarDate(int d) { CalendarDate date; - if (checkDaySinceEpoch(d)) { - getDateFromDayNumber(d, &date.year, &date.month, &date.day); - date.daysSinceEpoch = d; - // Calculate day of week. Sunday is 0 - date.dow=(date.daysSinceEpoch+BASE_DOW)%7; - if (date.dow<0) date.dow+=7; - } + getDateFromDayNumber(d, &date.year, &date.month, &date.day); + date.daysSinceEpoch = d; + // Calculate day of week. Sunday is 0 + date.dow=(date.daysSinceEpoch+BASE_DOW)%7; + if (date.dow<0) date.dow+=7; return date; }; int fromCalendarDate(CalendarDate *date) { - if (checkYear(date->year)) { - while (date->month < 0) { - date->year--; - date->month += 12; - } - while (date->month > 11) { - date->year++; - date->month -= 12; - } - return getDayNumberFromDate(date->year, date->month, date->day); + while (date->month < 0) { + date->year--; + date->month += 12; + } + while (date->month > 11) { + date->year++; + date->month -= 12; } - return 0; + return getDayNumberFromDate(date->year, date->month, date->day); }; @@ -295,12 +265,9 @@ JsVarFloat jswrap_date_now() { JsVar *jswrap_date_from_milliseconds(JsVarFloat time) { - if (checkTime(time)) { - JsVar *d = jspNewObject(0,"Date"); - jswrap_date_setTime(d, time); - return d; - } - return jsvNewNull(); + JsVar *d = jspNewObject(0,"Date"); + jswrap_date_setTime(d, time); + return d; } @@ -337,17 +304,9 @@ JsVar *jswrap_date_constructor(JsVar *args) { else jsExceptionHere(JSET_TYPEERROR, "Variables of type %t are not supported in date constructor", arg); jsvUnLock(arg); - if (!checkTime(time)) { - jsExceptionHere(JSET_ERROR, "Date out of bounds"); - return jsvNewNull(); - } } else { CalendarDate date; date.year = (int)jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 0)); - if (!checkYear(date.year)) { - jsExceptionHere(JSET_ERROR,"Year out of bounds"); - return jsvNewNull(); - } date.month = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 1))); date.day = (int)(jsvGetIntegerAndUnLock(jsvGetArrayItem(args, 2))); TimeInDay td; @@ -429,6 +388,10 @@ JsVarFloat jswrap_date_getTime(JsVar *date) { Set the time/date of this Date class */ JsVarFloat jswrap_date_setTime(JsVar *date, JsVarFloat timeValue) { + if (abs(timeValue) > 4.0e-16) { + jsExceptionHere(JSET_ERROR, "Date out of bounds"); + return 0.0; + } if (date) jsvObjectSetChildAndUnLock(date, "ms", jsvNewFromFloat(timeValue)); return timeValue; @@ -673,11 +636,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 */ @@ -708,19 +671,16 @@ JsVarFloat jswrap_date_setMonth(JsVar *parent, int monthValue, JsVar *dayValue) } */ JsVarFloat jswrap_date_setFullYear(JsVar *parent, int yearValue, JsVar *monthValue, JsVar *dayValue) { - if (checkYear(yearValue)) { - TimeInDay td = getTimeFromDateVar(parent, false/*system timezone*/); - CalendarDate d = getCalendarDate(td.daysSinceEpoch); - d.year = yearValue; - if (jsvIsNumeric(monthValue)) - d.month = jsvGetInteger(monthValue); - if (jsvIsNumeric(dayValue)) - d.day = jsvGetInteger(dayValue); - td.daysSinceEpoch = fromCalendarDate(&d); - setCorrectTimeZone(&td); - return jswrap_date_setTime(parent, fromTimeInDay(&td)); - } - return jswrap_date_getTime(parent); + TimeInDay td = getTimeFromDateVar(parent, false/*system timezone*/); + CalendarDate d = getCalendarDate(td.daysSinceEpoch); + d.year = yearValue; + if (jsvIsNumeric(monthValue)) + d.month = jsvGetInteger(monthValue); + if (jsvIsNumeric(dayValue)) + d.day = jsvGetInteger(dayValue); + td.daysSinceEpoch = fromCalendarDate(&d); + setCorrectTimeZone(&td); + return jswrap_date_setTime(parent, fromTimeInDay(&td)); } From 8a352849ac120d84aa215c52a7c85c48ac09995b Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 20:04:51 +0000 Subject: [PATCH 06/13] Correction to the date limit --- src/jswrap_date.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 0d39d38f58..3b1d9cbba1 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -388,7 +388,7 @@ JsVarFloat jswrap_date_getTime(JsVar *date) { Set the time/date of this Date class */ JsVarFloat jswrap_date_setTime(JsVar *date, JsVarFloat timeValue) { - if (abs(timeValue) > 4.0e-16) { + if (fabs(timeValue) > 4.0e16) { jsExceptionHere(JSET_ERROR, "Date out of bounds"); return 0.0; } From ec3ba5f2ce3210a9aa4917e39fe2f23ce8bb539a Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Sun, 28 Jan 2024 20:10:58 +0000 Subject: [PATCH 07/13] Cleanup from old attempt to fix the bug --- src/jswrap_date.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 3b1d9cbba1..5799604c48 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -30,7 +30,7 @@ int getDayNumberFromDate(int y, int m, int d) { if (ans>1265579) { jsExceptionHere(JSET_ERROR, "Date out of bounds"); - return 0; + return 0; // Need to head off any overflow error } if (m < 2) { y--; @@ -55,21 +55,21 @@ void getDateFromDayNumber(int day, int *y, int *m, int *date) { a += 146095; a = (a < 0) ? ((a-36523)/36524) : (a/36524); a = day + a - ((a < 0) ? ((a-3)/4) : (a/4)); - b = (int)(((a<<2)+2877911-((a<0) ? 1460 : 0))/1461); - c = (int)(a + 719600 - 365*b - (((b<0) ? (b-3) : b)/4)); + b = ((a<<2)+2877911-((a<0) ? 1460 : 0))/1461; + c = a + 719600 - 365*b - (((b<0) ? (b-3) : b)/4); d = (5*c-1)/153; // Floor behaviour not needed, as c is always positive - if (date) *date=(int)(c-30*d-((3*d)/5)); + if (date) *date=c-30*d-((3*d)/5); if (m) { if (d<14) - *m=(int)(d-2); + *m=d-2; else - *m=(int)(d-14); + *m=d-14; } if (y) { if (d>13) - *y=(int)(b+1); + *y=b+1; else - *y=(int)b; + *y=b; } } From 42000b31ae80be4edd8e4758e8d6c83db8a9f569 Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Mon, 29 Jan 2024 12:00:16 +0000 Subject: [PATCH 08/13] Adding in the test case sent in a private email by Gordon --- tests/test_date.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_date.js b/tests/test_date.js index 560e23e7a9..865af614c9 100644 --- a/tests/test_date.js +++ b/tests/test_date.js @@ -17,7 +17,8 @@ var gmt = [ [ 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("5000-1-1").toISOString(), "5000-01-01T00:00:00.000Z"] +[ new Date("5000-1-1").toISOString(), "5000-01-01T00:00:00.000Z"], +[ new Date(-12,8).toString(), "Wed Aug 31 -12 00:00:00 GMT+0000"] ]; gmt.forEach(function(n) { if (n[0]==n[1]) pass++; }); From b9b88a2298a713862ad249151c00078197b47eea Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Mon, 29 Jan 2024 12:58:56 +0000 Subject: [PATCH 09/13] Further restrictting the Date() range for the PICO_R1_3 --- boards/PICO_R1_3.py | 1 + src/jswrap_date.c | 37 +++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) 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/src/jswrap_date.c b/src/jswrap_date.c index 5799604c48..8f1f73ad00 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -23,12 +23,23 @@ 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 +#define INTEGER_DIVIDE_FLOOR(a,b) (a/b) +#else +#define INTEGER_DIVIDE_FLOOR(a,b) ((a<0 ? a-b+1 : 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 = y<0 ? -y : y; + int ans; - if (ans>1265579) { +#ifdef ESPR_LIMIT_DATE_RANGE + if (y < 1601 || y > 1250000) { +#else + if (y < -1265580 || y > 1269519) { +#endif jsExceptionHere(JSET_ERROR, "Date out of bounds"); return 0; // Need to head off any overflow error } @@ -38,9 +49,9 @@ int getDayNumberFromDate(int y, int m, int d) { } // #2456 was created by integer division rounding towards zero, rather than the FLOOR-behaviour required by the algorithm. // We create the same effect as FLOOR by subtracting the divisor-minus-one from the dividend if the dividend is negative - ans = (y<0) ? ((y-99)/100) : (y/100); // FLOOR - d += (ans < 0) ? ((ans - 3)/4) : (ans/4); // FLOOR - d += (y<0) ? ((y-3)/4) : (y/4); // FLOOR + ans = INTEGER_DIVIDE_FLOOR(y,100); + d += INTEGER_DIVIDE_FLOOR(ans,4); + d += INTEGER_DIVIDE_FLOOR(y,4); return 365*y - ans + 30*m + ((3*m+6)/5) + d - 719531; } @@ -51,12 +62,14 @@ void getDateFromDayNumber(int day, int *y, int *m, int *date) { int b,c,d; // Bug #2456 fixed here too - a -= (a<0) ? ((a-146096)/146097) : (a/146097); + a -= INTEGER_DIVIDE_FLOOR(a,146097); a += 146095; - a = (a < 0) ? ((a-36523)/36524) : (a/36524); - a = day + a - ((a < 0) ? ((a-3)/4) : (a/4)); - b = ((a<<2)+2877911-((a<0) ? 1460 : 0))/1461; - c = a + 719600 - 365*b - (((b<0) ? (b-3) : b)/4); + a = INTEGER_DIVIDE_FLOOR(a,36524); + a = day + a - INTEGER_DIVIDE_FLOOR(a,4); + b = (a<<2) + 2877911; + b = INTEGER_DIVIDE_FLOOR(b,1461); + c = INTEGER_DIVIDE_FLOOR(b,4); + c = a + 719600 - 365*b - c; 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) { @@ -388,7 +401,11 @@ 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.16e13 || timeValue > 3.0e16) +#else if (fabs(timeValue) > 4.0e16) { +#endif jsExceptionHere(JSET_ERROR, "Date out of bounds"); return 0.0; } From a2552d643373bcfd8558391527c2a0e3c69269b4 Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Mon, 29 Jan 2024 13:07:57 +0000 Subject: [PATCH 10/13] Missing { in the PICO_R1_3 code --- src/jswrap_date.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 8f1f73ad00..b88427ae6a 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -402,7 +402,7 @@ Set the time/date of this Date class */ JsVarFloat jswrap_date_setTime(JsVar *date, JsVarFloat timeValue) { #ifdef ESPR_LIMIT_DATE_RANGE - if (timeValue < -1.16e13 || timeValue > 3.0e16) + if (timeValue < -1.16e13 || timeValue > 3.0e16) { #else if (fabs(timeValue) > 4.0e16) { #endif From fcaadd83528e3995bf9afe5992bff2e442d0435c Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Mon, 29 Jan 2024 16:28:42 +0000 Subject: [PATCH 11/13] Fixed Date test; cleaned up Date() library range; checking to see if Pico now uses more or less memory. --- src/jswrap_date.c | 16 +++++++++++----- tests/test_date.js | 7 +++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index b88427ae6a..d5bea5285e 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -24,7 +24,13 @@ const char *MONTHNAMES = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\ const char *DAYNAMES = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; #ifdef ESPR_LIMIT_DATE_RANGE -#define INTEGER_DIVIDE_FLOOR(a,b) (a/b) + +int integerDivideFloor(int a, int b) { + return a/b; +} + +#define INTEGER_DIVIDE_FLOOR(a,b) integerDivideFloor(a,b) + #else #define INTEGER_DIVIDE_FLOOR(a,b) ((a<0 ? a-b+1 : a)/b) #endif @@ -36,9 +42,9 @@ int getDayNumberFromDate(int y, int m, int d) { int ans; #ifdef ESPR_LIMIT_DATE_RANGE - if (y < 1601 || y > 1250000) { + if (y < 1500 || y >= 1250000) { // Should actually work down to 1101, but since the Gregorian calendar started in 1582 . . . #else - if (y < -1265580 || y > 1269519) { + if (y < -1250000 || y >= 1250000) { #endif jsExceptionHere(JSET_ERROR, "Date out of bounds"); return 0; // Need to head off any overflow error @@ -402,9 +408,9 @@ Set the time/date of this Date class */ JsVarFloat jswrap_date_setTime(JsVar *date, JsVarFloat timeValue) { #ifdef ESPR_LIMIT_DATE_RANGE - if (timeValue < -1.16e13 || timeValue > 3.0e16) { + if (timeValue < -1.48317696e13 || timeValue >= 3.93840543168E+016) { // This should actually work down to 1101AD . . . #else - if (fabs(timeValue) > 4.0e16) { + if (timeValue < -3.95083256832E+016 || timeValue >= 3.93840543168E+016) { #endif jsExceptionHere(JSET_ERROR, "Date out of bounds"); return 0.0; diff --git a/tests/test_date.js b/tests/test_date.js index 865af614c9..905a963277 100644 --- a/tests/test_date.js +++ b/tests/test_date.js @@ -4,6 +4,8 @@ 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 ], @@ -17,8 +19,9 @@ var gmt = [ [ 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("5000-1-1").toISOString(), "5000-01-01T00:00:00.000Z"], -[ new Date(-12,8).toString(), "Wed Aug 31 -12 00:00:00 GMT+0000"] +[ 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++; }); From 307238c0cf94731558a97725e57386b44425b9dd Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Mon, 29 Jan 2024 16:43:18 +0000 Subject: [PATCH 12/13] Saving a few bytes of memory by pushing the integer divide into a function --- src/jswrap_date.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index d5bea5285e..d444f8091b 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -24,15 +24,15 @@ const char *MONTHNAMES = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\ 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 int integerDivideFloor(int a, int b) { return a/b; } - -#define INTEGER_DIVIDE_FLOOR(a,b) integerDivideFloor(a,b) - #else -#define INTEGER_DIVIDE_FLOOR(a,b) ((a<0 ? a-b+1 : a)/b) +// This rounds down, which is what the algorithm needs +int integerDivideFloor(int a, int b) { + return (a < 0 ? a-b+1 : a)/b; +} #endif @@ -49,16 +49,13 @@ int getDayNumberFromDate(int y, int m, int d) { jsExceptionHere(JSET_ERROR, "Date out of bounds"); return 0; // Need to head off any overflow error } - if (m < 2) { + while (m < 2) { y--; m+=12; } // #2456 was created by integer division rounding towards zero, rather than the FLOOR-behaviour required by the algorithm. - // We create the same effect as FLOOR by subtracting the divisor-minus-one from the dividend if the dividend is negative - ans = INTEGER_DIVIDE_FLOOR(y,100); - d += INTEGER_DIVIDE_FLOOR(ans,4); - d += INTEGER_DIVIDE_FLOOR(y,4); - return 365*y - ans + 30*m + ((3*m+6)/5) + d - 719531; + ans = integerDivideFloor(y,100); + return 365*y + integerDivideFloor(y,4) - ans + integerDivideFloor(ans,4) + 30*m + ((3*m+6)/5) + d - 719531; } // Convert a number of days since 1970 into y,m,d. 0<=m<=11 @@ -68,14 +65,10 @@ void getDateFromDayNumber(int day, int *y, int *m, int *date) { int b,c,d; // Bug #2456 fixed here too - a -= INTEGER_DIVIDE_FLOOR(a,146097); - a += 146095; - a = INTEGER_DIVIDE_FLOOR(a,36524); - a = day + a - INTEGER_DIVIDE_FLOOR(a,4); - b = (a<<2) + 2877911; - b = INTEGER_DIVIDE_FLOOR(b,1461); - c = INTEGER_DIVIDE_FLOOR(b,4); - c = a + 719600 - 365*b - c; + a = integerDivideFloor(a - integerDivideFloor(a,146097) + 146095,36524); + a = day + a - integerDivideFloor(a,4); + b = integerDivideFloor((a<<2)+2877911,1461); + c = a + 719600 - 365*b - integerDivideFloor(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) { From 38a5b0cdb23cfb43b2265cb2252232d40ace36a1 Mon Sep 17 00:00:00 2001 From: Deirdre O Byrne Date: Mon, 29 Jan 2024 16:53:37 +0000 Subject: [PATCH 13/13] Saving even more bytes --- src/jswrap_date.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/jswrap_date.c b/src/jswrap_date.c index d444f8091b..b087bc6ec7 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -25,14 +25,13 @@ 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 -int integerDivideFloor(int a, int b) { - return a/b; -} +#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 @@ -54,8 +53,8 @@ int getDayNumberFromDate(int y, int m, int d) { m+=12; } // #2456 was created by integer division rounding towards zero, rather than the FLOOR-behaviour required by the algorithm. - ans = integerDivideFloor(y,100); - return 365*y + integerDivideFloor(y,4) - ans + integerDivideFloor(ans,4) + 30*m + ((3*m+6)/5) + d - 719531; + 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 @@ -65,10 +64,10 @@ void getDateFromDayNumber(int day, int *y, int *m, int *date) { int b,c,d; // Bug #2456 fixed here too - a = integerDivideFloor(a - integerDivideFloor(a,146097) + 146095,36524); - a = day + a - integerDivideFloor(a,4); - b = integerDivideFloor((a<<2)+2877911,1461); - c = a + 719600 - 365*b - integerDivideFloor(b,4); + 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) {