Skip to content

Commit ac3d2b5

Browse files
author
Davies Liu
committed
cover all timezone
1 parent 20904c4 commit ac3d2b5

File tree

3 files changed

+66
-11
lines changed

3 files changed

+66
-11
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -854,14 +854,18 @@ object DateTimeUtils {
854854
* Lookup the offset for given millis seconds since 1970-01-01 00:00:00 in a timezone.
855855
*/
856856
private def getOffsetFromLocalMillis(millisLocal: Long, tz: TimeZone): Long = {
857-
val rawOffset = tz.getRawOffset
857+
var guess = tz.getRawOffset
858858
// the actual offset should be calculated based on milliseconds in UTC
859-
var offset = tz.getOffset(millisLocal - rawOffset)
860-
if (offset != rawOffset) {
861-
// Could be in DST, try with best effort
862-
offset = tz.getOffset(millisLocal - offset)
859+
var actual = tz.getOffset(millisLocal - guess)
860+
// At the start of DST, the local time is forwarded by an hour, so it's OK to get the
861+
// local time bigger than current one after an round trip (local -> UTC -> local).
862+
// At the end of DST, the local time is backwarded by an hour, actual offset will be
863+
// less than guess, we should decrease the guess and try again.
864+
while (actual < guess) {
865+
guess = actual
866+
actual = tz.getOffset(millisLocal - guess)
863867
}
864-
offset
868+
guess
865869
}
866870

867871
/**
@@ -883,4 +887,14 @@ object DateTimeUtils {
883887
val offset = getOffsetFromLocalMillis(time / 1000L, tz)
884888
time - offset * 1000L
885889
}
890+
891+
/**
892+
* Re-initialize the current thread's thread locals. Exposed for testing.
893+
*/
894+
private[util] def resetThreadLocals(): Unit = {
895+
threadLocalGmtCalendar.remove()
896+
threadLocalLocalTimeZone.remove()
897+
threadLocalTimestampFormat.remove()
898+
threadLocalDateFormat.remove()
899+
}
886900
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.sql.catalyst.util
19+
20+
import java.util.TimeZone
21+
22+
/**
23+
* Helper functions for testing date and time functionality.
24+
*/
25+
object DateTimeTestUtils {
26+
27+
val ALL_TIMEZONES: Seq[TimeZone] = TimeZone.getAvailableIDs.toSeq.map(TimeZone.getTimeZone)
28+
29+
def withDefaultTimeZone[T](newDefaultTimeZone: TimeZone)(block: => T): T = {
30+
val originalDefaultTimeZone = TimeZone.getDefault
31+
try {
32+
DateTimeUtils.resetThreadLocals()
33+
TimeZone.setDefault(newDefaultTimeZone)
34+
block
35+
} finally {
36+
TimeZone.setDefault(originalDefaultTimeZone)
37+
DateTimeUtils.resetThreadLocals()
38+
}
39+
}
40+
}

sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/util/DateTimeUtilsSuite.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,11 +524,12 @@ class DateTimeUtilsSuite extends SparkFunSuite {
524524
}
525525

526526
test("daysToMillis and millisToDays") {
527-
(-1001 to 2000).foreach { d =>
528-
assert(millisToDays(daysToMillis(d)) === d)
529-
val date = toJavaTimestamp(daysToMillis(d) * 1000L)
530-
assert(date.getHours === 0)
531-
assert(date.getMinutes === 0)
527+
for (tz <- DateTimeTestUtils.ALL_TIMEZONES) {
528+
DateTimeTestUtils.withDefaultTimeZone(tz) {
529+
(-2000 to 2000).foreach { d =>
530+
assert(millisToDays(daysToMillis(d)) === d)
531+
}
532+
}
532533
}
533534
}
534535
}

0 commit comments

Comments
 (0)