Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix for #2456 (PR #2230) - Date library bug fixes #2459

Merged
merged 13 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards/PICO_R1_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
]
Expand Down
2 changes: 1 addition & 1 deletion libs/filesystem/jswrap_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion libs/misc/nmea.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
69 changes: 50 additions & 19 deletions src/jswrap_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,52 @@ 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
// https://github.com/deirdreobyrne/CalendarAndDST
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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}
Expand All @@ -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
*/
Expand All @@ -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));
}
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/jswrap_date.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion targets/nrf5x/bluetooth_ancs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion targets/stm32/jshardware.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 11 additions & 1 deletion tests/test_date.js
Original file line number Diff line number Diff line change
@@ -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 ],
Expand All @@ -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++; });
Expand Down
Loading