From 55d9c380eb54b3fc0ab43f7dfebca6415b262e13 Mon Sep 17 00:00:00 2001 From: Morten Haraldsen Date: Wed, 27 Dec 2023 09:44:25 +0100 Subject: [PATCH] Xmas 2023 (#19) * Micro-optimizations * Update readme --- README.md | 32 ++- doc/format.png | Bin 0 -> 24905 bytes doc/parse.png | Bin 0 -> 25600 bytes doc/parse_raw.png | Bin 0 -> 26212 bytes doc/performance.png | Bin 37381 -> 0 bytes plot.py | 4 +- pom.xml | 24 +- render.sh | 4 + src/main/java/com/ethlo/time/DateTime.java | 5 + .../com/ethlo/time/internal/EthloITU.java | 241 +++++++++--------- .../internal/LimitedCharArrayIntegerUtil.java | 43 +--- .../java/com/ethlo/time/CorrectnessTest.java | 7 + .../ethlo/time/FormatterBenchmarkTest.java | 2 +- .../com/ethlo/time/ParserBenchmarkTest.java | 2 +- .../ethlo/time/itu/EthloItuProfilerTest.java | 2 - .../itu/ITULenientParserBenchmarkTest.java | 19 +- .../java/com/ethlo/time/jdk/JdkRfc3339.java | 4 +- theme.mplstyle | 34 +++ 18 files changed, 229 insertions(+), 194 deletions(-) create mode 100644 doc/format.png create mode 100644 doc/parse.png create mode 100644 doc/parse_raw.png delete mode 100644 doc/performance.png create mode 100644 render.sh create mode 100644 theme.mplstyle diff --git a/README.md b/README.md index c7fd6d6..e58d2cd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ An extremely fast parser and formatter of ISO format date-times. > Date and time formats cause a lot of confusion and interoperability problems on the Internet. This document addresses many of the problems encountered and makes recommendations to improve consistency and interoperability when representing and using date and time in Internet protocols. -This project's goal it to do one thing and to do it right; make it easy to +This project's goal is to do one thing and to do it right; make it easy to handle [Date and Time on the Internet: Timestamps](https://www.ietf.org/rfc/rfc3339.txt) and W3C [Date and Time Formats](https://www.w3.org/TR/NOTE-datetime) in Java. @@ -21,9 +21,24 @@ W3C [Date and Time Formats](https://www.w3.org/TR/NOTE-datetime) in Java. ## Performance -Your mileage may vary, but tests indicate comfortably 10x faster than JDK classes. -Performance plot +Your mileage may vary. I've done my best to make sure these tests are as accurate as possible, but please do your own evaluation. +* The second resolution test-string is: `2017-12-21T12:20:45Z` +* The nanosecond-resolution test-string is: `2017-12-21T12:20:45.987654321Z` +### Parsing +Performance of parsing + +### Formatting +Performance of formatting + +### Raw parsing +If you do not need to have the full verification of `java.time.OffsetDateTime`, +you can use the raw, parsed data through `com.ethlo.time.DateTime` that incurs less overhead. + +Here it becomes even more visible how the parser scales with the length of the string that is parsed. +Performance of raw parsing + +### Environment Tests performed on a Lenovo P1 G6 laptop: * Intel(R) Core(TM) i9-13900H * Ubuntu 23.10 @@ -36,7 +51,7 @@ mvn jmh:benchmark To plot the result and create the resulting image, you can run `plot.py`, for example: ``` -python3 plot.py -i target/itu_performance.json --size=11,5 +python3 plot.py -i target/itu_performance.json ``` ## Example usage @@ -47,7 +62,7 @@ Add dependency com.ethlo.time itu - 1.7.3 + 1.7.4 ``` @@ -181,7 +196,7 @@ serves as a sane subset of ISO-8601, just like RFC-3339. Typical formats include: * `2017-12-27T23:45Z` - Minute resolution, UTC/Zulu time -* `2017-12-27` - Date only, no timezone (like someones birthday) +* `2017-12-27` - Date only, no timezone (like someone's birthday) * `2017-12` - Year and month only. Like an expiry date. ## Limitations @@ -204,6 +219,11 @@ instead of `60`. ## Changelog +### Version 1.7.4 + +2023-12-26 +* Parser performance improvements. + ### Version 1.7.0 2022-09-03 diff --git a/doc/format.png b/doc/format.png new file mode 100644 index 0000000000000000000000000000000000000000..593ebe79c37cad29b23c034b70d68651405705c7 GIT binary patch literal 24905 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 literal 0 HcmV?d00001 diff --git a/doc/parse_raw.png b/doc/parse_raw.png new file mode 100644 index 0000000000000000000000000000000000000000..6c630a78ce7f5f5b0cefe14e3b32e5e1cea60d17 GIT binary patch literal 26212 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 literal 0 HcmV?d00001 diff --git a/doc/performance.png b/doc/performance.png deleted file mode 100644 index 17498e3553c6b16b6e356c78fd35477bdba5157f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37381 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 diff --git a/plot.py b/plot.py index 39adf16..ce43f51 100755 --- a/plot.py +++ b/plot.py @@ -16,7 +16,7 @@ def __init__(self): self.parser = argparse.ArgumentParser(description='ReportGenerator') self.parser.add_argument('-i', default='target/itu_performance.json', help='The JMH result (JSON) file') self.parser.add_argument('-o', default='output.png', help='Output file path for bar chart image') - self.parser.add_argument('--size', default='10,16', help='Plot size') + self.parser.add_argument('--size', default='18,8', help='Plot size') self.parser.add_argument('--theme', default='default', help='Output theme for bar chart image') self.parser.add_argument('--include', default='parse,parseRaw,format', help='Prefix for test methods to include') @@ -38,7 +38,7 @@ def render(self, dtf, target): plot = dtf.plot(x=0, kind='barh', stacked=False, - title='Nanoseconds per operation (lower is better)', + title='Operations per millisecond (higher is better)', figsize=self.fig_size) for container in plot.containers: diff --git a/pom.xml b/pom.xml index eada5d5..74ba140 100644 --- a/pom.xml +++ b/pom.xml @@ -23,9 +23,9 @@ limitations under the License. com.ethlo.time bundle itu - 1.7.3 + 1.7.4-SNAPSHOT Internet Time Utility - Extremely fast date/time parser and formatter - RFC 3339 (ISO 8601 profile) and W3C format + Very fast date-time parser and formatter - RFC 3339 (ISO 8601 profile) and W3C format @@ -65,25 +65,25 @@ limitations under the License. org.apache.commons commons-lang3 - 3.12.0 + 3.14.0 test com.google.http-client google-http-client - 1.43.1 + 1.43.3 test org.openjdk.jmh jmh-core - 1.35 + 1.37 test org.openjdk.jmh jmh-generator-annprocess - 1.35 + 1.37 test @@ -97,7 +97,7 @@ limitations under the License. maven-compiler-plugin - 3.8.1 + 3.12.1 1.8 1.8 @@ -111,14 +111,14 @@ limitations under the License. 1.8 1.8 - SampleTime - 1 - 30 + Throughput + 10 + 2 1 JSON target/itu_performance.json - 3 - 10 + 10 + 2 diff --git a/render.sh b/render.sh new file mode 100644 index 0000000..e990736 --- /dev/null +++ b/render.sh @@ -0,0 +1,4 @@ +#!/bin/bash +python3 plot.py --include=parse -i target/itu_performance.json --theme theme.mplstyle --size=12,4 -o doc/parse.png +python3 plot.py --include=format -i target/itu_performance.json --theme theme.mplstyle --size=12,4 -o doc/format.png +python3 plot.py --include=raw -i target/itu_performance.json --theme theme.mplstyle --size=12,4 -o doc/parse_raw.png diff --git a/src/main/java/com/ethlo/time/DateTime.java b/src/main/java/com/ethlo/time/DateTime.java index f665644..d23a020 100644 --- a/src/main/java/com/ethlo/time/DateTime.java +++ b/src/main/java/com/ethlo/time/DateTime.java @@ -230,6 +230,11 @@ public LocalDateTime toLocalDatetime() public OffsetDateTime toOffsetDatetime() { assertMinGranularity(Field.MINUTE); + return toOffsetDatetimeNoGranularityCheck(); + } + + public OffsetDateTime toOffsetDatetimeNoGranularityCheck() + { if (offset != null) { return OffsetDateTime.of(year, month, day, hour, minute, second, nano, offset.toZoneOffset()); diff --git a/src/main/java/com/ethlo/time/internal/EthloITU.java b/src/main/java/com/ethlo/time/internal/EthloITU.java index c45ddff..50d4a23 100644 --- a/src/main/java/com/ethlo/time/internal/EthloITU.java +++ b/src/main/java/com/ethlo/time/internal/EthloITU.java @@ -21,9 +21,9 @@ */ import static com.ethlo.time.internal.LeapSecondHandler.LEAP_SECOND_SECONDS; -import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.indexOfNonDigit; +import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.DIGIT_9; +import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.ZERO; import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.parsePositiveInt; -import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.uncheckedParsePositiveInt; import java.time.DateTimeException; import java.time.Month; @@ -90,28 +90,40 @@ private static int writeTz(char[] buf, int start, TimezoneOffset tz) } } - private int getHour(final char[] chars) + private static int scale(int fractions, int len) { - return parsePositiveInt(chars, 11, 13); - } - - private int getMinute(final char[] chars) - { - return parsePositiveInt(chars, 14, 16); - } - - private int getDay(final char[] chars) - { - return parsePositiveInt(chars, 8, 10); + switch (len) + { + case 0: + throw new DateTimeException("Must have at least 1 fraction digit"); + case 1: + return fractions * 100_000_000; + case 2: + return fractions * 10_000_000; + case 3: + return fractions * 1_000_000; + case 4: + return fractions * 100_000; + case 5: + return fractions * 10_000; + case 6: + return fractions * 1_000; + case 7: + return fractions * 100; + case 8: + return fractions * 10; + default: + return fractions; + } } - private DateTime handleTime(char[] chars, int year, int month, int day, int hour, int minute) + private DateTime handleTime(String chars, int year, int month, int day, int hour, int minute) { - switch (chars[16]) + switch (chars.charAt(16)) { // We have more granularity, keep going case TIME_SEPARATOR: - return handleSeconds(year, month, day, hour, minute, chars); + return handleTime(year, month, day, hour, minute, chars); case PLUS: case MINUS: @@ -123,34 +135,35 @@ private DateTime handleTime(char[] chars, int year, int month, int day, int hour default: assertPositionContains(chars, 16, TIME_SEPARATOR, PLUS, MINUS, ZULU_UPPER); } - throw new DateTimeException(new String(chars)); + throw new DateTimeException(chars); } - private void assertPositionContains(char[] chars, int offset, char expected) + private void assertPositionContains(String chars, int offset, char expected) { - if (offset >= chars.length) + if (offset >= chars.length()) { raiseDateTimeException(chars, "Unexpected end of input"); } - if (chars[offset] != expected) + if (chars.charAt(offset) != expected) { throw new DateTimeException("Expected character " + expected - + " at position " + (offset + 1) + " '" + new String(chars) + "'"); + + " at position " + (offset + 1) + " '" + chars + "'"); } } - private void assertPositionContains(char[] chars, int offset, char... expected) + private void assertPositionContains(String chars, int offset, char... expected) { - if (offset >= chars.length) + if (offset >= chars.length()) { raiseDateTimeException(chars, "Unexpected end of input"); } boolean found = false; + final char needle = chars.charAt(offset); for (char e : expected) { - if (chars[offset] == e) + if (needle == e) { found = true; break; @@ -159,27 +172,36 @@ private void assertPositionContains(char[] chars, int offset, char... expected) if (!found) { throw new DateTimeException("Expected character " + Arrays.toString(expected) - + " at position " + (offset + 1) + " '" + new String(chars) + "'"); + + " at position " + (offset + 1) + " '" + chars + "'"); } } - private TimezoneOffset parseTimezone(char[] chars, int offset) + private TimezoneOffset parseTimezone(String chars, int offset) { - final int len = chars.length; + if (offset >= chars.length()) + { + throw new DateTimeException("No timezone information: " + chars); + } + final int len = chars.length(); final int left = len - offset; - final char c = chars[offset]; + final char c = chars.charAt(offset); if (c == ZULU_UPPER || c == ZULU_LOWER) { assertNoMoreChars(chars, offset); return TimezoneOffset.UTC; } + final char sign = chars.charAt(offset); + if (sign != PLUS && sign != MINUS) + { + throw new DateTimeException("Invalid character starting at position " + offset + ": " + chars); + } + if (left != 6) { - throw new DateTimeException("Invalid timezone offset: " + new String(chars, offset, left)); + throw new DateTimeException("Invalid timezone offset: " + new String(chars.toCharArray(), offset, left)); } - final char sign = chars[offset]; int hours = parsePositiveInt(chars, offset + 1, offset + 3); int minutes = parsePositiveInt(chars, offset + 4, offset + 4 + 2); if (sign == MINUS) @@ -187,10 +209,6 @@ private TimezoneOffset parseTimezone(char[] chars, int offset) hours = -hours; minutes = -minutes; } - else if (sign != PLUS) - { - throw new DateTimeException("Invalid character starting at position " + offset + 1); - } if (sign == MINUS && hours == 0 && minutes == 0) { @@ -200,11 +218,11 @@ else if (sign != PLUS) return TimezoneOffset.ofHoursMinutes(hours, minutes); } - private void assertNoMoreChars(char[] chars, int lastUsed) + private void assertNoMoreChars(String chars, int lastUsed) { - if (chars.length > lastUsed + 1) + if (chars.length() > lastUsed + 1) { - throw new DateTimeException("Trailing junk data after position " + (lastUsed + 1) + ": " + new String(chars)); + throw new DateTimeException("Trailing junk data after position " + (lastUsed + 1) + ": " + chars); } } @@ -295,7 +313,7 @@ private void addFractions(char[] buf, int fractionDigits, int nano) @Override public OffsetDateTime parseDateTime(final String dateTime) { - return assertSecondsGranularity(parse(dateTime)).toOffsetDatetime(); + return assertSecondsGranularity(parse(dateTime)).toOffsetDatetimeNoGranularityCheck(); } private DateTime assertSecondsGranularity(DateTime dt) @@ -307,7 +325,6 @@ private DateTime assertSecondsGranularity(DateTime dt) return dt; } - @Override public String formatUtcMilli(OffsetDateTime date) { @@ -333,20 +350,19 @@ public String formatUtc(OffsetDateTime date) } @Override - public DateTime parse(String text) + public DateTime parse(String chars) { - if (text == null) + if (chars == null) { throw new NullPointerException("text cannot be null"); } - final int len = text.length(); - final char[] chars = text.toCharArray(); + final int len = chars.length(); // Date portion // YEAR - final int years = getYear(chars); + final int years = parsePositiveInt(chars, 0, 4); if (4 == len) { return DateTime.ofYear(years); @@ -354,7 +370,7 @@ public DateTime parse(String text) // MONTH assertPositionContains(chars, 4, DATE_SEPARATOR); - final int months = getMonth(chars); + final int months = parsePositiveInt(chars, 5, 7); if (7 == len) { return DateTime.ofYearMonth(years, months); @@ -362,7 +378,7 @@ public DateTime parse(String text) // DAY assertPositionContains(chars, 7, DATE_SEPARATOR); - final int days = getDay(chars); + final int days = parsePositiveInt(chars, 8, 10); if (10 == len) { return DateTime.ofDate(years, months, days); @@ -370,68 +386,74 @@ public DateTime parse(String text) // HOURS assertPositionContains(chars, 10, SEPARATOR_UPPER, SEPARATOR_LOWER, SEPARATOR_SPACE); - final int hours = getHour(chars); + final int hours = parsePositiveInt(chars, 11, 13); // MINUTES assertPositionContains(chars, 13, TIME_SEPARATOR); - final int minutes = getMinute(chars); - if (16 == len) + final int minutes = parsePositiveInt(chars, 14, 16); + if (len > 16) { - return DateTime.of(years, months, days, hours, minutes, null); + // SECONDS or TIMEZONE + return handleTime(chars, years, months, days, hours, minutes); } - // SECONDS or TIMEZONE - return handleTime(chars, years, months, days, hours, minutes); - } - - private int getMonth(final char[] chars) - { - return parsePositiveInt(chars, 5, 7); + // Have only minutes + return DateTime.of(years, months, days, hours, minutes, null); } - private int getYear(final char[] chars) - { - return parsePositiveInt(chars, 0, 4); - } - - private DateTime handleSeconds(int year, int month, int day, int hour, int minute, char[] chars) + private DateTime handleTime(int year, int month, int day, int hour, int minute, String chars) { // From here the specification is more lenient - final int remaining = chars.length - 19; + final int len = chars.length(); + final int remaining = len - 19; if (remaining == 0) { - final int seconds = getSeconds(chars); - return leapSecondCheck(year, month, day, hour, minute, seconds, 0, null, 0); + final int seconds = parsePositiveInt(chars, 17, 19); + leapSecondCheck(year, month, day, hour, minute, seconds, 0, null); + return new DateTime(Field.SECOND, year, month, day, hour, minute, seconds, 0, null, 0); } TimezoneOffset offset = null; int fractions = 0; int fractionDigits = 0; - final char c = chars[19]; - if (remaining == 1 && (c == ZULU_UPPER || c == ZULU_LOWER)) - { - // Do nothing we are done - offset = TimezoneOffset.UTC; - assertNoMoreChars(chars, 19); - } - else if (remaining >= 1 && c == FRACTION_SEPARATOR) + char c = chars.charAt(19); + if (c == FRACTION_SEPARATOR) { // We have fractional seconds - final int idx = indexOfNonDigit(chars, 20); - if (idx != -1) + int result = 0; + int idx = 20; + boolean nonDigitFound = false; + do { - // We have an end of fractions - final int len = idx - 20; - fractions = getFractions(chars, idx, len); - fractionDigits = len; - offset = parseTimezone(chars, idx); - } - else + c = chars.charAt(idx); + if (c < ZERO || c > DIGIT_9) + { + nonDigitFound = true; + fractionDigits = idx - 20; + fractions = scale(-result, fractionDigits); + offset = parseTimezone(chars, idx); + } + else + { + result = (result << 1) + (result << 3); + result -= c - ZERO; + } + idx++; + } while (idx < len && !nonDigitFound); + + if (!nonDigitFound) { - raiseDateTimeException(chars, "No timezone information"); + fractionDigits = idx - 20; + fractions = scale(-result, fractionDigits); + offset = parseTimezone(chars, idx); } } - else if (remaining >= 1 && (c == PLUS || c == MINUS)) + else if (remaining == 1 && (c == ZULU_UPPER || c == ZULU_LOWER)) + { + // Do nothing we are done + offset = TimezoneOffset.UTC; + } + else if (c == PLUS || c == MINUS) { // No fractional sections offset = parseTimezone(chars, 19); @@ -441,10 +463,14 @@ 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, fractionDigits); + final int second = parsePositiveInt(chars, 17, 19); + + leapSecondCheck(year, month, day, hour, minute, second, fractions, offset); + + return fractionDigits > 0 ? DateTime.of(year, month, day, hour, minute, second, fractions, offset, fractionDigits) : DateTime.of(year, month, day, hour, minute, second, offset); } - private DateTime leapSecondCheck(int year, int month, int day, int hour, int minute, int second, int nanos, TimezoneOffset offset, final int fractionDigits) + private void leapSecondCheck(int year, int month, int day, int hour, int minute, int second, int nanos, TimezoneOffset offset) { if (second == LEAP_SECOND_SECONDS) { @@ -465,45 +491,10 @@ private DateTime leapSecondCheck(int year, int month, int day, int hour, int min } } } - 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) + private void raiseDateTimeException(String chars, String message) { - throw new DateTimeException(message + ": " + new String(chars)); - } - - private int getSeconds(final char[] chars) - { - return parsePositiveInt(chars, 17, 19); - } - - private int getFractions(final char[] chars, final int idx, final int len) - { - final int fractions; - fractions = uncheckedParsePositiveInt(chars, 20, idx); - switch (len) - { - case 0: - throw new DateTimeException("Must have at least 1 fraction digit"); - case 1: - return fractions * 100_000_000; - case 2: - return fractions * 10_000_000; - case 3: - return fractions * 1_000_000; - case 4: - return fractions * 100_000; - case 5: - return fractions * 10_000; - case 6: - return fractions * 1_000; - case 7: - return fractions * 100; - case 8: - return fractions * 10; - default: - return fractions; - } + throw new DateTimeException(message + ": " + chars); } } diff --git a/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java b/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java index adec589..b989b85 100644 --- a/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java +++ b/src/main/java/com/ethlo/time/internal/LimitedCharArrayIntegerUtil.java @@ -26,7 +26,7 @@ public final class LimitedCharArrayIntegerUtil { public static final char DIGIT_9 = '9'; - private static final char ZERO = '0'; + public static final char ZERO = '0'; private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; private static final int TABLE_WIDTH = 4; private static final int RADIX = 10; @@ -48,37 +48,23 @@ private LimitedCharArrayIntegerUtil() { } - public static int parsePositiveInt(final char[] strNum, int startInclusive, int endExclusive) + public static int parsePositiveInt(final String strNum, int startInclusive, int endExclusive) { - if (endExclusive > strNum.length) + if (endExclusive > strNum.length()) { - throw new DateTimeException("Unexpected end of expression at position " + strNum.length + ": '" + new String(strNum) + "'"); + throw new DateTimeException("Unexpected end of expression at position " + strNum.length() + ": '" + strNum + "'"); } int result = 0; for (int i = startInclusive; i < endExclusive; i++) { - final char c = strNum[i]; - if (isNotDigit(c)) + final char c = strNum.charAt(i); + if (c < ZERO || c > DIGIT_9) { throw new DateTimeException("Character " + c + " is not a digit"); } - int digit = digit(c); - result *= RADIX; - result -= digit; - } - return -result; - } - - public static int uncheckedParsePositiveInt(final char[] strNum, int startInclusive, int endExclusive) - { - int result = 0; - for (int i = startInclusive; i < endExclusive; i++) - { - final char c = strNum[i]; - int digit = digit(c); - result *= RADIX; - result -= digit; + result = (result << 1) + (result << 3); + result -= c - ZERO; } return -result; } @@ -148,21 +134,12 @@ public static int indexOfNonDigit(final char[] text, int offset) { for (int i = offset; i < text.length; i++) { - if (isNotDigit(text[i])) + char c = text[i]; + if (c < ZERO || c > DIGIT_9) { return i; } } return -1; } - - private static boolean isNotDigit(char c) - { - return (c < ZERO || c > DIGIT_9); - } - - private static int digit(char c) - { - return c - ZERO; - } } diff --git a/src/test/java/com/ethlo/time/CorrectnessTest.java b/src/test/java/com/ethlo/time/CorrectnessTest.java index 41a2447..e1c62e5 100644 --- a/src/test/java/com/ethlo/time/CorrectnessTest.java +++ b/src/test/java/com/ethlo/time/CorrectnessTest.java @@ -97,6 +97,13 @@ public void parseWithFragmentsNoTimezone() assertThat(exception.getMessage()).isEqualTo("No timezone information: 2017-12-21T12:20:45.987"); } + @Test + public void parseWithFragmentsNonDigit() + { + final DateTimeException exception = assertThrows(DateTimeException.class, () -> parser.parseDateTime("2017-12-21T12:20:45.9b7Z")); + assertThat(exception.getMessage()).isEqualTo("Invalid character starting at position 21: 2017-12-21T12:20:45.9b7Z"); + } + @Test public void testParseLeapSecondUTCJune() { diff --git a/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java b/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java index 92da3d3..3091feb 100644 --- a/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/FormatterBenchmarkTest.java @@ -29,7 +29,7 @@ import com.ethlo.time.internal.Rfc3339Formatter; -@OutputTimeUnit(TimeUnit.NANOSECONDS) +@OutputTimeUnit(TimeUnit.MILLISECONDS) public abstract class FormatterBenchmarkTest { private static final OffsetDateTime input = OffsetDateTime.parse("2017-12-21T12:20:45.987654321Z"); diff --git a/src/test/java/com/ethlo/time/ParserBenchmarkTest.java b/src/test/java/com/ethlo/time/ParserBenchmarkTest.java index afeb1c1..7f97dcb 100644 --- a/src/test/java/com/ethlo/time/ParserBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/ParserBenchmarkTest.java @@ -28,7 +28,7 @@ import com.ethlo.time.internal.Rfc3339Parser; -@OutputTimeUnit(TimeUnit.NANOSECONDS) +@OutputTimeUnit(TimeUnit.MILLISECONDS) public abstract class ParserBenchmarkTest { protected static Rfc3339Parser parser; diff --git a/src/test/java/com/ethlo/time/itu/EthloItuProfilerTest.java b/src/test/java/com/ethlo/time/itu/EthloItuProfilerTest.java index 45ef5a9..224d984 100644 --- a/src/test/java/com/ethlo/time/itu/EthloItuProfilerTest.java +++ b/src/test/java/com/ethlo/time/itu/EthloItuProfilerTest.java @@ -20,12 +20,10 @@ * #L% */ -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import com.ethlo.time.internal.EthloITU; -@Disabled public class EthloItuProfilerTest { private static final EthloITU ethloItu = EthloITU.getInstance(); diff --git a/src/test/java/com/ethlo/time/itu/ITULenientParserBenchmarkTest.java b/src/test/java/com/ethlo/time/itu/ITULenientParserBenchmarkTest.java index 2217347..74bb474 100644 --- a/src/test/java/com/ethlo/time/itu/ITULenientParserBenchmarkTest.java +++ b/src/test/java/com/ethlo/time/itu/ITULenientParserBenchmarkTest.java @@ -23,32 +23,31 @@ import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import com.ethlo.time.internal.EthloITU; -@Warmup(iterations = 1, time = 30) -@BenchmarkMode(Mode.SampleTime) -@Fork(1) -@OutputTimeUnit(TimeUnit.NANOSECONDS) +@OutputTimeUnit(TimeUnit.MILLISECONDS) public class ITULenientParserBenchmarkTest { private static final EthloITU ethloItu = EthloITU.getInstance(); @Benchmark - public void parseRawDate(final Blackhole blackhole) + public void rawDate(final Blackhole blackhole) { blackhole.consume(ethloItu.parse("2017-12-21")); } @Benchmark - public void parseRawSecond(final Blackhole blackhole) + public void rawSecond(final Blackhole blackhole) { blackhole.consume(ethloItu.parse("2017-12-21T12:20:45Z")); } + + @Benchmark + public void rawNanos(final Blackhole blackhole) + { + blackhole.consume(ethloItu.parse("2017-12-21T12:20:45.123123123Z")); + } } diff --git a/src/test/java/com/ethlo/time/jdk/JdkRfc3339.java b/src/test/java/com/ethlo/time/jdk/JdkRfc3339.java index 6d71537..3250ad5 100644 --- a/src/test/java/com/ethlo/time/jdk/JdkRfc3339.java +++ b/src/test/java/com/ethlo/time/jdk/JdkRfc3339.java @@ -20,14 +20,14 @@ * #L% */ +import com.ethlo.time.internal.AbstractRfc3339; + import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; -import com.ethlo.time.internal.AbstractRfc3339; - /** * Java 8 JDK classes. The safe and normally "efficient enough" choice. * diff --git a/theme.mplstyle b/theme.mplstyle new file mode 100644 index 0000000..a7fc9ba --- /dev/null +++ b/theme.mplstyle @@ -0,0 +1,34 @@ +# Seaborn common parameters +# .15 = dark_gray +# .8 = light_gray +text.color: .15 +axes.labelcolor: .15 +legend.frameon: False +legend.numpoints: 1 +legend.scatterpoints: 1 +xtick.direction: out +ytick.direction: out +xtick.color: .15 +ytick.color: .15 +axes.axisbelow: True +font.family: sans-serif +grid.linestyle: - +lines.solid_capstyle: round + +# Seaborn darkgrid parameters +axes.grid: False +axes.facecolor: EAEAF2 +axes.edgecolor: white +axes.linewidth: 0 +grid.color: white +xtick.major.size: 0 +ytick.major.size: 0 +xtick.minor.size: 0 +ytick.minor.size: 0 + +# Custom +font.sans-serif: Overpass, Helvetica, Helvetica Neue, Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif +axes.prop_cycle: cycler('color', ['7A76C2', 'ff6e9c98', 'f62196', '18c0c4', 'f3907e', '66E9EC']) +image.cmap: RdPu +figure.facecolor: fefeff +savefig.facecolor: fefeff \ No newline at end of file