Skip to content

Commit

Permalink
Resolve timezone in date property so that TzId param conforms to immu…
Browse files Browse the repository at this point in the history
…tability
  • Loading branch information
benfortuna committed Mar 27, 2020
1 parent c37dd4a commit e0dd7b3
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 30 deletions.
11 changes: 2 additions & 9 deletions src/main/java/net/fortuna/ical4j/data/DefaultContentHandler.java
Expand Up @@ -3,7 +3,6 @@
import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.component.CalendarComponent;
import net.fortuna.ical4j.model.component.VTimeZone;
import net.fortuna.ical4j.model.parameter.TzId;
import net.fortuna.ical4j.util.Constants;

import java.io.IOException;
Expand Down Expand Up @@ -96,7 +95,8 @@ public void endComponent(String name) {

@Override
public void startProperty(String name) {
propertyBuilder = new PropertyBuilder().factories(propertyFactorySupplier.get()).name(name);
propertyBuilder = new PropertyBuilder().factories(propertyFactorySupplier.get())
.name(name).timeZoneRegistry(tzRegistry);
}

@Override
Expand Down Expand Up @@ -129,13 +129,6 @@ public void parameter(String name, String value) throws URISyntaxException {
Parameter parameter = new ParameterBuilder().factories(parameterFactorySupplier.get())
.name(name).value(value).build();

if (parameter instanceof TzId && tzRegistry != null) {
// VTIMEZONE may be defined later, so so keep
// track of dates until all components have been
// parsed, and then try again later
((TzId) parameter).setTimeZoneRegistry(tzRegistry);
}

propertyBuilder.parameter(parameter);
}

Expand Down
13 changes: 13 additions & 0 deletions src/main/java/net/fortuna/ical4j/model/PropertyBuilder.java
@@ -1,5 +1,6 @@
package net.fortuna.ical4j.model;

import net.fortuna.ical4j.model.property.DateProperty;
import net.fortuna.ical4j.model.property.XProperty;
import net.fortuna.ical4j.util.Strings;

Expand All @@ -19,6 +20,8 @@ public class PropertyBuilder extends AbstractContentBuilder {

private List<Parameter> parameters = new ArrayList<>();

private TimeZoneRegistry timeZoneRegistry;

public PropertyBuilder factories(List<PropertyFactory<?>> factories) {
this.factories.addAll(factories);
return this;
Expand All @@ -41,6 +44,11 @@ public PropertyBuilder parameter(Parameter parameter) {
return this;
}

public PropertyBuilder timeZoneRegistry(TimeZoneRegistry timeZoneRegistry) {
this.timeZoneRegistry = timeZoneRegistry;
return this;
}

public Property build() throws ParseException, IOException, URISyntaxException {
Property property = null;
for (PropertyFactory<?> factory : factories) {
Expand All @@ -49,6 +57,11 @@ public Property build() throws ParseException, IOException, URISyntaxException {
if (property instanceof Escapable) {
property.setValue(Strings.unescape(value));
}

if (property instanceof DateProperty) {
((DateProperty<?>) property).setTimeZoneRegistry(timeZoneRegistry);
property.setValue(value);
}
}
}

Expand Down
33 changes: 32 additions & 1 deletion src/main/java/net/fortuna/ical4j/model/TemporalAdapter.java
Expand Up @@ -42,15 +42,22 @@ public class TemporalAdapter<T extends Temporal> implements Serializable {

private final TzId tzId;

private transient TimeZoneRegistry timeZoneRegistry;

private transient T temporal;

public TemporalAdapter(TemporalAdapter<T> adapter) {
this.temporal = adapter.temporal;
this.valueString = adapter.valueString;
this.tzId = adapter.tzId;
this.timeZoneRegistry = adapter.timeZoneRegistry;
}

public TemporalAdapter(T temporal) {
this(temporal, null);
}

public TemporalAdapter(T temporal, TimeZoneRegistry timeZoneRegistry) {
Objects.requireNonNull(temporal, "temporal");
this.temporal = temporal;
this.valueString = toString(temporal, ZoneId.systemDefault());
Expand All @@ -59,6 +66,7 @@ public TemporalAdapter(T temporal) {
} else {
this.tzId = null;
}
this.timeZoneRegistry = timeZoneRegistry;
}

private TemporalAdapter(String valueString) {
Expand All @@ -73,16 +81,28 @@ private TemporalAdapter(String valueString) {
* @param tzId a zone id to apply to the parsed value
*/
private TemporalAdapter(String value, TzId tzId) {
this(value, tzId, null);
}

/**
*
* @param value a string representation of a floating date/time value
* @param tzId a zone id to apply to the parsed value
* @param timeZoneRegistry timezone definitions
*/
private TemporalAdapter(String value, TzId tzId, TimeZoneRegistry timeZoneRegistry) {
this.valueString = value;
this.tzId = tzId;
this.timeZoneRegistry = timeZoneRegistry;
}

public T getTemporal() {
if (temporal == null) {
synchronized (valueString) {
if (temporal == null) {
if (tzId != null) {
temporal = (T) CalendarDateFormat.FLOATING_DATE_TIME_FORMAT.parse(valueString, tzId.toZoneId());
temporal = (T) CalendarDateFormat.FLOATING_DATE_TIME_FORMAT.parse(valueString,
tzId.toZoneId(timeZoneRegistry));
} else {
temporal = (T) PARSE_FORMAT.parse(valueString);
}
Expand Down Expand Up @@ -188,6 +208,17 @@ public static TemporalAdapter<ZonedDateTime> parse(String value, TzId tzId) {
return new TemporalAdapter<>(value, tzId);
}

/**
*
* @param value a string representing a floating temporal value
* @param tzId a timezone applied to the parsed value
* @param timeZoneRegistry timezone definitions
* @return
*/
public static TemporalAdapter<ZonedDateTime> parse(String value, TzId tzId, TimeZoneRegistry timeZoneRegistry) {
return new TemporalAdapter<>(value, tzId, timeZoneRegistry);
}

/**
* This method provides support for conversion of legacy {@link Date} and {@link DateTime} instances to temporal
* values.
Expand Down
20 changes: 9 additions & 11 deletions src/main/java/net/fortuna/ical4j/model/parameter/TzId.java
Expand Up @@ -53,25 +53,14 @@ public class TzId extends Parameter implements Escapable {

private final String value;

private transient TimeZoneRegistry timeZoneRegistry;

/**
* @param aValue a string representation of a time zone identifier
*/
public TzId(final String aValue) {
this(aValue, null);
}

public TzId(final String aValue, TimeZoneRegistry timeZoneRegistry) {
super(TZID);
// parameter values may be quoted if they contain characters in the
// set [:;,]..
this.value = Strings.unquote(aValue);
this.timeZoneRegistry = timeZoneRegistry;
}

public void setTimeZoneRegistry(TimeZoneRegistry timeZoneRegistry) {
this.timeZoneRegistry = timeZoneRegistry;
}

/**
Expand All @@ -81,6 +70,15 @@ public void setTimeZoneRegistry(TimeZoneRegistry timeZoneRegistry) {
* @return a zone id represented by this instance
*/
public ZoneId toZoneId() {
return toZoneId(null);
}

/**
*
* @param timeZoneRegistry
* @return
*/
public ZoneId toZoneId(TimeZoneRegistry timeZoneRegistry) {
if (timeZoneRegistry != null && !timeZoneRegistry.getZoneRules().isEmpty()) {
return timeZoneRegistry.getZoneId(getValue());
} else {
Expand Down
21 changes: 12 additions & 9 deletions src/main/java/net/fortuna/ical4j/model/property/DateProperty.java
Expand Up @@ -31,10 +31,7 @@
*/
package net.fortuna.ical4j.model.property;

import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyFactory;
import net.fortuna.ical4j.model.TemporalAdapter;
import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.parameter.TzId;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.util.Strings;
Expand Down Expand Up @@ -77,6 +74,8 @@ public abstract class DateProperty<T extends Temporal> extends Property {

private TemporalAdapter<T> date;

private transient TimeZoneRegistry timeZoneRegistry;

/**
* @param name the property name
* @param parameters a list of initial parameters
Expand Down Expand Up @@ -104,7 +103,7 @@ public T getDate() {
if (date != null) {
Optional<TzId> tzId = getParameter(Parameter.TZID);
if (tzId.isPresent()) {
return (T) date.toLocalTime(tzId.get().toZoneId());
return (T) date.toLocalTime(tzId.get().toZoneId(timeZoneRegistry));
} else {
return date.getTemporal();
}
Expand All @@ -121,7 +120,7 @@ public T getDate() {
*/
public void setDate(T date) {
if (date != null) {
this.date = new TemporalAdapter<>(date);
this.date = new TemporalAdapter<>(date, timeZoneRegistry);
} else {
this.date = null;
}
Expand All @@ -142,7 +141,7 @@ public void setValue(final String value) throws DateTimeParseException {
// value can be either a date-time or a date..
if (value != null && !value.isEmpty()) {
Optional<TzId> tzId = getParameter(Parameter.TZID);
this.date = tzId.map(id -> (TemporalAdapter<T>) TemporalAdapter.parse(value, id))
this.date = tzId.map(id -> (TemporalAdapter<T>) TemporalAdapter.parse(value, id, timeZoneRegistry))
.orElseGet(() -> TemporalAdapter.parse(value));
} else {
this.date = null;
Expand All @@ -155,12 +154,16 @@ public void setValue(final String value) throws DateTimeParseException {
public String getValue() {
Optional<TzId> tzId = getParameter(Parameter.TZID);
if (tzId.isPresent()) {
return date.toString(tzId.get().toZoneId());
return date.toString(tzId.get().toZoneId(timeZoneRegistry));
} else {
return Strings.valueOf(date);
}
}

public void setTimeZoneRegistry(TimeZoneRegistry timeZoneRegistry) {
this.timeZoneRegistry = timeZoneRegistry;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -222,7 +225,7 @@ public void validate() throws ValidationException {

// ensure tzid matches date-time timezone..
final Optional<TzId> tzId = getParameter(Parameter.TZID);
if (!tzId.isPresent() || !tzId.get().toZoneId().equals(dateTime.getZone())) {
if (!tzId.isPresent() || !tzId.get().toZoneId(timeZoneRegistry).equals(dateTime.getZone())) {
throw new ValidationException("TZID parameter [" + tzId.get() + "] does not match the timezone ["
+ dateTime.getZone() + "]");
}
Expand Down

0 comments on commit e0dd7b3

Please sign in to comment.