Skip to content

Do not use local-timezone dates for doing date to string conversions to specific timezones. #1155

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

Closed
wants to merge 6 commits into from

Conversation

Jimbly
Copy link

@Jimbly Jimbly commented Jul 24, 2015

This should not change any general behavior, but fixes issues when you try to insert dates which are within your timezone offset of a daylight saving time switchover time.

Using the locale-specific Date accessor functions is certainly not valid because, if your local timezone includes daylight saving time, there is no possible Javascript date value which could return the string, for instance, "2015-03-08 02:30".
Instead, use the getUTC methods of the Date object after simply adjusting the timestamp by the desired timezone offset.

I had difficultly understanding what in the world the test_timezone.js test was doing, so I wrote a new one. The new test is specifically:
No matter what date is used, no matter what system timezone, no matter what node-mysql timezone, no matter what server-side timezone, if you insert a Javascript Date object, you should get the same Date object back (this was previously failing if that date object happened to be near daylight saving time boundaries in the local system timezone).
Additionally, the string representation of the date in MySQL should be in the timezone specified by the node-mysql timezone (I believe that is what the old test was trying to check). As far as I know the server-side MySQL TIME_ZONE variable doesn't affect DATETIME objects (however it does affect TIMESTAMP objects, which would currently fail these same tests, but that's probably a more significant change for another PR another day...).

After doing all of this, I went back to read through the old test_timezone, and saw it was suffering from the same general issues as SqlString.js - it was using the local timezone string representation of a date to store arbitrary dates (that typeCast function would just generate an invalid date object if run during one of the selected timezone offsets from 2:30am on the day daylight saving time kicks in, among other problems).

Rule of thumb with Javascript dates: if someone is calling .getTimezoneOffset() on a date object, they're probably doing it wrong.

This should fix #1045

…to specific timezones.

This is certainly not valid because, if your local timezone includes daylight saving time, there is no possible Javascript date value which could return the string, for instance, "2015-03-08 02:30".
Instead, use the getUTC methods of the Date object after simply adjusting the timestamp by the desired timezone offset.

I had difficultly understanding what in the world the test_timezone.js test was doing, so I wrote a new one. The new test is specifically:
No matter what date is used, no matter what system timezone, no matter what node-mysql timezone, no matter what server-side timezone, if you insert a Javascript Date object, you should get the same Date object back (this was previously failing if that date object happened to be near daylight saving time boundaries in the local system timezone).
Additionally, the string representation of the date in MySQL should be in the timezone specified by the node-mysql timezone.  As far as I know the server-side MySQL timezone variable doesn't affect DATETIME objects (however it does affect TIMESTAMP objects, which will currently fail a bunch of these tests, but that's probably a more significant change for another PR another day...).

After doing all of this, I went back to read through the old test_timezone, and saw it was suffering from the same general issues as SqlString.js - it was using the local timezone string representation of a date to store arbitrary dates (that typeCast function would just generate an invalid date object if run during one of the selected timezone offsets from 2:30am on the day daylight saving time kicks in, among other problems).

Rule of thumb with Javascript dates: if you're calling .getTimezoneOffset() on a date object, you're probably doing it wrong.
@dougwilson dougwilson self-assigned this Aug 9, 2015
@dougwilson
Copy link
Member

Thank you so much! Sorry it took me a while to get back to you. So oddly the test you add does not fail with the current code (see Travis CI build https://travis-ci.org/felixge/node-mysql/builds/74812812). Would it be possible to include a test that fails without your changes and passes with them?

@Jimbly
Copy link
Author

Jimbly commented Aug 9, 2015

So, my test fails on the current code depending on what timezone the system you're running the tests on (the previous tests would fail, I think, based on a combination of both system timezone and actual time of running the test).

I'll look into if there's a way to tell node/V8 (or some kind of mock) to use a specific timezone, so that the test would reproduce on any system. If you know of a good way of doing that, please let me know =).

@dougwilson
Copy link
Member

Ah, gotcha. I think the changes are definitely good :) My worry here is that if they are not at least failing on Travis CI without the change, we could very well break timezone handling again with the 3.0 refactor and not know since the tests won't fail.

I see you have SET TIME_ZONE="+00:00"-like commands in the tests. Is there not a way to just use that to set it to, say, Pacific?

@Jimbly
Copy link
Author

Jimbly commented Aug 9, 2015

Yeah, I completely agree, I do not want some other change to break this again =). I was hoping that, since the tests previously failed, that maybe Travis CI's local timezone was one with daylight saving time, but perhaps only some hosts that run tests are (or if it's not in one of the US timezones, the particular date values to try inserting that would fail might not be in my dates I put in my test).

So, first, the SET TIME_ZONE commands change the timezone on the communications channel with MySQL, that was not the source of the bug (but I still think it's good to test that regardless of that setting that dates work). The particular thing that was broken previously was based on the local system timezone (the timezone of V8 Date objects), not this. Second, there's no MySQL command to set the communications time zone to Pacific without first inserting a whole bunch of stuff into the mysql.timezone tables. There are shell scripts to populate those tables, but on my local vanilla installation of MySQL they tables were empty (or just had the handful or UTC timezones).

Jimbly added 5 commits August 13, 2015 21:51
…h daylight saving time.

Also fix bug where test was inserting multiple rows with effectively the same keys and reading back the wrong row.
This reverts commit f22f432.

Doing this, and pushing it so, we can see in the log that it fails on TravisCI
@Jimbly
Copy link
Author

Jimbly commented Aug 16, 2015

So, apparently, as far as I could tell, none of the date mocking libraries in Node handle mocking dates in a timezone with daylight saving time, so I had to write one. Can see in the commit log of this branch that the the new tests failed on TravisCI on the old code (date_fix~1). I think this PR is now ready to go.

@dougwilson
Copy link
Member

Thank you so much!

@Jimbly Jimbly deleted the date_fix branch August 17, 2015 16:59
@dougwilson
Copy link
Member

This change is now published to npm as 2.9.0

@Jimbly
Copy link
Author

Jimbly commented Aug 20, 2015

Great, looks good!

@mysqljs mysqljs locked and limited conversation to collaborators Nov 30, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Development

Successfully merging this pull request may close these issues.

Timezones integration tests intermittently failing
2 participants