jfxtras.icalendarfx.utilities.DateTimeUtilities Maven / Gradle / Ivy
package jfxtras.icalendarfx.utilities;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoField.YEAR;
import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.SignStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalAmount;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import jfxtras.icalendarfx.components.VEvent;
import jfxtras.icalendarfx.parameters.VParameterElement;
import jfxtras.icalendarfx.properties.component.time.TimeTransparency.TimeTransparencyType;
import jfxtras.icalendarfx.utilities.DateTimeUtilities;
import jfxtras.icalendarfx.utilities.ICalendarUtilities;
import jfxtras.icalendarfx.utilities.Pair;
/**
* Temporal date and date-time types and supporting methods for iCalendar.
* DATE
* DATE_WITH_LOCAL_TIME
* DATE_WITH_LOCAL_TIME_AND_TIME_ZONE
* DATE_WITH_UTC_TIME:
* see iCalendar RFC 5545, page 32-33
*
* includes methods to format a Temporal representing a DateTimeType as a String
*
* @author David Bal
*
*/
public final class DateTimeUtilities
{
private DateTimeUtilities() { }
private static final ZoneId DEFAULT_ZONE = ZoneId.systemDefault(); // default ZoneId to convert to
/**
* Default DateTimeType to use when none is specified. For example, when a date-only component is converted to
* a date-time one.
*/
public static final DateTimeType DEFAULT_DATE_TIME_TYPE = DateTimeType.DATE_WITH_UTC_TIME;
public final static DateTimeFormatter LOCAL_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(MONTH_OF_YEAR, 2)
.appendValue(DAY_OF_MONTH, 2)
.toFormatter();
final static DateTimeFormatter LOCAL_TIME_FORMATTER = new DateTimeFormatterBuilder()
.appendValue(HOUR_OF_DAY, 2)
.appendValue(MINUTE_OF_HOUR, 2)
.appendValue(SECOND_OF_MINUTE, 2)
.toFormatter();
public final static DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(LOCAL_DATE_FORMATTER)
.appendLiteral('T')
.append(LOCAL_TIME_FORMATTER)
.toFormatter();
public final static DateTimeFormatter ZONED_DATE_TIME_UTC_FORMATTER = new DateTimeFormatterBuilder()
.append(LOCAL_DATE_TIME_FORMATTER)
.appendOffsetId()
.toFormatter();
// @Deprecated
public final static DateTimeFormatter ZONED_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
.optionalStart()
.appendLiteral('[')
.parseCaseInsensitive()
.appendZoneRegionId()
.appendLiteral(']')
.optionalEnd()
.append(LOCAL_DATE_TIME_FORMATTER)
.optionalStart()
.appendOffsetId()
.optionalEnd()
.toFormatter();
final static DateTimeFormatter ZONE_FORMATTER = new DateTimeFormatterBuilder()
.optionalStart()
.parseCaseInsensitive()
.appendLiteral("TZID=")
.appendZoneRegionId()
.optionalEnd()
.toFormatter();
/** Compares two temporals of the same type */
public final static Comparator TEMPORAL_COMPARATOR = (t1, t2) ->
{
if (t1.getClass().equals(t2.getClass()))
{
return getTemporalComparator(t1).compare(t1, t2);
} else
{
throw new DateTimeException("For comparision, Temporal classes must be equal (" + t1.getClass().getSimpleName() + ", " + t2.getClass().getSimpleName() + ")");
}
};
/** Compares two temporals of the LocalDate, LocalDateTime and ZonedDateTime
* Makes LocalDate at start of day. Add system default ZoneId to LocalDate and LocalDateTime */
public final static Comparator TEMPORAL_COMPARATOR2 = (t1, t2) ->
{
ZonedDateTime z1 = (ZonedDateTime) DateTimeType.DATE_WITH_UTC_TIME.from(t1);
ZonedDateTime z2 = (ZonedDateTime) DateTimeType.DATE_WITH_UTC_TIME.from(t2);
return z1.compareTo(z2);
};
/** Returns correct comparator based on Temporal parameter */
public final static Comparator getTemporalComparator(Temporal t)
{
if (t instanceof LocalDate)
{
return (t1, t2) -> ((LocalDate) t1).compareTo((LocalDate) t2);
} else if (t instanceof LocalDateTime)
{
return (t1, t2) -> ((LocalDateTime) t1).compareTo((LocalDateTime) t2);
} else if (t instanceof ZonedDateTime)
{
return (t1, t2) -> ((ZonedDateTime) t1).compareTo((ZonedDateTime) t2);
} else
{
throw new DateTimeException("Unsupported Temporal type:" + t.getClass().getSimpleName());
}
}
/** Determines if Temporal is before t2
* Works for LocalDate, LocalDateTime and ZonedDateTime
*
* @param t1 first Temporal
* @param t2 second Temporal (to compare with t1)
* @return true if t1 is before t2, false otherwise
*/
public static boolean isBefore(Temporal t1, Temporal t2)
{
if (t1.getClass().equals(t2.getClass()))
{
if (t1 instanceof LocalDate)
{
return ((LocalDate) t1).isBefore((LocalDate) t2);
} else if (t1 instanceof LocalDateTime)
{
return ((LocalDateTime) t1).isBefore((LocalDateTime) t2);
} else if (t1 instanceof ZonedDateTime)
{
return ((ZonedDateTime) t1).isBefore((ZonedDateTime) t2);
} else
{
throw new DateTimeException("Unsupported Temporal class: " + t1.getClass());
}
}
throw new DateTimeException("For comparision, Temporal classes must be equal (" + t1.getClass().getSimpleName() + ", " + t2.getClass().getSimpleName() + ")");
}
/** Determines if Temporal is after t2
* Works for LocalDate, LocalDateTime and ZonedDateTime
*
* @param t1 first Temporal
* @param t2 second Temporal (to compare with t1)
* @return true if t1 is after t2, false otherwise
*/
public static boolean isAfter(Temporal t1, Temporal t2)
{
if (t1.getClass().equals(t2.getClass()))
{
if (t1 instanceof LocalDate)
{
return ((LocalDate) t1).isAfter((LocalDate) t2);
} else if (t1 instanceof LocalDateTime)
{
return ((LocalDateTime) t1).isAfter((LocalDateTime) t2);
} else if (t1 instanceof ZonedDateTime)
{
return ((ZonedDateTime) t1).isAfter((ZonedDateTime) t2);
} else
{
throw new DateTimeException("Unsupported Temporal class: " + t1.getClass());
}
} throw new DateTimeException("For comparision, Temporal classes must be equal (" + t1.getClass().getSimpleName() + ", " + t2.getClass().getSimpleName() + ")");
}
private final static int CONFLICT_CHECK_QUANTITY = 400;
/** Check if schedule conflict exists for {@link TimeTransparencyType.OPAQUE OPAQUE} events
* using default check quantity
*
* @param vEvent event to test
* @param vEvents existing events
* @return UID and start of recurrence of conflicting event, null otherwise
*/
public static String checkScheduleConflict(VEvent vEvent, List vEvents)
{
return checkScheduleConflict(vEvent, vEvents, CONFLICT_CHECK_QUANTITY);
}
/** Check if schedule conflict exists for {@link TimeTransparencyType.OPAQUE OPAQUE} events.
*
* @param vEvent event to test
* @param vEvents existing events
* @param checkQuantity amount of recurrences to be tested
* @return UID and start of recurrence of conflicting event, null otherwise
*/
// TODO - Should this method compare FreeBusy times too?
public static String checkScheduleConflict(VEvent vEvent, List vEvents, int checkQuantity)
{
// must be opaque to cause conflict, opaque is default
TimeTransparencyType newTransparency = (vEvent.getTimeTransparency() == null) ? TimeTransparencyType.OPAQUE : vEvent.getTimeTransparency().getValue();
if (newTransparency == TimeTransparencyType.TRANSPARENT)
{
return null;
}
/*
* Make list of recurrence start date/times for VEvent to be tested
*/
LocalDate dtstart = LocalDate.from(vEvent.getDateTimeStart().getValue());
TemporalAmount duration = vEvent.getActualDuration();
List newStarts = vEvent.streamRecurrences().limit(checkQuantity).collect(Collectors.toList());
Temporal lastStart = newStarts.get(newStarts.size()-1);
/*
* Make sorted list of events before lastStart from all VEvents
*/
List eventTimes = vEvents.stream()
.filter(v ->
{ // only keep OPAQUE events
TimeTransparencyType myTransparency = (v.getTimeTransparency() == null) ? TimeTransparencyType.OPAQUE : v.getTimeTransparency().getValue();
return myTransparency == TimeTransparencyType.OPAQUE;
})
.flatMap(v ->
{
TemporalAmount actualDuration = v.getActualDuration();
Temporal myDTStart = v.getDateTimeStart().getValue().with(dtstart);
return v.streamRecurrences(myDTStart)
.limit(checkQuantity)
.filter(t -> ! DateTimeUtilities.isAfter(t, lastStart))
.map(t ->
{
String uid = (v.getUniqueIdentifier() != null) ? v.getUniqueIdentifier().getValue() : null;
return new Triple(t, t.plus(actualDuration), uid);
});
})
.sorted((t1, t2) -> DateTimeUtilities.TEMPORAL_COMPARATOR2.compare(t1.start, t2.start))
.collect(Collectors.toList());
/*
* Search for a conflict
* 1. New start before existing end, new end after existing start
*/
for (Temporal newStart : newStarts)
{
Temporal newEnd = newStart.plus(duration);
Triple firstConflict = eventTimes.stream()
.filter(triple ->
{
boolean newStartBeforeEnd = DateTimeUtilities.isBefore(newStart, triple.end);
boolean newEndAfterStart = DateTimeUtilities.isAfter(newEnd, triple.start);
return (newStartBeforeEnd && newEndAfterStart);
})
.findAny()
.orElseGet(() -> null);
if (firstConflict != null)
{
String uid = (firstConflict.uid != null) ? firstConflict.uid + ", " : "";
return (firstConflict == null) ? null : uid + DateTimeUtilities.temporalToString(firstConflict.start);
}
}
return null; // no conflicts found
}
// Triple class for checkScheduleConflict
private static class Triple
{
public Triple(Temporal start, Temporal end, String uid)
{
this.start = start;
this.end = end;
this.uid = uid;
}
Temporal start;
Temporal end;
String uid;
}
// /** Check if schedule conflict exists for {@link TimeTransparencyType.OPAQUE OPAQUE} events.
// * Finds conflict faster, but not necessarily the first conflict that occurs
// *
// * @param vEvent event to test
// * @param vEvents existing events
// * @param checkQuantity amount of recurrences to be tested
// * @return UID and start of recurrence of conflicting event, null otherwise
// */
// // TODO - DOESN'T WORK, FIX IF WANTED
// public static String checkScheduleConflict2(VEvent vEvent, List vEvents, int checkQuantity)
// {
// // must be opaque to cause conflict, opaque is default
// TimeTransparencyType newTransparency = (vEvent.getTimeTransparency() == null) ? TimeTransparencyType.OPAQUE : vEvent.getTimeTransparency().getValue();
// if (newTransparency == TimeTransparencyType.TRANSPARENT)
// {
// return null;
// }
//
// LocalDate dtstart = LocalDate.from(vEvent.getDateTimeStart().getValue());
// TemporalAmount duration = vEvent.getActualDuration();
// List newStarts = vEvent.streamRecurrences().limit(checkQuantity).collect(Collectors.toList());
// Temporal lastStart = newStarts.get(newStarts.size()-1);
//
// /*
// * Search for a conflict
// * Note: The first conflict found may not be the first occurring conflict.
// * Make list of Pairs containing start and end temporals
// */
// for (VEvent v : vEvents)
// {
// // can only conflict with opaque events, opaque is default
// TimeTransparencyType myTransparency = (v.getTimeTransparency() == null) ? TimeTransparencyType.OPAQUE : v.getTimeTransparency().getValue();
// if (myTransparency == TimeTransparencyType.OPAQUE)
// {
// List> eventTimes = new ArrayList<>();
// Temporal myDTStart = v.getDateTimeStart().getValue().with(dtstart);
// TemporalAmount actualDuration = v.getActualDuration();
// Iterator existingStartIterator = v.streamRecurrences(myDTStart).limit(checkQuantity).iterator();
// while (existingStartIterator.hasNext())
// {
// Temporal t = existingStartIterator.next();
// if (DateTimeUtilities.isAfter(t, lastStart)) break; // quit adding if after last new start temporal
// eventTimes.add(new Pair<>(t, t.plus(actualDuration)));
// }
//
// for (Temporal newStart : newStarts)
// {
// Temporal newEnd = newStart.plus(duration);
// Pair conflict = eventTimes.stream()
// .filter(p ->
// {
// Temporal existingStart = p.getKey();
// Temporal existingEnd = p.getValue();
// // test start
// boolean isOnOrAfter = ! DateTimeUtilities.isBefore(newStart, existingStart);
// if (isOnOrAfter)
// {
// return ! DateTimeUtilities.isAfter(newStart, existingEnd);
// }
// // test end
// boolean isAfter = DateTimeUtilities.isAfter(newEnd, existingStart);
// if (isAfter)
// {
// return DateTimeUtilities.isBefore(newEnd, existingEnd);
// }
// return false;
// })
// .findAny()
// .orElseGet(() -> null);
// if (conflict != null)
// {
// String uid = (v.getUniqueIdentifier() != null) ? v.getUniqueIdentifier().getValue() + ", " : "";
// return (conflict == null) ? null : uid + conflict.getKey();
// }
// }
// }
// }
// return null; // no conflicts found
// }
/**
* returns week of month.
* For example, a LocalDate representing March 10, 2016 returns 2, for the 2nd Thursday.
*
* @param dateBasedTemporal - date based Temporal, such as LocalDate
* @return - ordinal week in month, such as 2nd (as in 2nd Wednesday in month)
*/
public static int weekOrdinalInMonth(Temporal dateBasedTemporal)
{
Temporal start = dateBasedTemporal.with(TemporalAdjusters.firstDayOfMonth());
int ordinalWeekNumber = 0;
while (! DateTimeUtilities.isBefore(dateBasedTemporal, start))
{
ordinalWeekNumber++;
start = start.plus(1, ChronoUnit.WEEKS);
}
return ordinalWeekNumber;
}
/**
* Calculate TemporalAmount between two Temporals.
* Both temporals must be the same type and representations of a DateTimeType.
*
* @param startInclusive - the start temporal, not null
* @param endExclusive - the end temporal, not null
* @return - Period for DATE, Duration for all other DateTimeTypes
*/
public static TemporalAmount temporalAmountBetween(Temporal startInclusive, Temporal endExclusive)
{
final TemporalAmount duration;
if (startInclusive instanceof LocalDate || endExclusive instanceof LocalDate)
{
duration = Period.between(LocalDate.from(startInclusive), LocalDate.from(endExclusive));
} else
{
duration = Duration.between(startInclusive, endExclusive);
}
return duration;
}
/**
* produced ISO.8601 date and date-time string for given Temporal of type
* LocalDate, LocalDateTime or ZonedDateTime
*
* @param temporal
* @return
*/
public static String temporalToString(Temporal temporal)
{
if (temporal instanceof ZonedDateTime)
{
ZonedDateTime value = (ZonedDateTime) temporal;
ZoneId z = value.getZone();
if (z.normalized().equals(ZoneOffset.UTC))
{
return DateTimeUtilities.ZONED_DATE_TIME_UTC_FORMATTER.format(value);
} else
{
return DateTimeUtilities.LOCAL_DATE_TIME_FORMATTER.format(value); // Time zone is added through TimeZoneIdentifier parameter
}
} else if (temporal instanceof LocalDateTime)
{
LocalDateTime value = (LocalDateTime) temporal;
return DateTimeUtilities.LOCAL_DATE_TIME_FORMATTER.format(value);
} else if (temporal instanceof LocalDate)
{
return DateTimeUtilities.LOCAL_DATE_FORMATTER.format(temporal);
} else if (temporal != null)
{
throw new DateTimeException("Unsuported Date-Time class:" + temporal.getClass().getSimpleName());
}
return null;
}
/**
* Parse ISO.8601 string into LocalDate, LocalDateTime or ZonedDateTime Temporal object.
*/
public static Temporal temporalFromString(String string)
{
List> list = ICalendarUtilities.parseInlineElementsToListPair(string);
Map map = list.stream().collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
// Map map = ICalendarUtilities.propertyLineToParameterMap(string);
StringBuilder builder = new StringBuilder(50);
String value = map.get(ICalendarUtilities.PROPERTY_VALUE_KEY);
if (map.get(VParameterElement.TIME_ZONE_IDENTIFIER.toString()) != null)
{
if (value.charAt(value.length()-1) != 'Z')
{
builder.append("[");
builder.append(map.get(VParameterElement.TIME_ZONE_IDENTIFIER.toString()));
builder.append("]");
}
}
builder.append(value);
String string2 = builder.toString();
final String form0 = "^[0-9]{8}";
final String form1 = "^[0-9]{8}T([0-9]{6})";
final String form2 = "^[0-9]{8}T([0-9]{6})Z";
final String form3 = "^(\\[.*/.*\\])[0-9]{8}T([0-9]{6}Z?)";
if (string2.matches(form0))
{
return LocalDate.parse(string2, DateTimeUtilities.LOCAL_DATE_FORMATTER);
} else if (string2.matches(form1))
{
return LocalDateTime.parse(string2, DateTimeUtilities.LOCAL_DATE_TIME_FORMATTER);
} else if (string2.matches(form2))
{
return ZonedDateTime.parse(string2, DateTimeUtilities.ZONED_DATE_TIME_UTC_FORMATTER);
} else if (string2.matches(form3))
{
return ZonedDateTime.parse(string2, DateTimeUtilities.ZONED_DATE_TIME_FORMATTER);
} else
{
throw new DateTimeException("Can't parse date-time string:" + string);
}
}
/**
* Convert 2-character string to DayOfWeek following below convention:
* weekday = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA"
* Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, and SATURDAY days of the week.
*
* @param dayOfWeekAbbreviation - 2 character abbreviation representing day of the week
* @return - associated DayOfWeek
*/
public static DayOfWeek dayOfWeekFromAbbreviation(String dayOfWeekAbbreviation)
{
Optional optional = Arrays.stream(DayOfWeek.values())
.filter(d -> d.toString().substring(0, 2).equals(dayOfWeekAbbreviation.toUpperCase()))
.findAny();
return (optional.isPresent()) ? optional.get() : null;
}
/**
* Returns LocalDateTime from Temporal that is an instance of either LocalDate, LocalDateTime or ZonedDateTime
* If the parameter is type LocalDate the returned LocalDateTime is atStartofDay.
* If the parameter is type ZonedDateTime the zoneID is changed to ZoneId.systemDefault() before taking the
* LocalDateTime.
*/
@Deprecated
public static LocalDateTime toLocalDateTime(Temporal temporal)
{
return (LocalDateTime) DateTimeType.DATE_WITH_LOCAL_TIME.from(temporal);
}
public enum DateTimeType
{
// DATE ("^(VALUE=DATE:)?[0-9]{8}")
DATE ("^[0-9]{8}")
{
@Override
public Temporal from(Temporal temporal, ZoneId zone)
{
return this.from(temporal);
}
@Override
public Temporal from(Temporal temporal)
{
switch(DateTimeType.of(temporal))
{
case DATE:
return temporal; // do nothing
case DATE_WITH_LOCAL_TIME:
return LocalDate.from(temporal);
case DATE_WITH_LOCAL_TIME_AND_TIME_ZONE:
case DATE_WITH_UTC_TIME:
return ZonedDateTime.from(temporal).withZoneSameInstant(DEFAULT_ZONE).toLocalDate();
default:
throw new DateTimeException("Unsupported Temporal class:" + temporal.getClass().getSimpleName());
}
}
@Override
boolean is(Temporal temporal)
{
return temporal instanceof LocalDate;
}
@Override
public Temporal parse(String temporalString, ZoneId zone)
{
if (temporalString.matches(getPattern()))
{
return LocalDate.parse(temporalString, LOCAL_DATE_FORMATTER);
}
return null;
}
}
, DATE_WITH_LOCAL_TIME ("^[0-9]{8}T([0-9]{6})")
{
@Override
public Temporal from(Temporal temporal, ZoneId zone)
{
return this.from(temporal);
}
@Override
public Temporal from(Temporal temporal)
{
switch(DateTimeType.of(temporal))
{
case DATE:
return LocalDate.from(temporal).atStartOfDay();
case DATE_WITH_LOCAL_TIME:
return temporal; // do nothing
case DATE_WITH_LOCAL_TIME_AND_TIME_ZONE:
case DATE_WITH_UTC_TIME:
return ZonedDateTime.from(temporal).withZoneSameInstant(DEFAULT_ZONE).toLocalDateTime();
default:
throw new DateTimeException("Unsupported Temporal class:" + temporal.getClass().getSimpleName());
}
}
@Override
boolean is(Temporal temporal)
{
return temporal instanceof LocalDateTime;
}
@Override
public Temporal parse(String temporalString, ZoneId zone)
{
boolean isTzidEmpty = zone == null;
boolean isPatternMatch = temporalString.matches(getPattern());
if (isTzidEmpty && isPatternMatch)
{
return LocalDateTime.parse(temporalString, LOCAL_DATE_TIME_FORMATTER);
}
return null;
}
}
, DATE_WITH_UTC_TIME ("^[0-9]{8}T([0-9]{6})Z")
{
@Override
public Temporal from(Temporal temporal, ZoneId zone)
{
return this.from(temporal);
}
@Override
public Temporal from(Temporal temporal)
{
switch(DateTimeType.of(temporal))
{
case DATE:
return LocalDate.from(temporal).atStartOfDay().atZone(ZoneId.of("Z"));
case DATE_WITH_LOCAL_TIME:
return LocalDateTime.from(temporal).atZone(DEFAULT_ZONE).withZoneSameInstant(ZoneId.of("Z"));
case DATE_WITH_LOCAL_TIME_AND_TIME_ZONE:
return ZonedDateTime.from(temporal).withZoneSameInstant(ZoneId.of("Z"));
case DATE_WITH_UTC_TIME:
return temporal; // do nothing
default:
throw new DateTimeException("Unsupported Temporal class:" + temporal.getClass().getSimpleName());
}
}
@Override
boolean is(Temporal temporal)
{
if (temporal instanceof ZonedDateTime)
{
ZoneId z = ((ZonedDateTime) temporal).getZone();
return z == ZoneId.of("Z");
}
return false;
}
@Override
public Temporal parse(String temporalString, ZoneId zone)
{
boolean isPatternMatch = temporalString.matches(getPattern());
if (isPatternMatch)
{
return ZonedDateTime.parse(temporalString, ZONED_DATE_TIME_UTC_FORMATTER);
}
return null;
}
}
, DATE_WITH_LOCAL_TIME_AND_TIME_ZONE ("^[0-9]{8}T([0-9]{6})Z?")
{
@Override
public Temporal from(Temporal temporal, ZoneId zone)
{
switch(DateTimeType.of(temporal))
{
case DATE:
return LocalDate.from(temporal).atStartOfDay().atZone(zone);
case DATE_WITH_LOCAL_TIME:
return LocalDateTime.from(temporal).atZone(zone);
case DATE_WITH_LOCAL_TIME_AND_TIME_ZONE:
case DATE_WITH_UTC_TIME:
return ZonedDateTime.from(temporal).withZoneSameInstant(zone);
default:
throw new DateTimeException("Unsupported Temporal class:" + temporal.getClass().getSimpleName());
}
}
@Override
public Temporal from(Temporal temporal)
{
throw new DateTimeException("Can't make DATE_WITH_LOCAL_TIME_AND_TIME_ZONE without time zone. Use from(Temporal temporal, ZoneId zone) instead");
}
@Override
boolean is(Temporal temporal)
{
return temporal instanceof ZonedDateTime;
}
@Override
public Temporal parse(String temporalString, ZoneId zone)
{
boolean isTzidEmpty = zone == null;
boolean isPatternMatch = temporalString.matches(getPattern());
if (! isTzidEmpty && isPatternMatch)
{
LocalDateTime localDateTime = LocalDateTime.parse(temporalString, LOCAL_DATE_TIME_FORMATTER);
return localDateTime.atZone(zone);
}
return null;
}
};
private String pattern;
public String getPattern() { return pattern; }
DateTimeType(String pattern)
{
this.pattern = pattern;
}
/** Find DateTimeType of Temporal parameter temporal */
public static DateTimeType of(Temporal temporal)
{
return Arrays.stream(DateTimeType.values())
.filter(d -> d.is(temporal))
.findFirst()
.get();
}
/**
* returns true if temporal is of the DateTimeType, false otherwise
*/
abstract boolean is(Temporal temporal);
public abstract Temporal parse(String value, ZoneId zone);
/** Convert temporal to new DateTimeType - for DATE_WITH_LOCAL_TIME_AND_TIME_ZONE */
public abstract Temporal from(Temporal temporal, ZoneId zone);
/** Convert temporal to new DateTimeType - for all types, but DATE_WITH_LOCAL_TIME_AND_TIME_ZONE */
public abstract Temporal from(Temporal temporal);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy