org.embulk.util.timestamp.LegacyDateTimeZones Maven / Gradle / Ivy
Show all versions of embulk-util-timestamp Show documentation
/*
* Copyright 2019 The Embulk project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.embulk.util.timestamp;
import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.embulk.util.rubytime.RubyDateTimeZones;
/**
* This class contains a {@code static} method to interpret a timezone string
* in the historical manner of legacy Embulk.
*
* American timezones
*
* Legacy Embulk's {@code org.embulk.spi.time.TimestampParser} starts interpreting a timezone
* string from Joda-Time's {@link org.joda.time.format.DateTimeFormat} like below. It was before
* straightforward {@link org.joda.time.DateTimeZone#forID(java.lang.String) DateTimeZone#forID}
* and {@link org.joda.time.DateTimeZone#getAvailableIDs() DateTimeZone#getAvailableIDs}.
*
*
{@code DateTimeFormat.forPattern("z").parseMillis("PDT")}
*
* It had been in Embulk since
*
* {@code v0.1.0} until
*
* {@code v0.8.20}. GitHub Issue #860
* describes the details.
*
*
It unfortunately caused some wrong conversions for some American timezones: {@code "EST"},
* {@code "EDT"}, {@code "CST"}, {@code "CDT"}, {@code "MST"}, {@code "MDT"}, {@code "PST"}, and
* {@code "PDT"}. For example, {@code "PDT"} should be recognized as {@code "-07:00"} because
* {@code "PDT"} was a daylight saving time. But, {@code "PDT"} was actually recognized as
* {@code "-08:00"} along with {@code "PST"}.
*
*
It was because {@code "PDT"} and {@code "PST"} were aliases of {@code "America/Los_Angeles"}
* in Joda-Time, not aliases of a fixed offset. Joda-Time originally intends to recognize both
* {@code "PDT"} and {@code "PST"} to be aligned with the date-time with the timezone. For example,
* {@code "2017-08-20 12:34:56 PST"} goes to {@code "2017-08-20 12:34:56 America/Los_Angeles"},
* and this {@code "America/Los_Angeles"} is recognized as {@code "-07:00"} because the date was
* in the daylight saving time. Same for {@code "2017-08-20 12:34:56 PDT"}. This {@code "PDT"} is
* eventually recognized as the same {@code "-07:00"} because of the date as well.
*
*
However, {@code DateTimeFormat.forPattern("z").parseMillis("PDT")} was always converted to
* {@code "-08:00"} because the string, only {@code "PDT"}, does not have any date information.
* Legacy Embulk never recognized {@code "PDT"} as {@code "-07:00"} even for summer as a result.
*
*
JFYI, the set of special timezones comes from
*
* {@code DateTimeUtils#buildDefaultTimeZoneNames}.
*
*
"HST" and "ROC"
*
* Embulk replaced Joda-Time into {@linkplain java.time Java Date and Time API} when upgraded
* to Embulk v0.9. It started to use {@link java.time.ZoneId#of(String, java.util.Map) ZoneId.of}
* instead of {@link org.joda.time.DateTimeZone#forID(String) DateTimeZone.forId} at that time.
*
*
{@link java.time.ZoneId#of(String)} does not recognize {@code "HST"} nor {@code "ROC"} that
* are recognized by {@link org.joda.time.DateTimeZone#forID(String)}.
*
*
Available timezones in Joda-Time are described in
*
* Joda-Time - Java date and time API - Time Zones.
*
* @see org.joda.time.DateTimeUtils#getDefaultTimeZoneNames()
*/
public final class LegacyDateTimeZones {
private LegacyDateTimeZones() {
// No instantiation.
}
/**
* Converts a timezone string to {@link java.time.ZoneId} in legacy Embulk's manner.
*
*
It recognizes a timezone string in the following priority.
*
*
* - {@code "Z"} is always recognized as UTC in the highest priority.
*
- Some special timezone strings are recognized by
* {@link java.time.ZoneId#of(String, java.util.Map) ZoneId.of(String, Map)}
* with a predefined alias map like below for legacy historical reasons.
*
* - {@code "EST"} is recognized as {@code "-05:00"}.
*
- {@code "EDT"} is recognized as {@code "-05:00"}, too.
*
- {@code "CST"} is recognized as {@code "-06:00"}.
*
- {@code "CDT"} is recognized as {@code "-06:00"}, too.
*
- {@code "MST"} is recognized as {@code "-07:00"}.
*
- {@code "MDT"} is recognized as {@code "-07:00"}, too.
*
- {@code "PST"} is recognized as {@code "-08:00"}.
*
- {@code "PDT"} is recognized as {@code "-08:00"}, too.
*
- {@code "HST"} is recognized as {@code "-10:00"}.
*
- {@code "ROC"} is recognized as the same as {@code "Asia/Taipei"}.
*
* - Otherwise, the given timezone string is recognized as Ruby-compatible zone tab.
*
- If none of the above rules does not recognize the given timezone string, it returns {@code null}.
*
*
* Some of its time offset transition (e.g. daylight saving time) can be different from
* Embulk's actual historical transitions. But, the difference comes from their tz database
* version difference. The difference should be acceptable as users should expect tz database
* can be updated anytime.
*
* @param zoneName a timezone string
* @return a {@link java.time.ZoneId} converted
*/
public static ZoneId toZoneId(final String zoneName) {
if (zoneName == null) {
return null;
}
if (zoneName.equals("Z")) {
return ZoneOffset.UTC;
}
try {
return ZoneId.of(zoneName, ALIASES); // Is never returns null unless Exception is thrown.
} catch (final DateTimeException ex) {
// Fallback to Ruby's Date._strptime.
final int offsetInSeconds = RubyDateTimeZones.toOffsetInSeconds(zoneName);
if (offsetInSeconds != Integer.MIN_VALUE) {
return ZoneOffset.ofTotalSeconds(offsetInSeconds);
}
}
return null;
}
static {
final HashMap aliases = new HashMap<>();
aliases.put("EST", "-05:00");
aliases.put("EDT", "-05:00");
aliases.put("CST", "-06:00");
aliases.put("CDT", "-06:00");
aliases.put("MST", "-07:00");
aliases.put("MDT", "-07:00");
aliases.put("PST", "-08:00");
aliases.put("PDT", "-08:00");
aliases.put("HST", "-10:00");
aliases.put("ROC", "Asia/Taipei");
ALIASES = Collections.unmodifiableMap(aliases);
}
private static final Map ALIASES;
}