@@ -856,18 +856,30 @@ object DateTimeUtils {
856856 private def getOffsetFromLocalMillis (millisLocal : Long , tz : TimeZone ): Long = {
857857 var guess = tz.getRawOffset
858858 // the actual offset should be calculated based on milliseconds in UTC
859- var actual = tz.getOffset(millisLocal - guess)
860- if (actual != guess) {
861- // try with the result as guess
862- guess = actual
863- actual = tz.getOffset(millisLocal - guess)
864- // At the start of DST, the local time is forwarded by an hour, so it's OK to get the
865- // local time bigger than current one after an round trip (local -> UTC -> local).
866- // At the end of DST, the local time is backwarded by an hour, actual offset will be
867- // less than guess, we should decrease the guess and try again.
868- while (actual < guess) {
869- guess = actual
870- actual = tz.getOffset(millisLocal - guess)
859+ val offset = tz.getOffset(millisLocal - guess)
860+ if (offset != guess) {
861+ guess = tz.getOffset(millisLocal - offset)
862+ if (guess != offset) {
863+ // fallback to do the reverse lookup using java.sql.Timestamp
864+ // this should only happen near the start or end of DST
865+ val days = Math .floor(millisLocal.toDouble / MILLIS_PER_DAY ).toInt
866+ val year = getYear(days)
867+ val month = getMonth(days)
868+ val day = getDayOfMonth(days)
869+
870+ var millisOfDay = (millisLocal % MILLIS_PER_DAY ).toInt
871+ if (millisOfDay < 0 ) {
872+ millisOfDay += MILLIS_PER_DAY .toInt
873+ }
874+ val seconds = (millisOfDay / 1000L ).toInt
875+ val hh = seconds / 3600
876+ val mm = seconds / 60 % 60
877+ val ss = seconds % 60
878+ val nano = millisOfDay % 1000 * 1000000
879+
880+ // create a Timestamp to get the unix timestamp (in UTC)
881+ val timestamp = new Timestamp (year - 1900 , month - 1 , day, hh, mm, ss, nano)
882+ guess = (millisLocal - timestamp.getTime).toInt
871883 }
872884 }
873885 guess
0 commit comments