#61 Distinguishing between floating and UTC and local times

open
nobody
Model (11)
5
2014-11-17
2010-03-17
Anonymous
No

It seems that Time actually supports three types of time forms:

(1) a time expressed at UTC (identified if Time.isUtc() == true)
(2) a floating time
(3) a time expressed at a particular timezone (a rooted time)

Cases (2) and (3) are hard to distinguish.

What I'd prefer is something more explicit: that a floating time's getTimeZone() == null, and that a rooted time has getTimeZone() != null.

The patch below change Time's constructors to support these three cases:

* Time({String|Date|long} time, TimeZone tz) — a rooted time
* Time({String|Date|long} time, boolean isUtc) — if true, then a UTC time, false then a floating time

I modified DateTime's uses of Time to match. I haven't changed DateTime's constructors to match, though I'd recommend it if you accept this change.

There's an interesting discrepancy still: a floating time is still rooted in some timezone. So the following code won't produce the result that it implies:

Time floating = new Time("090000", false); // a floating time
Time local = new Time(floating, TimeZone.getTimeZone("somewhere/else")); // express the floating time as a local time

It's almost tempting to remove the Time(Date, ...) constructors to force the developer to be aware of the issue. Alternatively we could add some additional protocol to Time like #refloat(TimeZone newTimeZone) or something similar.

=== modified file 'source/net/fortuna/ical4j/model/Time.java'
--- source/net/fortuna/ical4j/model/Time.java 2010-03-16 23:14:47 +0000
+++ source/net/fortuna/ical4j/model/Time.java 2010-03-16 23:42:59 +0000
@@ -54,6 +54,7 @@
private static final long serialVersionUID = -8401010870773304348L;

private boolean utc = false;
+ private TimeZone timezone = null;

/**
* FORM #1: LOCAL TIME.
@@ -69,16 +70,20 @@
* @param timezone a timezone for the instance
*/
public Time(final TimeZone timezone) {
- this(timezone, TimeZones.isUtc(timezone));
- }
-
- /**
- * @param timezone a timezone for the instance
- * @param utc indicates if the time is in UTC
- */
- public Time(final TimeZone timezone, boolean utc) {
- super(utc ? UTC_PATTERN : DEFAULT_PATTERN, Dates.PRECISION_SECOND, timezone);
+ super(DEFAULT_PATTERN, Dates.PRECISION_SECOND, timezone);
+ this.timezone = timezone;
getFormat().setTimeZone(timezone);
+ this.utc = false;
+ }
+
+ /**
+ * @param utc if true, the time is in UTC, otherwise floating
+ */
+ public Time(boolean utc) {
+ super(utc ? UTC_PATTERN : DEFAULT_PATTERN, Dates.PRECISION_SECOND,
+ utc ? getUtcTimeZone() : TimeZones.getDateTimeZone());
+ this.timezone = null;
+ getFormat().setTimeZone(utc ? getUtcTimeZone() : TimeZones.getDateTimeZone());
this.utc = utc;
}

@@ -87,73 +92,77 @@
* @param timezone a timezone for the instance
*/
public Time(final long time, final TimeZone timezone) {
- this(time, timezone, TimeZones.isUtc(timezone));
- }
-
- /**
- * @param time a time value in milliseconds from the epoch
- * @param timezone a timezone for the instance
- * @param utc indicates if the time is in UTC
- */
- public Time(final long time, final TimeZone timezone, boolean utc) {
- super(time, (utc ? UTC_PATTERN : DEFAULT_PATTERN), Dates.PRECISION_SECOND, timezone);
+ super(time, DEFAULT_PATTERN, Dates.PRECISION_SECOND, timezone);
+ this.timezone = timezone;
getFormat().setTimeZone(timezone);
+ this.utc = false;
+ }
+
+ /**
+ * @param time a time value in milliseconds from the epoch
+ * @param utc if true, then the time is in UTC, otherwise it is a floating time
+ */
+ public Time(final long time, boolean utc) {
+ super(time, (utc ? UTC_PATTERN : DEFAULT_PATTERN), Dates.PRECISION_SECOND,
+ utc ? getUtcTimeZone() : TimeZones.getDateTimeZone());
+ this.timezone = null;
+ getFormat().setTimeZone(utc ? getUtcTimeZone() : TimeZones.getDateTimeZone());
this.utc = utc;
}

- /**
+ /**
* @param time a time value in milliseconds from the epoch
* @param timezone a timezone for the instance
*/
public Time(final java.util.Date time, final TimeZone timezone) {
- this(time, timezone, TimeZones.isUtc(timezone));
+ this(time.getTime(), timezone);
}

/**
* @param time a time value as a Java date instance
* @param timezone a timezone for the instance
- * @param utc indicates if the time is in UTC
+ * @param utc if true, then the time is in UTC, otherwise it is a floating time
*/
- public Time(final java.util.Date time, final TimeZone timezone, boolean utc) {
- super(time.getTime(), (utc ? UTC_PATTERN : DEFAULT_PATTERN), Dates.PRECISION_SECOND, timezone);
- getFormat().setTimeZone(timezone);
- this.utc = utc;
+ public Time(final java.util.Date time, boolean utc) {
+ this(time.getTime(), utc);
}

private static final Pattern UTC_TIME_SPECIFICATION = Pattern.compile("[0-9]+Z");
/**
* Interpret a string representation of a time instance. If the string has
- * a trailing "Z", then interpret it as a UTC value. Otherwise interpret it
- * as a floating time
+ * a trailing "Z", then interpret it as a UTC value. Otherwise it is interpreted
+ * as a floating time.
* @param value the time string representing
* @throws ParseException where the specified value is not a valid time string
*/
public Time(String value) throws ParseException {
- this(value,
- UTC_TIME_SPECIFICATION.matcher(value).matches() ? TimeZone.getTimeZone(TimeZones.UTC_ID)
- : TimeZones.getDateTimeZone(),
- UTC_TIME_SPECIFICATION.matcher(value).matches());
+ this(value, UTC_TIME_SPECIFICATION.matcher(value).matches());
}

/**
- * @param value
- * @param timezone
+ * @param time a time value expressed as YYYYMMDD[Z]
+ * @param timezone a timezone for the instance; null if floating
* @throws ParseException where the specified value is not a valid time string
*/
public Time(String value, TimeZone timezone) throws ParseException {
- this(value, timezone, TimeZones.isUtc(timezone));
+ this(parseDate(value, timezone), timezone);
}

/**
- * @param value
- * @param timezone
- * @param utc
+ * @param time a time value in as YYYYMMDD
+ * @param timezone a timezone for the instance; null if floating
+ * @param utc indicates if the time is in UTC
* @throws ParseException where the specified value is not a valid time string
*/
- public Time(String value, TimeZone timezone, boolean utc) throws ParseException {
- this(parseDate(value, timezone), timezone, utc);
+ public Time(String value, boolean utc) throws ParseException {
+ this(parseDate(value, UTC_TIME_SPECIFICATION.matcher(value).matches() ? getUtcTimeZone()
+ : TimeZones.getDateTimeZone()), utc);
}

+ private static TimeZone getUtcTimeZone() {
+ return TimeZone.getTimeZone(TimeZones.UTC_ID);
+ }
+
private static java.util.Date parseDate(String value, TimeZone timezone) throws ParseException {
DateFormat df = new SimpleDateFormat(DEFAULT_PATTERN);
df.setTimeZone(timezone);
@@ -177,6 +186,6 @@
}

public java.util.TimeZone getTimeZone() {
- return getFormat().getTimeZone();
+ return timezone;
}
}

=== modified file 'source/net/fortuna/ical4j/model/DateTime.java'
--- source/net/fortuna/ical4j/model/DateTime.java 2010-03-16 23:14:47 +0000
+++ source/net/fortuna/ical4j/model/DateTime.java 2010-03-17 13:52:03 +0000
@@ -384,11 +384,12 @@
this.timezone = null;
if (utc) {
getFormat().setTimeZone(TimeZone.getTimeZone(TimeZones.UTC_ID));
+ time = new Time(time, true);
}
else {
- resetTimeZone();
+ getFormat().setTimeZone(TimeZone.getDefault());
+ time = new Time(time, TimeZone.getDefault());
}
- time = new Time(time, getFormat().getTimeZone(), utc);
}

/**
@@ -401,24 +402,15 @@
this.timezone = timezone;
if (timezone != null) {
getFormat().setTimeZone(timezone);
+ time = new Time(time, timezone);
}
else {
- resetTimeZone();
+ getFormat().setTimeZone(TimeZone.getDefault());
+ time = new Time(time, TimeZone.getDefault());
}
- time = new Time(time, getFormat().getTimeZone(), false);
}

/**
- * Reset the timezone to default.
- */
- private void resetTimeZone() {
- // use GMT timezone to avoid daylight savings rules affecting floating
- // time values..
- getFormat().setTimeZone(TimeZone.getDefault());
-// getFormat().setTimeZone(TimeZone.getTimeZone(TimeZones.GMT_ID));
- }
-
- /**
* Returns the current timezone associated with this date-time value.
* @return a Java timezone
*/

=== modified file 'test/net/fortuna/ical4j/model/TimeTest.java'
--- test/net/fortuna/ical4j/model/TimeTest.java 2010-03-16 23:14:47 +0000
+++ test/net/fortuna/ical4j/model/TimeTest.java 2010-03-17 14:01:12 +0000
@@ -121,11 +121,13 @@

// test Time(String) for various formats, including UTC formats
- suite.addTest(new TimeTest(new Time("020000", TimeZones.getDateTimeZone(), false), "020000"));
- suite.addTest(new TimeTest(new Time("080000", TimeZones.getDateTimeZone(), false), "080000"));
- suite.addTest(new TimeTest(new Time("093000", TimeZones.getDateTimeZone(), false), "093000"));
+ suite.addTest(new TimeTest(new Time("020000", false), "020000"));
+ suite.addTest(new TimeTest(new Time("080000", false), "080000"));
+ suite.addTest(new TimeTest(new Time("093000", false), "093000"));
+ suite.addTest(new TimeTest(new Time("093000", TimeZones.getDateTimeZone()), "093000"));

- suite.addTest(new TimeTest(new Time("093000Z", TimeZones.getDateTimeZone(), true), "093000Z"));
+ suite.addTest(new TimeTest(new Time("093000Z", true), "093000Z"));
+ suite.addTest(new TimeTest(new Time("093000Z", false), "093000"));

// test Time(String, TimeZone)
suite.addTest(new TimeTest(new Time("020000",
@@ -135,7 +137,9 @@
// other tests..
suite.addTest(new TimeTest("testInvalidTimeString"));
suite.addTest(new TimeTest("testTimeToStringEquals"));
+ suite.addTest(new TimeTest("testNormalTimes"));
suite.addTest(new TimeTest("testUtc"));
+ suite.addTest(new TimeTest("testFloating"));

return suite;
}
@@ -179,22 +183,42 @@
assertEquals(date1.toString(), date2.toString());
}

+ public void testNormalTimes() throws ParseException {
+ // ordinary date..
+ TimeZone tz = registry.getTimeZone("Australia/Melbourne");
+ time = new Time("093000", tz);
+ assertFalse(time.isUtc());
+ assertEquals(tz, time.getTimeZone());
+ assertEquals("093000", time.toString());
+ }
+
/**
* Test UTC date-times.
*/
public void testUtc() throws ParseException {
- // ordinary date..
- Time date1 = new Time("093000", registry.getTimeZone("Australia/Melbourne"));
- assertFalse(date1.isUtc());
-
+ Time time;
+
TimeZone utcTz = registry.getTimeZone(TimeZones.UTC_ID);
utcTz.setID(TimeZones.UTC_ID);

- // Defined in UTC
- Time date3 = new Time("093000", utcTz);
- assertTrue(date3.isUtc());
- assertEquals("093000Z", date3.toString());
+ // Defined in UTC, but is not UTC
+ time = new Time("093000", utcTz);
+ assertFalse(time.isUtc());
+ assertEquals("093000", time.toString());
+ assertEquals(utcTz, time.getTimeZone());
+
+ // Defined as UTC
+ time = new Time("093000", true);
+ assertTrue(time.isUtc());
+ assertEquals("093000Z", time.toString());
}

+ public void testFloating() throws ParseException {
+ Time time;
+
+ time = new Time("093000", false);
+ assertNull(time.getTimeZone());
+ assertEquals("093000", time.toString());
+ }

}

Discussion

  • Brian de Alwis

    Brian de Alwis - 2010-03-17

    Oops, my login session expired. This was submitted by briandealwis.

     
  • Brian de Alwis

    Brian de Alwis - 2010-04-30

    I'll resubmit this and attach the patch instead.

     

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks