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

Support ambiguous time #3786

Merged
merged 5 commits into from Nov 16, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 12 additions & 10 deletions src/coprocessor/codec/mysql/time/mod.rs
Expand Up @@ -89,13 +89,18 @@ fn ymd_hms_nanos<T: TimeZone>(
secs: u32,
nanos: u32,
) -> Result<DateTime<T>> {
tz.ymd_opt(year, month, day)
.and_hms_opt(hour, min, secs)
.single()
use chrono::NaiveDate;

// Note: We are not using `tz::from_ymd_opt` as suggested in chrono's README due to
// chronotope/chrono-tz #23.
// As a workaround, we first build a NaiveDate, then attach time zone information to it.
NaiveDate::from_ymd_opt(year, month, day)
.and_then(|date| date.and_hms_opt(hour, min, secs))
.and_then(|t| t.checked_add_signed(Duration::nanoseconds(i64::from(nanos))))
.and_then(|datetime| tz.from_local_datetime(&datetime).earliest())
Copy link
Member

Choose a reason for hiding this comment

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

Why is earliest chosen?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because MySQL choses earliest when there is ambiguous time.

For example, in America/Los_Angeles time zone: After Sunday, 06 November, 2011 01:59:59 AM: Clocks were moved backward to become Sunday, 06 November, 2011 01:00:00 AM. When giving 2011-11-06 01:59:59, it can be interpreted to the following two:

  • The time before transition: 2011-11-06 01:59:59 -7 (timestamp: 1320569999)

  • The time after transition: 2011-11-06 01:59:59 -8 (timestamp: 1320573599)

In MySQL:

mysql> SET time_zone = 'America/Los_Angeles';
Query OK, 0 rows affected (0.03 sec)

mysql> select unix_timestamp('2011-11-06 01:59:59');
+---------------------------------------+
| unix_timestamp('2011-11-06 01:59:59') |
+---------------------------------------+
|                            1320569999 |
+---------------------------------------+
1 row in set (0.01 sec)

mysql> select unix_timestamp('2011-11-06 02:00:00');
+---------------------------------------+
| unix_timestamp('2011-11-06 02:00:00') |
+---------------------------------------+
|                            1320573600 |
+---------------------------------------+
1 row in set (0.00 sec)

Copy link
Contributor

Choose a reason for hiding this comment

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

So earliest is equal to single when the conversion is unique?

Copy link
Member Author

Choose a reason for hiding this comment

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

.ok_or_else(|| {
box_err!(
"'{}-{}-{} {}:{}:{}.{:09}' is not a valid datetime",
"'{}-{}-{} {}:{}:{}.{:09}' is not a valid datetime in specified time zone",
year,
month,
day,
Expand Down Expand Up @@ -1035,23 +1040,20 @@ mod tests {

#[test]
fn test_parse_datetime_dst() {
// Ambiguous date times are commented out.
// See https://github.com/chronotope/chrono-tz/issues/23

let ok_tables = vec![
("Asia/Shanghai", "1988-04-09 23:59:59", 576604799),
("Asia/Shanghai", "1988-04-10 01:00:00", 576604800),
("Asia/Shanghai", "1988-09-11 00:00:00", 589910400),
("Asia/Shanghai", "1988-09-11 00:00:01", 589910401),
// ("Asia/Shanghai", "1988-09-10 23:59:59", 589906799), // ambiguous
// ("Asia/Shanghai", "1988-09-10 23:00:00", 589903200), // ambiguous
("Asia/Shanghai", "1988-09-10 23:59:59", 589906799), // ambiguous
("Asia/Shanghai", "1988-09-10 23:00:00", 589903200), // ambiguous
("Asia/Shanghai", "1988-09-10 22:59:59", 589903199),
("Asia/Shanghai", "2015-01-02 23:59:59", 1420214399),
("America/Los_Angeles", "1919-03-30 01:59:59", -1601820001),
("America/Los_Angeles", "1919-03-30 03:00:00", -1601820000),
("America/Los_Angeles", "2011-03-13 01:59:59", 1300010399),
("America/Los_Angeles", "2011-03-13 03:00:00", 1300010400),
// ("America/Los_Angeles", "2011-11-06 01:59:59", 1320569999), // ambiguous
("America/Los_Angeles", "2011-11-06 01:59:59", 1320569999), // ambiguous
("America/Los_Angeles", "2011-11-06 02:00:00", 1320573600),
("America/Toronto", "2013-11-18 11:55:00", 1384793700),
];
Expand Down