From 84573fe972899ae762a7da262758c34ac7dc4f15 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Tue, 13 Sep 2016 18:39:37 +0200 Subject: [PATCH] SONAR-8028 DateUtils parse date and datetime --- .../java/org/sonar/api/server/ws/Request.java | 27 ++++--- .../java/org/sonar/api/utils/DateUtils.java | 57 ++++++++++++++ .../org/sonar/api/server/ws/RequestTest.java | 29 +++++-- .../org/sonar/api/utils/DateUtilsTest.java | 77 +++++++++++++++++-- 4 files changed, 166 insertions(+), 24 deletions(-) diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java index c0b7ec7d618..c296d2c48ec 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/Request.java @@ -32,6 +32,8 @@ import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.SonarException; import static com.google.common.base.Preconditions.checkArgument; +import static org.sonar.api.utils.DateUtils.parseDateQuietly; +import static org.sonar.api.utils.DateUtils.parseDateTimeQuietly; /** * @since 4.2 @@ -224,19 +226,20 @@ public abstract class Request { @CheckForNull public Date paramAsDateTime(String key) { - String s = param(key); - if (s != null) { - try { - return DateUtils.parseDateTime(s); - } catch (SonarException notDateTime) { - try { - return DateUtils.parseDate(s); - } catch (SonarException notDateEither) { - throw new IllegalArgumentException(String.format("'%s' cannot be parsed as either a date or date+time", s)); - } - } + String stringDate = param(key); + if (stringDate == null) { + return null; + } + + Date date = parseDateTimeQuietly(stringDate); + if (date != null) { + return date; } - return null; + + date = parseDateQuietly(stringDate); + checkArgument(date != null, "'%s' cannot be parsed as either a date or date+time", stringDate); + + return date; } @CheckForNull diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java index ab8c90fbb52..b3f1a36311d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java @@ -32,6 +32,8 @@ import java.util.Date; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkArgument; + /** * Parses and formats ISO 8601 dates. * This class is thread-safe. @@ -142,6 +144,61 @@ public final class DateUtils { return datetime; } + /** + * @throws IllegalArgumentException if stringDate is not a correctly formed date or datetime + * @return the datetime, {@code null} if stringDate is null + * @since 6.1 + */ + @CheckForNull + public static Date parseDateOrDateTime(@Nullable String stringDate) { + if (stringDate == null) { + return null; + } + + Date date = parseDateTimeQuietly(stringDate); + if (date != null) { + return date; + } + + date = parseDateQuietly(stringDate); + checkArgument(date != null, "'%s' cannot be parsed as either a date or date+time", stringDate); + + return date; + } + + /** + * @see #parseDateOrDateTime(String) + */ + @CheckForNull + public static Date parseStartingDateOrDateTime(@Nullable String stringDate) { + return parseDateOrDateTime(stringDate); + } + + /** + * Return the datetime if @param stringDate is a datetime, date + 1 day if stringDate is a date. + * So '2016-09-01' would return a date equivalent to '2016-09-02T00:00:00+0000' in GMT + * @see #parseDateOrDateTime(String) + * @throws IllegalArgumentException if stringDate is not a correctly formed date or datetime + * @return the datetime, {@code null} if stringDate is null + * @since 6.1 + */ + @CheckForNull + public static Date parseEndingDateOrDateTime(@Nullable String stringDate) { + if (stringDate == null) { + return null; + } + + Date date = parseDateTimeQuietly(stringDate); + if (date != null) { + return date; + } + + date = parseDateQuietly(stringDate); + checkArgument(date != null, "'%s' cannot be parsed as either a date or date+time", stringDate); + + return addDays(date, 1); + } + /** * Adds a number of days to a date returning a new object. * The original date object is unchanged. diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java index fa3605d0935..4c4fbf5f028 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/RequestTest.java @@ -21,7 +21,11 @@ package org.sonar.api.server.ws; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.io.InputStream; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +35,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.sonar.api.rule.RuleStatus; import org.sonar.api.server.ws.internal.PartImpl; import org.sonar.api.server.ws.internal.ValidatingRequest; @@ -39,7 +44,10 @@ import org.sonar.api.utils.DateUtils; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.sonar.api.utils.DateUtils.parseDate; +import static org.sonar.api.utils.DateUtils.parseDateTime; +@RunWith(DataProviderRunner.class) public class RequestTest { @Rule @@ -214,6 +222,21 @@ public class RequestTest { assertThat(underTest.setParam("a_date", "2014-05-27").paramAsDate("a_date")).isEqualTo(DateUtils.parseDate("2014-05-27")); } + @DataProvider + public static Object[][] date_times() { + return new Object[][] { + {"2014-05-27", parseDate("2014-05-27")}, + {"2014-05-27T15:50:45+0100", parseDateTime("2014-05-27T15:50:45+0100")}, + {null, null} + }; + } + + @Test + @UseDataProvider("date_times") + public void param_as__date_time(String stringDate, Date expectedDate) { + assertThat(underTest.setParam("a_date", stringDate).paramAsDateTime("a_date")).isEqualTo(expectedDate); + } + @Test public void fail_when_param_as_date_not_a_date() { expectedException.expect(IllegalArgumentException.class); @@ -222,12 +245,6 @@ public class RequestTest { underTest.setParam("a_date", "polop").paramAsDate("a_date"); } - @Test - public void param_as_datetime() { - assertThat(underTest.setParam("a_datetime", "2014-05-27T15:50:45+0100").paramAsDateTime("a_datetime")).isEqualTo(DateUtils.parseDateTime("2014-05-27T15:50:45+0100")); - assertThat(underTest.setParam("a_datetime", "2014-05-27").paramAsDateTime("a_datetime")).isEqualTo(DateUtils.parseDate("2014-05-27")); - } - @Test public void fail_when_param_as_datetime_not_a_datetime() { expectedException.expect(IllegalArgumentException.class); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java index 34b321c83b8..94892baf665 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java @@ -19,20 +19,30 @@ */ package org.sonar.api.utils; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.fail; +import static org.sonar.api.utils.DateUtils.parseDate; +import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; +import static org.sonar.api.utils.DateUtils.parseDateTime; +import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime; +import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime; +@RunWith(DataProviderRunner.class) public class DateUtilsTest { @Rule - public ExpectedException thrown = ExpectedException.none(); + public ExpectedException expectedException = ExpectedException.none(); @Test public void parseDate_valid_format() { @@ -42,13 +52,13 @@ public class DateUtilsTest { @Test public void parseDate_not_valid_format() { - thrown.expect(SonarException.class); + expectedException.expect(SonarException.class); DateUtils.parseDate("2010/05/18"); } @Test public void parseDate_not_lenient() { - thrown.expect(SonarException.class); + expectedException.expect(SonarException.class); DateUtils.parseDate("2010-13-18"); } @@ -61,7 +71,7 @@ public class DateUtilsTest { @Test public void parseDate_fail_if_additional_characters() { - thrown.expect(SonarException.class); + expectedException.expect(SonarException.class); DateUtils.parseDate("1986-12-04foo"); } @@ -73,13 +83,13 @@ public class DateUtilsTest { @Test public void parseDateTime_not_valid_format() { - thrown.expect(SonarException.class); + expectedException.expect(SonarException.class); DateUtils.parseDate("2010/05/18 10:55"); } @Test public void parseDateTime_fail_if_additional_characters() { - thrown.expect(SonarException.class); + expectedException.expect(SonarException.class); DateUtils.parseDateTime("1986-12-04T01:02:03+0300foo"); } @@ -129,6 +139,61 @@ public class DateUtilsTest { assertThat(DateUtils.dateToLong(null)).isEqualTo(null); } + @DataProvider + public static Object[][] date_times() { + return new Object[][] { + {"2014-05-27", parseDate("2014-05-27")}, + {"2014-05-27T15:50:45+0100", parseDateTime("2014-05-27T15:50:45+0100")}, + {null, null} + }; + } + + @Test + @UseDataProvider("date_times") + public void param_as__date_time(String stringDate, Date expectedDate) { + assertThat(parseDateOrDateTime(stringDate)).isEqualTo(expectedDate); + assertThat(parseStartingDateOrDateTime(stringDate)).isEqualTo(expectedDate); + } + + @DataProvider + public static Object[][] ending_date_times() { + return new Object[][] { + {"2014-05-27", parseDate("2014-05-28")}, + {"2014-05-27T15:50:45+0100", parseDateTime("2014-05-27T15:50:45+0100")}, + {null, null} + }; + } + + @Test + @UseDataProvider("ending_date_times") + public void param_as_ending_date_time(String stringDate, Date expectedDate) { + assertThat(parseEndingDateOrDateTime(stringDate)).isEqualTo(expectedDate); + } + + @Test + public void fail_when_param_as_date_or_datetime_not_a_datetime() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("'polop' cannot be parsed as either a date or date+time"); + + parseDateOrDateTime("polop"); + } + + @Test + public void fail_when_param_as_starting_datetime_not_a_datetime() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("'polop' cannot be parsed as either a date or date+time"); + + parseStartingDateOrDateTime("polop"); + } + + @Test + public void fail_when_param_as_ending_datetime_not_a_datetime() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("'polop' cannot be parsed as either a date or date+time"); + + parseEndingDateOrDateTime("polop"); + } + /** * Cordially copied from XStream unit test * See http://koders.com/java/fid8A231D75F2C6E6909FB26BCA11C12D08AD05FB50.aspx?s=ThreadSafeDateFormatTest -- 2.39.5