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

#524 Don't change of time zone Id in a call to DateTimeZone.forID #525

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
32 changes: 31 additions & 1 deletion src/main/java/org/joda/time/DateTimeZone.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,36 @@ public static DateTimeZone forID(String id) {
throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
}

/**
* Same as {@link #forID(String) forID} but does not mapping of deprecated time zones.
*
* @param id the ID of the datetime zone, null means default
* @return the DateTimeZone object for the ID
* @throws IllegalArgumentException if the ID is not recognised
*/
public static DateTimeZone forExactID(String id) {
if (id == null) {
return getDefault();
}
if (id.equals("UTC")) {
return DateTimeZone.UTC;
}
DateTimeZone zone = getProvider().getExactZone(id);
if (zone != null) {
return zone;
}
if (id.startsWith("+") || id.startsWith("-")) {
int offset = parseOffset(id);
if (offset == 0L) {
return DateTimeZone.UTC;
} else {
id = printOffset(offset);
return fixedOffsetZone(id, offset);
}
}
throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
}

/**
* Gets a time zone instance for the specified offset to UTC in hours.
* This method assumes standard length hours.
Expand Down Expand Up @@ -1261,7 +1291,7 @@ private void readObject(ObjectInputStream in) throws IOException {
}

private Object readResolve() throws ObjectStreamException {
return forID(iID);
return forExactID(iID);
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/joda/time/tz/Provider.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public interface Provider {
*/
DateTimeZone getZone(String id);

/**
* Same as {@link #getZone(String) getZone} but does no mapping of deprecated time zones
*
* @param id String id
* @return null if not found
*/
DateTimeZone getExactZone(String id);

/**
* Returns an unmodifiable set of ids. All providers must at least
* support id "UTC".
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/joda/time/tz/UTCProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ public DateTimeZone getZone(String id) {
return null;
}

/**
* Returns {@link DateTimeZone#UTC UTC} for <code>"UTC"</code>, null
* otherwise.
*/
public DateTimeZone getExactZone(String id) {
if ("UTC".equalsIgnoreCase(id)) {
return DateTimeZone.UTC;
}
return null;
}

/**
* Returns a singleton collection containing only <code>"UTC"</code>.
*/
Expand Down
73 changes: 65 additions & 8 deletions src/main/java/org/joda/time/tz/ZoneInfoProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import java.lang.ref.SoftReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -157,22 +159,72 @@ public DateTimeZone getZone(String id) {
return null;
}

if (obj instanceof SoftReference<?>) {
if (obj instanceof Entry) {
// If this point is reached, mapping must link to another.
@SuppressWarnings("unchecked")
Entry<String, SoftReference<DateTimeZone>> entry = (Entry<String, SoftReference<DateTimeZone>>) obj;
return getZone(entry.getKey());
} else if (obj instanceof SoftReference<?>) {
@SuppressWarnings("unchecked")
SoftReference<DateTimeZone> ref = (SoftReference<DateTimeZone>) obj;
DateTimeZone tz = ref.get();
if (tz != null) {
return tz;
}
// Reference cleared; load data again.
return loadZoneData(id);
return loadZoneData(id, id);
} else if (id.equals(obj)) {
// Load zone data for the first time.
return loadZoneData(id);
return loadZoneData(id, id);
}

// If this point is reached, mapping must link to another.
return getZone((String)obj);
return getZone((String) obj);
}

/**
* Returns a DateTimeZone object for the the id, that includes the exact id as provided, no mapping of deprecated zone ids is done as in {@code getZone}.
*
* @param id the id to load
* @return the loaded zone
*/
public DateTimeZone getExactZone(String id) {
if (id == null) {
return null;
}

Object obj = iZoneInfoMap.get(id);
if (obj == null) {
return null;
}

if (obj instanceof Entry) {
// If this point is reached, mapping must link to another.
@SuppressWarnings("unchecked")
Entry<String, SoftReference<DateTimeZone>> entry = (Entry<String, SoftReference<DateTimeZone>>) obj;
SoftReference<DateTimeZone> ref = entry.getValue();
DateTimeZone tz = ref.get();
if (tz != null) {
return tz;
}
// Reference cleared; load data again.
return loadZoneData(entry.getKey(), id);
} else if (obj instanceof SoftReference<?>) {
@SuppressWarnings("unchecked")
SoftReference<DateTimeZone> ref = (SoftReference<DateTimeZone>) obj;
DateTimeZone tz = ref.get();
if (tz != null) {
return tz;
}
// Reference cleared; load data again.
return loadZoneData(id, id);
} else if (id.equals(obj)) {
// Load zone data for the first time.
return loadZoneData(id, id);
}

// If this point is reached, mapping must link to another.
return loadZoneData((String) obj, id);
}

/**
Expand Down Expand Up @@ -231,15 +283,20 @@ public InputStream run() {
/**
* Loads the time zone data for one id.
*
* @param id the id to load
* @param dataId the id of the time zone in the time zone data base (for renamed time zones this is the new one)
* @param id the id of the time zone in the returned object (for renamed time zones this is the old one)
* @return the zone
*/
private DateTimeZone loadZoneData(String id) {
private DateTimeZone loadZoneData(String dataId, String id) {
InputStream in = null;
try {
in = openResource(id);
in = openResource(dataId);
DateTimeZone tz = DateTimeZoneBuilder.readFrom(in, id);
iZoneInfoMap.put(id, new SoftReference<DateTimeZone>(tz));
if (!dataId.equals(id)) {
iZoneInfoMap.put(id, new SimpleEntry<String, SoftReference<DateTimeZone>>(dataId, new SoftReference<DateTimeZone>(tz)));
} else {
iZoneInfoMap.put(id, new SoftReference<DateTimeZone>(tz));
}
return tz;
} catch (IOException ex) {
uncaughtException(ex);
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/org/joda/time/TestDateTimeZone.java
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,9 @@ public Set getAvailableIDs() {
public DateTimeZone getZone(String id) {
return null;
}
public DateTimeZone getExactZone(String id) {
return null;
}
}
static class MockEmptyIDSProvider implements Provider {
public Set getAvailableIDs() {
Expand All @@ -610,6 +613,9 @@ public Set getAvailableIDs() {
public DateTimeZone getZone(String id) {
return null;
}
public DateTimeZone getExactZone(String id) {
return null;
}
}
static class MockNoUTCProvider implements Provider {
public Set getAvailableIDs() {
Expand All @@ -620,6 +626,9 @@ public Set getAvailableIDs() {
public DateTimeZone getZone(String id) {
return null;
}
public DateTimeZone getExactZone(String id) {
return null;
}
}
static class MockBadUTCProvider implements Provider {
public Set getAvailableIDs() {
Expand All @@ -631,6 +640,9 @@ public Set getAvailableIDs() {
public DateTimeZone getZone(String id) {
return null;
}
public DateTimeZone getExactZone(String id) {
return null;
}
}
static class MockOKProvider implements Provider {
public Set getAvailableIDs() {
Expand All @@ -642,6 +654,9 @@ public Set getAvailableIDs() {
public DateTimeZone getZone(String id) {
return DateTimeZone.UTC;
}
public DateTimeZone getExactZone(String id) {
return DateTimeZone.UTC;
}
}

//-----------------------------------------------------------------------
Expand Down
60 changes: 58 additions & 2 deletions src/test/java/org/joda/time/tz/TestCachedDateTimeZone.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,22 @@
*/
package org.joda.time.tz;

import org.joda.time.DateTimeZone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.time.ZoneId;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;

import junit.framework.TestCase;
import junit.framework.TestSuite;

import org.joda.time.DateTimeZone;

/**
* Test cases for FixedDateTimeZone.
*
Expand Down Expand Up @@ -55,6 +61,56 @@ protected void tearDown() throws Exception {
DateTimeZone.setDefault(originalDateTimeZone);
}

public void testRangoonDateTimeZone_MappingToNewId_Asia_Yangon() throws Exception {
DateTimeZone zoneOld = DateTimeZone.forID("Asia/Rangoon");
assertEquals(zoneOld.getID(), "Asia/Yangon");

DateTimeZone zoneNew = DateTimeZone.forID("Asia/Yangon");
assertEquals(zoneNew.getID(), "Asia/Yangon");
}

public void testRangoonDateTimeZone_NoMapping() throws Exception {
DateTimeZone zoneOld = DateTimeZone.forExactID("Asia/Rangoon");
assertEquals(zoneOld.getID(), "Asia/Rangoon");

DateTimeZone zoneNew = DateTimeZone.forID("Asia/Yangon");
assertEquals(zoneNew.getID(), "Asia/Yangon");
}

public void testRangoonJavaUtilTimeZone() throws Exception {
TimeZone zone = TimeZone.getTimeZone("Asia/Rangoon");
assertEquals(zone.getID(), "Asia/Rangoon");

TimeZone zoneNew = TimeZone.getTimeZone("Asia/Yangon");
assertEquals(zoneNew.getID(), "Asia/Yangon");
}

public void testRangoonJavaTimeZoneId() throws Exception {
ZoneId zone = ZoneId.of("Asia/Rangoon");
assertEquals(zone.getId(), "Asia/Rangoon");

ZoneId zoneNew = ZoneId.of("Asia/Yangon");
assertEquals(zoneNew.getId(), "Asia/Yangon");
}

public void testRangoonCachingForMappingCode() throws Exception {
DateTimeZone zoneYangon = DateTimeZone.forID("Asia/Rangoon");
DateTimeZone zoneRangoon = DateTimeZone.forExactID("Asia/Rangoon");
Field field_iZoneInfoMap = ZoneInfoProvider.class.getDeclaredField("iZoneInfoMap");
field_iZoneInfoMap.setAccessible(true);
Map<String, Object> iZoneInfoMap = (Map<String, Object>) field_iZoneInfoMap.get(DateTimeZone.getProvider());
Entry<String, SoftReference<DateTimeZone>> entryRangoon = (Entry<String, SoftReference<DateTimeZone>>) iZoneInfoMap.get("Asia/Rangoon");
assertEquals("Asia/Yangon", entryRangoon.getKey());
SoftReference<DateTimeZone> entryYangon = (SoftReference<DateTimeZone>) iZoneInfoMap.get("Asia/Yangon");
entryRangoon.getValue().clear();
entryYangon.clear();

assertEquals(DateTimeZone.forID("Asia/Rangoon"), zoneYangon);
assertEquals(DateTimeZone.forExactID("Asia/Rangoon"), zoneRangoon);
assertEquals(DateTimeZone.forID("Asia/Rangoon").getID(), "Asia/Yangon");
assertEquals(DateTimeZone.forExactID("Asia/Rangoon").getID(), "Asia/Rangoon");
}

public void test_caching() throws Exception {
CachedDateTimeZone zone1 = CachedDateTimeZone.forZone(DateTimeZone.forID("Europe/Paris"));
CachedDateTimeZone zone2 = CachedDateTimeZone.forZone(DateTimeZone.forID("Europe/Paris"));
Expand Down