diff --git a/pom.xml b/pom.xml index 0c7484c..2536d19 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.ethlo.time itu - 1.6.2-SNAPSHOT + 1.7.0 Internet Time Utility Extremely fast date/time parser and formatter - RFC 3339 (ISO 8601 profile) and W3C format diff --git a/src/main/java/com/ethlo/time/DateTime.java b/src/main/java/com/ethlo/time/DateTime.java index 89555c3..f665644 100644 --- a/src/main/java/com/ethlo/time/DateTime.java +++ b/src/main/java/com/ethlo/time/DateTime.java @@ -50,8 +50,9 @@ public class DateTime private final int second; private final int nano; private final TimezoneOffset offset; + private final int fractionDigits; - public DateTime(final Field field, final int year, final int month, final int day, final int hour, final int minute, final int second, final int nano, final TimezoneOffset offset) + public DateTime(final Field field, final int year, final int month, final int day, final int hour, final int minute, final int second, final int nano, final TimezoneOffset offset, final int fractionDigits) { this.field = field; this.year = year; @@ -62,6 +63,7 @@ public DateTime(final Field field, final int year, final int month, final int da this.second = assertSize(second, 0, 60, Field.SECOND); this.nano = assertSize(nano, 0, 999_999_999, Field.NANO); this.offset = offset; + this.fractionDigits = fractionDigits; } /** @@ -69,15 +71,15 @@ public DateTime(final Field field, final int year, final int month, final int da */ public static DateTime of(int year, int month, int day, int hour, int minute, int second, TimezoneOffset offset) { - return new DateTime(Field.SECOND, year, month, day, hour, minute, second, 0, offset); + return new DateTime(Field.SECOND, year, month, day, hour, minute, second, 0, offset, 0); } /** * Create a new instance with nanosecond granularity from the input parameters */ - public static DateTime of(int year, int month, int day, int hour, int minute, int second, int nanos, TimezoneOffset offset) + public static DateTime of(int year, int month, int day, int hour, int minute, int second, int nanos, TimezoneOffset offset, final int fractionDigits) { - return new DateTime(Field.NANO, year, month, day, hour, minute, second, nanos, offset); + return new DateTime(Field.NANO, year, month, day, hour, minute, second, nanos, offset, fractionDigits); } /** @@ -85,7 +87,7 @@ public static DateTime of(int year, int month, int day, int hour, int minute, in */ public static DateTime ofYear(int year) { - return new DateTime(Field.YEAR, year, 0, 0, 0, 0, 0, 0, null); + return new DateTime(Field.YEAR, year, 0, 0, 0, 0, 0, 0, null, 0); } /** @@ -93,7 +95,7 @@ public static DateTime ofYear(int year) */ public static DateTime ofYearMonth(int years, int months) { - return new DateTime(Field.MONTH, years, months, 0, 0, 0, 0, 0, null); + return new DateTime(Field.MONTH, years, months, 0, 0, 0, 0, 0, null, 0); } /** @@ -101,7 +103,7 @@ public static DateTime ofYearMonth(int years, int months) */ public static DateTime ofDate(int years, int months, int days) { - return new DateTime(Field.DAY, years, months, days, 0, 0, 0, 0, null); + return new DateTime(Field.DAY, years, months, days, 0, 0, 0, 0, null, 0); } /** @@ -109,7 +111,7 @@ public static DateTime ofDate(int years, int months, int days) */ public static DateTime of(int years, int months, int days, int hours, int minute, TimezoneOffset offset) { - return new DateTime(Field.MINUTE, years, months, days, hours, minute, 0, 0, offset); + return new DateTime(Field.MINUTE, years, months, days, hours, minute, 0, 0, offset, 0); } /** @@ -120,7 +122,7 @@ public static DateTime of(int years, int months, int days, int hours, int minute */ public static DateTime of(OffsetDateTime dateTime) { - return DateTime.of(dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano(), TimezoneOffset.of(dateTime.getOffset())); + return DateTime.of(dateTime.getYear(), dateTime.getMonthValue(), dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano(), TimezoneOffset.of(dateTime.getOffset()), 9); } private int assertSize(int value, int min, int max, Field field) @@ -367,14 +369,24 @@ private String toString(final DateTime date, final Field lastIncluded, final int } /** - * Formats this date-time as a date/date-time with the same fields as was parsed and no fractions in the second. + * Return the number of significant fraction digits in the second. + * + * @return The number of significant fraction digits + */ + public int getFractionDigits() + { + return fractionDigits; + } + + /** + * Formats this date-time as a date/date-time with the same fields as was parsed * * @return The formatted date/date-time string */ @Override public String toString() { - return toString(field); + return fractionDigits > 0 ? toString(fractionDigits) : toString(field); } /** @@ -399,6 +411,7 @@ public boolean equals(final Object o) && minute == dateTime.minute && second == dateTime.second && nano == dateTime.nano + && fractionDigits == dateTime.fractionDigits && field == dateTime.field && Objects.equals(offset, dateTime.offset); } @@ -409,6 +422,6 @@ public boolean equals(final Object o) @Override public int hashCode() { - return Objects.hash(field.ordinal(), year, month, day, hour, minute, second, nano, offset); + return Objects.hash(field.ordinal(), year, month, day, hour, minute, second, nano, offset, fractionDigits); } } diff --git a/src/main/java/com/ethlo/time/ITU.java b/src/main/java/com/ethlo/time/ITU.java index c9157ab..32d661e 100644 --- a/src/main/java/com/ethlo/time/ITU.java +++ b/src/main/java/com/ethlo/time/ITU.java @@ -96,8 +96,9 @@ public static String formatUtc(OffsetDateTime offsetDateTime, int fractionDigits /** * Format the input as an ISO format string, limited to the granularity of the specified field, in the UTC timezone. + * * @param offsetDateTime The date-time to format - * @param lastIncluded The last included field + * @param lastIncluded The last included field * @return The formatted string */ public static String formatUtc(OffsetDateTime offsetDateTime, Field lastIncluded) @@ -174,7 +175,8 @@ public static String formatUtcNano(final OffsetDateTime offsetDateTime) /** * Parse the input, and use callbacks for the type of date/date-time it contains. This allows you to handle different granularity inputs with ease! - * @param text The text to parse as a date/date-time + * + * @param text The text to parse as a date/date-time * @param temporalConsumer The consumer of the found date/date-time */ public static void parse(final String text, final TemporalConsumer temporalConsumer) @@ -207,7 +209,8 @@ else if (dateTime.includesGranularity(Field.MONTH)) /** * Parse the input, and use callbacks for the type of date/date-time it contains. This allows you to handle different granularity inputs with ease! - * @param text The text to parse as a date/date-time + * + * @param text The text to parse as a date/date-time * @param temporalHandler The handler of the found date/date-time */ @@ -241,7 +244,8 @@ else if (dateTime.includesGranularity(Field.MONTH)) /** * Check if the input is valid for one of the specified types - * @param text The input to check + * + * @param text The input to check * @param types The types that are considered valid * @return True if valid, otherwise false */ diff --git a/src/main/java/com/ethlo/time/TemporalHandler.java b/src/main/java/com/ethlo/time/TemporalHandler.java index c26fe11..fa41248 100644 --- a/src/main/java/com/ethlo/time/TemporalHandler.java +++ b/src/main/java/com/ethlo/time/TemporalHandler.java @@ -29,6 +29,7 @@ /** * Handler for flexibly dealing with different granularity date/date-times + * * @param The return type of the functions */ public interface TemporalHandler diff --git a/src/main/java/com/ethlo/time/TimezoneOffset.java b/src/main/java/com/ethlo/time/TimezoneOffset.java index 679a042..44fa2d8 100644 --- a/src/main/java/com/ethlo/time/TimezoneOffset.java +++ b/src/main/java/com/ethlo/time/TimezoneOffset.java @@ -86,6 +86,9 @@ public ZoneOffset toZoneOffset() return ZoneOffset.ofHoursMinutes(hours, minutes); } + /** + * @hidden + */ @Override public boolean equals(final Object o) { @@ -101,6 +104,9 @@ public boolean equals(final Object o) return hours == that.hours && minutes == that.minutes; } + /** + * @hidden + */ @Override public int hashCode() { diff --git a/src/main/java/com/ethlo/time/internal/EthloITU.java b/src/main/java/com/ethlo/time/internal/EthloITU.java index eb5785a..e2f4006 100644 --- a/src/main/java/com/ethlo/time/internal/EthloITU.java +++ b/src/main/java/com/ethlo/time/internal/EthloITU.java @@ -39,13 +39,12 @@ public class EthloITU extends AbstractRfc3339 implements W3cDateTimeUtil { - private static final EthloITU instance = new EthloITU(); - - private static final char PLUS = '+'; - private static final char MINUS = '-'; public static final char DATE_SEPARATOR = '-'; public static final char TIME_SEPARATOR = ':'; public static final char SEPARATOR_UPPER = 'T'; + private static final EthloITU instance = new EthloITU(); + private static final char PLUS = '+'; + private static final char MINUS = '-'; private static final char SEPARATOR_LOWER = 't'; private static final char SEPARATOR_SPACE = ' '; private static final char FRACTION_SEPARATOR = '.'; @@ -64,6 +63,33 @@ public static EthloITU getInstance() return instance; } + public static String finish(char[] buf, int length, final TimezoneOffset tz) + { + int tzLen = 0; + if (tz != null) + { + tzLen = writeTz(buf, length, tz); + } + return new String(buf, 0, length + tzLen); + } + + private static int writeTz(char[] buf, int start, TimezoneOffset tz) + { + if (tz.equals(TimezoneOffset.UTC)) + { + buf[start] = ZULU_UPPER; + return 1; + } + else + { + buf[start] = tz.getTotalSeconds() < 0 ? MINUS : PLUS; + LimitedCharArrayIntegerUtil.toString(Math.abs(tz.getHours()), buf, start + 1, 2); + buf[start + 3] = TIME_SEPARATOR; + LimitedCharArrayIntegerUtil.toString(Math.abs(tz.getMinutes()), buf, start + 4, 2); + return 6; + } + } + private int getHour(final char[] chars) { return parsePositiveInt(chars, 11, 13); @@ -260,33 +286,6 @@ private boolean handleDatePart(final Field lastIncluded, final char[] buffer, fi return lastIncluded == field; } - public static String finish(char[] buf, int length, final TimezoneOffset tz) - { - int tzLen = 0; - if (tz != null) - { - tzLen = writeTz(buf, length, tz); - } - return new String(buf, 0, length + tzLen); - } - - private static int writeTz(char[] buf, int start, TimezoneOffset tz) - { - if (tz.equals(TimezoneOffset.UTC)) - { - buf[start] = ZULU_UPPER; - return 1; - } - else - { - buf[start] = tz.getTotalSeconds() < 0 ? MINUS : PLUS; - LimitedCharArrayIntegerUtil.toString(Math.abs(tz.getHours()), buf, start + 1, 2); - buf[start + 3] = TIME_SEPARATOR; - LimitedCharArrayIntegerUtil.toString(Math.abs(tz.getMinutes()), buf, start + 4, 2); - return 6; - } - } - private void addFractions(char[] buf, int fractionDigits, int nano) { final double d = widths[fractionDigits - 1]; @@ -402,12 +401,12 @@ private DateTime handleSeconds(int year, int month, int day, int hour, int minut if (remaining == 0) { final int seconds = getSeconds(chars); - return leapSecondCheck(year, month, day, hour, minute, seconds, 0, null, false); + return leapSecondCheck(year, month, day, hour, minute, seconds, 0, null, 0); } TimezoneOffset offset = null; int fractions = 0; - boolean hasFractions = false; + int fractionDigits = 0; final char c = chars[19]; if (remaining == 1 && (c == ZULU_UPPER || c == ZULU_LOWER)) { @@ -424,7 +423,7 @@ else if (remaining >= 1 && c == FRACTION_SEPARATOR) // We have an end of fractions final int len = idx - 20; fractions = getFractions(chars, idx, len); - hasFractions = true; + fractionDigits = len; offset = parseTimezone(chars, idx); } else @@ -442,10 +441,10 @@ else if (remaining >= 1 && (c == PLUS || c == MINUS)) raiseDateTimeException(chars, "Unexpected character at position 19"); } - return leapSecondCheck(year, month, day, hour, minute, getSeconds(chars), fractions, offset, hasFractions); + return leapSecondCheck(year, month, day, hour, minute, getSeconds(chars), fractions, offset, fractionDigits); } - private DateTime leapSecondCheck(int year, int month, int day, int hour, int minute, int second, int nanos, TimezoneOffset offset, final boolean hasFractions) + private DateTime leapSecondCheck(int year, int month, int day, int hour, int minute, int second, int nanos, TimezoneOffset offset, final int fractionDigits) { if (second == LEAP_SECOND_SECONDS) { @@ -466,7 +465,7 @@ private DateTime leapSecondCheck(int year, int month, int day, int hour, int min } } } - return hasFractions ? DateTime.of(year, month, day, hour, minute, second, nanos, offset) : DateTime.of(year, month, day, hour, minute, second, offset); + return fractionDigits > 0 ? DateTime.of(year, month, day, hour, minute, second, nanos, offset, fractionDigits) : DateTime.of(year, month, day, hour, minute, second, offset); } private void raiseDateTimeException(char[] chars, String message) diff --git a/src/test/java/com/ethlo/time/AbstractTest.java b/src/test/java/com/ethlo/time/AbstractTest.java index 0bb8f84..c4e13f9 100644 --- a/src/test/java/com/ethlo/time/AbstractTest.java +++ b/src/test/java/com/ethlo/time/AbstractTest.java @@ -20,9 +20,10 @@ * #L% */ +import org.junit.jupiter.api.BeforeEach; + import com.ethlo.time.internal.Rfc3339Formatter; import com.ethlo.time.internal.Rfc3339Parser; -import org.junit.jupiter.api.BeforeEach; public abstract class AbstractTest { diff --git a/src/test/java/com/ethlo/time/CharArrayUtilTest.java b/src/test/java/com/ethlo/time/CharArrayUtilTest.java index 228fd75..ec2505d 100644 --- a/src/test/java/com/ethlo/time/CharArrayUtilTest.java +++ b/src/test/java/com/ethlo/time/CharArrayUtilTest.java @@ -24,9 +24,10 @@ import java.util.Arrays; -import com.ethlo.time.internal.LimitedCharArrayIntegerUtil; import org.junit.jupiter.api.Test; +import com.ethlo.time.internal.LimitedCharArrayIntegerUtil; + public class CharArrayUtilTest { @Test diff --git a/src/test/java/com/ethlo/time/DateTimeTest.java b/src/test/java/com/ethlo/time/DateTimeTest.java index e6771d9..ea71b33 100644 --- a/src/test/java/com/ethlo/time/DateTimeTest.java +++ b/src/test/java/com/ethlo/time/DateTimeTest.java @@ -20,18 +20,11 @@ * #L% */ -import org.junit.jupiter.api.Assertions; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import java.time.LocalDate; -import java.time.OffsetDateTime; -import java.time.Year; -import java.time.YearMonth; -import java.time.temporal.Temporal; - -import static org.assertj.core.api.Assertions.assertThat; - @Tag("CorrectnessTest") public class DateTimeTest { diff --git a/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java b/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java index f9cc36e..92da3d3 100644 --- a/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java @@ -23,11 +23,12 @@ import java.time.OffsetDateTime; import java.util.concurrent.TimeUnit; -import com.ethlo.time.internal.Rfc3339Formatter; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.infra.Blackhole; +import com.ethlo.time.internal.Rfc3339Formatter; + @OutputTimeUnit(TimeUnit.NANOSECONDS) public abstract class FormatterBenchmarkTest { diff --git a/src/test/java/com/ethlo/time/ITUTest.java b/src/test/java/com/ethlo/time/ITUTest.java index 1295e4b..4ec2628 100644 --- a/src/test/java/com/ethlo/time/ITUTest.java +++ b/src/test/java/com/ethlo/time/ITUTest.java @@ -79,7 +79,7 @@ void formatDateTimeWithLimitedGranularity() @Test void formatDateTimeWithFullGranularity() { - assertThat(DateTime.of(2012, 11, 31, 22, 50, 46, 123456789, TimezoneOffset.UTC).toString(9)).isEqualTo("2012-11-31T22:50:46.123456789Z"); + assertThat(DateTime.of(2012, 11, 31, 22, 50, 46, 1234567, TimezoneOffset.UTC, 7).toString()).isEqualTo("2012-11-31T22:50:46.1234567Z"); } @Test diff --git a/src/test/java/com/ethlo/time/ParserBenchmarkTest.java b/src/test/java/com/ethlo/time/ParserBenchmarkTest.java index 09ab2b7..afeb1c1 100644 --- a/src/test/java/com/ethlo/time/ParserBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/ParserBenchmarkTest.java @@ -22,11 +22,12 @@ import java.util.concurrent.TimeUnit; -import com.ethlo.time.internal.Rfc3339Parser; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.infra.Blackhole; +import com.ethlo.time.internal.Rfc3339Parser; + @OutputTimeUnit(TimeUnit.NANOSECONDS) public abstract class ParserBenchmarkTest { diff --git a/src/test/java/com/ethlo/time/TemporalHandlerTest.java b/src/test/java/com/ethlo/time/TemporalHandlerTest.java index ff72751..75e700f 100644 --- a/src/test/java/com/ethlo/time/TemporalHandlerTest.java +++ b/src/test/java/com/ethlo/time/TemporalHandlerTest.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/src/test/java/com/ethlo/time/TimezoneOffsetTest.java b/src/test/java/com/ethlo/time/TimezoneOffsetTest.java index 31d3977..8ebf42c 100644 --- a/src/test/java/com/ethlo/time/TimezoneOffsetTest.java +++ b/src/test/java/com/ethlo/time/TimezoneOffsetTest.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,11 +20,11 @@ * #L% */ -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.time.ZoneOffset; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; class TimezoneOffsetTest { diff --git a/src/test/java/com/ethlo/time/itu/ITURfc3339FormatterBenchmarkTest.java b/src/test/java/com/ethlo/time/itu/ITURfc3339FormatterBenchmarkTest.java index 619ef83..a87abd4 100644 --- a/src/test/java/com/ethlo/time/itu/ITURfc3339FormatterBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/itu/ITURfc3339FormatterBenchmarkTest.java @@ -20,8 +20,8 @@ * #L% */ -import com.ethlo.time.internal.EthloITU; import com.ethlo.time.FormatterBenchmarkTest; +import com.ethlo.time.internal.EthloITU; public class ITURfc3339FormatterBenchmarkTest extends FormatterBenchmarkTest { diff --git a/src/test/java/com/ethlo/time/itu/ITURfc3339ParserBenchmarkTest.java b/src/test/java/com/ethlo/time/itu/ITURfc3339ParserBenchmarkTest.java index 0436f76..4119c67 100644 --- a/src/test/java/com/ethlo/time/itu/ITURfc3339ParserBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/itu/ITURfc3339ParserBenchmarkTest.java @@ -20,8 +20,8 @@ * #L% */ -import com.ethlo.time.internal.EthloITU; import com.ethlo.time.ParserBenchmarkTest; +import com.ethlo.time.internal.EthloITU; public class ITURfc3339ParserBenchmarkTest extends ParserBenchmarkTest {