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

Daylight Savings Time support #2230

Merged
merged 20 commits into from
Jul 7, 2022
Merged

Daylight Savings Time support #2230

merged 20 commits into from
Jul 7, 2022

Conversation

deirdreobyrne
Copy link
Contributor

@deirdreobyrne deirdreobyrne commented Jul 4, 2022

Adds E.setDST(), which gives Espruino rules for determining when daylight savings time starts and ends. Note that E.setTimeZone() will have no effect when daylight savings time rules are in effect. (I note that Gadgetbridge sets the time zone, so by disabling the effectiveness of E.setTimeZone() we prevent clashes between Gadgetbridge and this DST code when DST changes).

Note that one of the tests in tests/test_date.js was actually wrong. It was assuming GMT, when the timezone was explicitly set with E.setTimeZone(2).

DST checks have been added to tests/test_date.js, and all of them are now passing.

src/jswrap_date.c Outdated Show resolved Hide resolved

if (!jsvIsArray(params)) return;
if (jsvGetLength(params) != 12) return;
jsvIteratorNew(&it,params,JSIF_DEFINED_ARRAY_ElEMENTS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would just replacing the following with jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT16, params, 0, 0) work here? It'd save a bit of code space

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably. Still familiarising myself with the data types.

}

// Convert a number of days since 1970 into y,m,d. 0<=m<=11
void getDateFromDayNumber(int day, int *y, int *m, int *date) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any kind of reference for this that could be linked? This looks a lot like witchcraft to me and it might just be handy to know where it came from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is adopted from an algorithm in a book called "Astronomical Algorithms" by Jean Meeus. The book - from 1991 - is well out of print by now.

I'm in the process of doing my own write-up on these algorithms. I've managed to convince myself of the correctness of everything but the constant 2877911. I've also ran getDayNumberFromDate against getDateFromDayNumber, and they are consistent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It's a magic bit of code - very few people doing stuff like that any more :) Just a reference to "Astronomical Algorithms" would be fine but a link to your writeup seems even better ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to do quite a bit of work to the algorithm in "Astronomical Algorithms", as it had its year zero in the year 4713BC. I wanted my year zero to be 1970 to correspond with the zero of Unix time, and the conversion was quite the mind bend.

I just love algorithms like that - it was a joy to work on!

jsvArrayBufferIteratorNew(&it, dst, 0);
y = 0;
while (y < 12) {
JsVar *setting = jsvArrayBufferIteratorGetValue(&it);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you use dstSetting[y++]=jsvArrayBufferIteratorGetIntegerValue(&it) here? We try and avoid accessing ->varData directly usually (eg using jsvGetInteger) as it makes it easier if we need to tweak the internals.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"type" : "method",
"class" : "Date",
"name" : "toLocalISOString",
"generate" : "jswrap_date_toLocalISOString",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could add "ifndef":"SAVE_ON_FLASH" to try and save a few bytes on micro:bit here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@gfwilliams
Copy link
Member

Hi! This looks really good, thanks! I've just enabled the CI check, and there do seem to be some issues though:

  • ESP32/Pixl/Pico/MDBT42/etc builds fail because of td.zone = jsdGetTimeZone() in libs/filesystem/jswrap_fs.c (FAT32 filesystem) - it should be a pretty easy fix as I think it's what you're already doing with a call to setCorrectTimeZone(&td);?
    td.daysSinceEpoch = fromCalenderDate(&date);
  • micro:bit 1 build fails because this seems to use about 1400 bytes more than the previous code (so adding about 1% to code size). The stuff I suggested may help a little but I'm actually pretty surprised this adds so much - not sure if you have any thoughts here? I guess we could just do #ifndef ESPR_NO_DAYLIGHT_SAVING and add that define on micro:bit 1, which is already a slightly limited build anyway?
  • Are you using tabs? If you look at jswrap_date.c at 209/318 and other places, the indenting seems a bit off?

But thanks - this looks really neat. It'll be a great addition.

@deirdreobyrne
Copy link
Contributor Author

  • ESP32/Pixl/Pico/MDBT42/etc builds fail because of td.zone = jsdGetTimeZone() in libs/filesystem/jswrap_fs.c (FAT32 filesystem) - it should be a pretty easy fix as I think it's what you're already doing with a call to setCorrectTimeZone(&td);?

Yes - that should fix the problem

  • micro:bit 1 build fails because this seems to use about 1400 bytes more than the previous code (so adding about 1% to code size). The stuff I suggested may help a little but I'm actually pretty surprised this adds so much - not sure if you have any thoughts here? I guess we could just do #ifndef ESPR_NO_DAYLIGHT_SAVING and add that define on micro:bit 1, which is already a slightly limited build anyway?

I'm also surprised at the 1400 bytes this takes. If it fails again then I guess micro:bit will have to do without DST.

  • Are you using tabs? If you look at jswrap_date.c at 209/318 and other places, the indenting seems a bit off?

I've had horrible problems with tabs.

@deirdreobyrne
Copy link
Contributor Author

I've done a bit of refactoring so that the code should take a bit less space. Expect another commit probably later today.

@gfwilliams
Copy link
Member

Excellent - thanks!

1 similar comment
@gfwilliams
Copy link
Member

Excellent - thanks!

@deirdreobyrne
Copy link
Contributor Author

I think that's about as good as I can make the code. I've implemented the ESPR_NO_DAYLIGHT_SAVING for microbit1, but I can't get it to compile for that platform! Could you review and see if I did ESPR_NO_DAYLIGHT_SAVING correctly? Or am I doing something wrong with my attempts to compile the microbit1 version?

@gfwilliams
Copy link
Member

Great - thanks! I'll take a look tomorrow and will see about merging

@gfwilliams
Copy link
Member

This looks great to me - the microbit build seems to work fine as well. Merging now - Thanks for this, and for making those tweaks!

@gfwilliams gfwilliams merged commit 450aec5 into espruino:master Jul 7, 2022
@deirdreobyrne
Copy link
Contributor Author

This looks great to me - the microbit build seems to work fine as well. Merging now - Thanks for this, and for making those tweaks!

Curious. I still cannot get the microbit1 build to work - which means that it's possible the microbit1 could actually support DST. Worth trying it out?

@deirdreobyrne
Copy link
Contributor Author

OK - figured out why I wasn't able to compile for microbit1 - I didn't have RELEASE=1 in my makefile.

The microbit1 cannot run the DST code for want of a measly 332 bytes. So unless I can think of some way of squeezing out another 332 bytes we can call this project done.

@deirdreobyrne deirdreobyrne deleted the DST branch July 7, 2022 11:30
@gfwilliams
Copy link
Member

The microbit1 cannot run the DST code for want of a measly 332 bytes

Well yes, but even if you squeezed it down 332 bytes and got it to compile, it's means that the very next improvement (even if it's a bugfix) will likely break the compile.

microbit 1 is super limited and we leave a lot of stuff out of it already - so I think not having it built in is the right thing to do anyway.

@gfwilliams
Copy link
Member

Hi! I know this is a while ago, but a segfault issue was just reported at #2456

Some weirdness can be reproduced just by doing d = new Date(-12,8) which seems valid, and this looks like it's down to your replacement getDateFromDayNumber which comes from https://github.com/deirdreobyrne/CalendarAndDST

It's giving negative values for days/months in some cases if the date is before 1970.

Before your changes went in, this worked fine.

// before 
>d = new Date(-12,8)
=Date: Tue Aug 31 -12 00:00:00 GMT+0000

// after
>d = new Date(-12,8).ms
=-62524915200000
>d = new Date(-12,8)
=Date: Wed h -29 -11 00:00:00 GMT+0000

It seemed you had a really thorough write-up in https://github.com/deirdreobyrne/CalendarAndDST so I thought this was worth asking you about first

@deirdreobyrne
Copy link
Contributor Author

deirdreobyrne commented Jan 26, 2024 via email

@gfwilliams
Copy link
Member

gfwilliams commented Jan 26, 2024

Thanks! Well, from my point of view:

  • It shouldn't ever segfault (eg month always 0..11, day always 1..31)
  • pre-1970 at minimum (ideally 1582), if you do new Date(-1000,7,1) you should expect it'll say
    =Date: ... Jul 1 -1000 00:00:00 GMT+0000 in return. Right now it says Date: Fri ength -27 -999 00:00:00 GMT+0000

I don't think the Date<->millis thing matters pre-1582, but it'd be nice if within reason you could add one day's worth of milliseconds and get to the next day.

@deirdreobyrne
Copy link
Contributor Author

deirdreobyrne commented Jan 27, 2024 via email

@gfwilliams
Copy link
Member

Sorry for the delayed response - what you've managed with #2459 looks great though!

Having as wider range as possible using Gregorian and throwing an exception outside of that seems great

gfwilliams added a commit that referenced this pull request Jan 29, 2024
Bugfix for #2456 (PR #2230) - Date library bug fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants