Skip to content

Commit

Permalink
Date list improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
benfortuna committed Jun 17, 2022
1 parent 037ede0 commit 1e8b13e
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 3 deletions.
42 changes: 39 additions & 3 deletions src/main/java/net/fortuna/ical4j/model/DateList.java
Expand Up @@ -53,6 +53,9 @@ public class DateList<T extends Temporal> implements Serializable {

private static final long serialVersionUID = -3700862452550012357L;

@SuppressWarnings("rawtypes")
public static final DateList EMPTY_LIST = new DateList();

private final List<TemporalAdapter<T>> dates;

/**
Expand All @@ -65,12 +68,21 @@ public DateList() {
/**
* Constructs a new date list of the specified type containing
* the dates in the specified list.
* @param list a list of dates to include in the new list
* @param dates a list of dates to include in the new list
*/
public DateList(final List<TemporalAdapter<T>> list) {
this.dates = list;
public DateList(final T...dates) {
this.dates = Arrays.stream(dates).map(TemporalAdapter::new).collect(Collectors.toList());
}

public DateList(TimeZoneRegistry timeZoneRegistry, T...dates) {
this.dates = Arrays.stream(dates).map(date -> new TemporalAdapter<>(date, timeZoneRegistry))
.collect(Collectors.toList());
}

private DateList(List<TemporalAdapter<T>> dates) {
this.dates = dates;
}

/**
* Parse a string representation of a date/time list.
*
Expand All @@ -79,20 +91,44 @@ public DateList(final List<TemporalAdapter<T>> list) {
* @throws DateTimeParseException
*/
public static DateList<? extends Temporal> parse(String value) {
if (value == null || value.isEmpty()) {
return emptyList();
}

List<TemporalAdapter<Temporal>> dates = Arrays.stream(value.split(","))
.parallel().map(TemporalAdapter::parse)
.collect(Collectors.toList());

return new DateList<>(dates);
}

public static DateList<? extends Temporal> parse(String value, ZoneId zoneId) {
if (value == null || value.isEmpty()) {
return emptyList();
}

List<TemporalAdapter<ZonedDateTime>> dates = Arrays.stream(value.split(","))
.parallel().map(s -> TemporalAdapter.parse(s, zoneId))
.collect(Collectors.toList());
return new DateList<>(dates);
}

public static DateList<ZonedDateTime> parse(String value, TzId tzId, TimeZoneRegistry timeZoneRegistry) {
if (value == null || value.isEmpty()) {
return emptyList();
}

List<TemporalAdapter<ZonedDateTime>> dates = Arrays.stream(value.split(","))
.parallel().map(s -> TemporalAdapter.parse(s, tzId, timeZoneRegistry))
.collect(Collectors.toList());
return new DateList<>(dates);
}

@SuppressWarnings("unchecked")
public static <T extends Temporal> DateList<T> emptyList() {
return (DateList<T>) EMPTY_LIST;
}

@Override
public String toString() {
if (dates.isEmpty()) {
Expand Down
91 changes: 91 additions & 0 deletions src/test/groovy/net/fortuna/ical4j/model/DateListTestSpec.groovy
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2022, Ben Fortuna
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* o Neither the name of Ben Fortuna nor the names of any other contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

package net.fortuna.ical4j.model

import net.fortuna.ical4j.model.component.VTimeZone
import net.fortuna.ical4j.model.property.TzId
import spock.lang.Specification

import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.zone.ZoneRulesProvider

class DateListTestSpec extends Specification {

def 'test date list string parsing'() {
expect: 'parsed string produces expected date list'
DateList.parse(value) == expectedDateList

where:
value | expectedDateList
'' | DateList.emptyList()
'20220617' | new DateList<>(LocalDate.of(2022, 06, 17))
'20220617,20220618' | new DateList<>(LocalDate.of(2022, 06, 17), LocalDate.of(2022, 06, 18))
'20220617T140000' | new DateList<>(LocalDateTime.of(2022, 06, 17, 14, 0))
'20220617T140000Z' | new DateList<>(LocalDateTime.of(2022, 06, 17, 14, 0).toInstant(ZoneOffset.UTC))
}

def 'test date list string parsing in another timezone'() {
expect: 'parsed string produces expected date list'
DateList.parse(value, ZoneId.of(zoneId)) == expectedDateList

where:
value | zoneId | expectedDateList
'20220617T140000' | 'Europe/London' | new DateList<>(LocalDateTime.of(2022, 06, 17, 14, 0).atZone(ZoneId.of('Europe/London')))
}

def 'test date list string parsing with a custom timezone'() {
given: 'a custom timezone registry'
TimeZoneRegistry timeZoneRegistry = new TimeZoneRegistryImpl()

and: 'a custom timezone'
VTimeZone vTimeZone = timeZoneRegistry.getTimeZone('Europe/Amsterdam').getVTimeZone()
vTimeZone.replace(new TzId('Europe/Atlantis'))

TimeZone timeZone = new TimeZone(vTimeZone)
timeZoneRegistry.register(timeZone)

and: 'registered zone rules provider'
ZoneRulesProvider.registerProvider(new ZoneRulesProviderImpl(timeZoneRegistry));

and: 'corresponding tzid parameter'
net.fortuna.ical4j.model.parameter.TzId tzIdParam = new net.fortuna.ical4j.model.parameter.TzId('Europe/Atlantis')

expect: 'parsed string produces expected date list'
DateList dateList = DateList.parse('20220617T140000', tzIdParam, timeZoneRegistry)
dateList.dates == new DateList<>(LocalDateTime.of(2022, 06, 17, 14, 0).atZone(timeZoneRegistry.getZoneId('Europe/Atlantis'))).dates
}
}

0 comments on commit 1e8b13e

Please sign in to comment.