Skip to content

Commit

Permalink
normalization of timezone ids
Browse files Browse the repository at this point in the history
see issue #756
  • Loading branch information
MenoData committed Mar 16, 2018
1 parent c0cbfdd commit 6986f6f
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 12 deletions.
113 changes: 103 additions & 10 deletions core/src/main/java/net/time4j/tz/Timezone.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ public abstract class Timezone
private static final String REPOSITORY_VERSION =
System.getProperty("net.time4j.tz.repository.version");

private static final Comparator<TZID> ID_COMPARATOR =
(o1, o2) -> o1.canonical().compareTo(o2.canonical());
private static final Comparator<TZID> ID_COMPARATOR = Comparator.comparing(TZID::canonical);

/**
* <p>This standard strategy which is also used by the JDK-class
Expand Down Expand Up @@ -169,7 +168,7 @@ public abstract class Timezone

private static final boolean ALLOW_SYSTEM_TZ_OVERRIDE = Boolean.getBoolean("net.time4j.allow.system.tz.override");

private static volatile ZonalKeys zonalKeys = null;
private static volatile ZonalKeys zonalKeys;
private static volatile Timezone currentSystemTZ = null;
private static volatile boolean cacheActive = true;
private static int softLimit = 11;
Expand Down Expand Up @@ -313,10 +312,7 @@ public String getVersion() {
try {
String zoneID = System.getProperty("user.timezone");

if (
"Z".equals(zoneID)
|| "UTC".equals(zoneID)
) {
if ("Z".equals(zoneID) || "UTC".equals(zoneID)) {
systemTZ = ZonalOffset.UTC.getModel();
} else if (zoneID != null) {
systemTZ = Timezone.getTZ(resolve(zoneID), zoneID, false);
Expand Down Expand Up @@ -714,6 +710,105 @@ public static Timezone of(

}

/**
* <p>Equivalent to {@code normalize(tzid.canonical())}. </p>
*
* @param tzid timezone id which might need normalization
* @return normalized identifier
* @throws IllegalArgumentException if given identifier is invalid (for example empty)
* @see #normalize(String)
* @since 4.36
*/
/*[deutsch]
* <p>&Auml;quivalent zu {@code normalize(tzid.canonical())}. </p>
*
* @param tzid timezone id which might need normalization
* @return normalized identifier
* @throws IllegalArgumentException if given identifier is invalid (for example empty)
* @see #normalize(String)
* @since 4.36
*/
public static TZID normalize(TZID tzid) {

return normalize(tzid.canonical());

}

/**
* <p>Tries to normalize given timezone identifier on the base of best efforts. </p>
*
* <p>This method is only capable of resolving old aliases to modern identifiers if the underlying
* {@code ZoneModelProvider} supports resolving of aliases. Fixed offsets like &quot;UTC+01&quot;
* can be resolved to instances of {@code ZonalOffset}. </p>
*
* @param tzid timezone id which might need normalization
* @return normalized identifier
* @throws IllegalArgumentException if given identifier is invalid (for example empty)
* @see TZID#canonical()
* @see ZonalOffset#parse(String)
* @since 4.36
*/
/*[deutsch]
* <p>Versucht das Beste, die angegebene Zeitzonenkennung zu einer gebr&auml;chlicheren Variante
* zu normalisieren. </p>
*
* <p>Diese Methode kann nur dann veraltete Aliaskennungen aufl&ouml;sen, wenn der zugrundeliegende
* {@code ZoneModelProvider} das unterst&uuml;tzt. Feste Zeitzonenverschiebungen wie &quot;UTC+01&quot;
* k&ouml;nnen zu Instanzen von {@code ZonalOffset} aufgel&ouml;st werden. </p>
*
* @param tzid timezone id which might need normalization
* @return normalized identifier
* @throws IllegalArgumentException if given identifier is invalid (for example empty)
* @see TZID#canonical()
* @see ZonalOffset#parse(String)
* @since 4.36
*/
public static TZID normalize(String tzid) {

String providerName = "";
String zoneKey = tzid;

for (int i = 0, n = zoneKey.length(); i < n; i++) {
if (zoneKey.charAt(i) == '~') {
providerName = zoneKey.substring(0, i);
zoneKey = zoneKey.substring(i + 1); // maybe empty string
break;
}
}

if (zoneKey.isEmpty()) {
throw new IllegalArgumentException("Empty zone identifier: " + tzid);
}

ZoneModelProvider provider = DEFAULT_PROVIDER;
boolean useDefault = (providerName.isEmpty() || providerName.equals(NAME_DEFAULT));

if (!useDefault && !providerName.equals("WINDOWS") && !providerName.equals("MILITARY")) {
provider = PROVIDERS.get(providerName);

if (provider == null) {
String msg;
if (providerName.equals(NAME_TZDB)) {
msg = "TZDB provider not available: ";
} else {
msg = "Timezone model provider not registered: ";
}
throw new IllegalArgumentException(msg + tzid);
}
}

String resolved;
String alias = zoneKey;
Map<String, String> aliases = provider.getAliases();

while ((resolved = aliases.get(alias)) != null) {
alias = resolved;
}

return resolve(alias);

}

/**
* <p>Gets the associated timezone identifier. </p>
*
Expand Down Expand Up @@ -1796,10 +1891,8 @@ private static class PlatformZoneProvider
@Override
public Set<String> getAvailableIDs() {

Set<String> ret = new HashSet<>();
String[] temp = java.util.TimeZone.getAvailableIDs();
ret.addAll(Arrays.asList(temp));
return ret;
return new HashSet<>(Arrays.asList(temp));

}

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/net/time4j/tz/ZonalOffset.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2017 Meno Hochschild, <http://www.menodata.de/>
* Copyright © 2013-2018 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (ZonalOffset.java) is part of project Time4J.
*
Expand Down Expand Up @@ -937,7 +937,7 @@ public String getStdFormatPattern(Locale locale) {
/**
* <p>Interpretiert eine kanonische Darstellung als Verschiebung. </p>
*
* <p>Unterst&uuml;tzt werden alle von den Methoden {@code canonical} oder
* <p>Unterst&uuml;tzt werden alle von den Methoden {@code canonical()} oder
* {@code toString()} produzierten Ausgaben. </p>
*
* @param offset zonal offset in canonical form to be parsed
Expand Down
7 changes: 7 additions & 0 deletions core/src/test/java/net/time4j/tz/OffsetTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,13 @@ public void serializeLongitudinal()
assertThat(offset, is(roundtrip(offset)));
}

@Test
public void normalize() {
assertThat(Timezone.normalize("GMT-07"), is(ZonalOffset.ofHours(OffsetSign.BEHIND_UTC, 7)));
assertThat(Timezone.normalize("UTC+1"), is(ZonalOffset.ofHours(OffsetSign.AHEAD_OF_UTC, 1)));
assertThat(Timezone.normalize("+05:30"), is(ZonalOffset.ofHoursMinutes(OffsetSign.AHEAD_OF_UTC, 5, 30)));
}

private static Object roundtrip(Object obj)
throws IOException, ClassNotFoundException {

Expand Down
7 changes: 7 additions & 0 deletions misc/src/test/java/net/time4j/tz/other/MilitaryZoneTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,11 @@ public void parse() throws ParseException {
assertThat(m, is(Moment.UNIX_EPOCH));
}

@Test
public void normalize() {
assertThat(
Timezone.normalize(MilitaryZone.BRAVO),
is(ZonalOffset.ofHours(OffsetSign.AHEAD_OF_UTC, 2)));
}

}
7 changes: 7 additions & 0 deletions misc/src/test/java/net/time4j/tz/other/WindowsZoneTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ public void serializeTimezone() throws IOException, ClassNotFoundException {
assertThat(tz, is(roundtrip(tz)));
}

@Test
public void normalize() {
assertThat(
Timezone.normalize(WindowsZone.of("Eastern Standard Time").resolveSmart(Locale.US)).canonical(),
is("America/New_York"));
}

private static Object roundtrip(Object obj)
throws IOException, ClassNotFoundException {

Expand Down

0 comments on commit 6986f6f

Please sign in to comment.