- Notifications
You must be signed in to change notification settings - Fork10
An extremely fast parser and formatter of standardized date and date-times supporting RFC-3339 (ISO-8601 profile) and more.
License
ethlo/itu
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
An extremely fast parser and formatter of ISO-8601 date-times. HandleRFC-3339 Timestamps and W3CDate and Time Formats with ease!Now also supports a subset of duration strings!
Low ceremony, high productivity with a very easy to use API.
- Well-documented.
- Aim for 100% specification compliance.
- Handling leap-seconds.
- Zero dependencies.
- Java 8 compatible.
- Apache 2 licensed.
Typically,10x to 30x faster than parsing and formatting with Java JDK classes.
The details and tests are available in a separate repository,date-time-wars.
Add dependency
<dependency> <groupId>com.ethlo.time</groupId> <artifactId>itu</artifactId> <version>1.14.0</version></dependency>
Below you find some samples of usage of this library. Please check out thejavadoc for more details.
This is a collection of usage examples for parsing.
The simplest and fastest way to parse an RFC-3339 timestamp by far!
finalStringtext ="2012-12-27T19:07:22.123456789-03:00";finalOffsetDateTimedateTime =ITU.parseDateTime(text);assertThat(dateTime.toString()).isEqualTo(text);
Parses a date-time with flexible granularity. Works for anything from a year to a timestamp with nanoseconds, with or without timezone offset.
finalStringtext ="2012-12-27T19:07:23.123";finalDateTimedateTime =ITU.parseLenient(text);finalStringformatted =dateTime.toString();assertThat(formatted).isEqualTo(text);
In case you encounter the need for a somewhat different time-separator or fraction separatoryou can use theParseConfig
to set up you preferred delimiters.
finalParseConfigconfig =ParseConfig.DEFAULT .withDateTimeSeparators('T','|') .withFractionSeparators('.',',');finalDateTimeresult =ITU.parseLenient("1999-11-22|11:22:17,191",config);assertThat(result.toString()).isEqualTo("1999-11-22T11:22:17.191");
This allows you to track where to start reading. Note that the check for trailing junk is disabled when usingParsePosition
.
finalParsePositionpos =newParsePosition(10);finalOffsetDateTimeresult =ITU.parseDateTime("some-data,1999-11-22T11:22:19+05:30,some-other-data",pos);assertThat(result.toString()).isEqualTo("1999-11-22T11:22:19+05:30");assertThat(pos.getIndex()).isEqualTo(35);
This is useful if you need to handle different granularity with different logic or interpolation.
finalTemporalHandler<OffsetDateTime>handler =newTemporalHandler<OffsetDateTime>() {@OverridepublicOffsetDateTimehandle(finalLocalDatelocalDate) {returnlocalDate.atTime(OffsetTime.of(LocalTime.of(0,0),ZoneOffset.UTC)); }@OverridepublicOffsetDateTimehandle(finalOffsetDateTimeoffsetDateTime) {returnoffsetDateTime; } };finalOffsetDateTimeresult =ITU.parse("2017-12-06",handler);assertThat(result.toString()).isEqualTo("2017-12-06T00:00Z");
In some real world scenarios, it is useful to parse a best-effort timestamp. To ease usage, we can easily convert a rawDateTime
instance intoInstant
.
Note the limitations and the assumption of UTC time-zone, as mentioned in the javadoc.
finalInstantinstant =ITU.parseLenient("2017-12-06").toInstant();assertThat(instant.toString()).isEqualTo("2017-12-06T00:00:00Z");
In case the format is not supported directly, you can build your own parser.
finalDateTimeParserparser =DateTimeParsers.of(digits(DAY,2),separators('-'),digits(MONTH,2),separators('-'),digits(YEAR,4),separators(' '),digits(HOUR,2),digits(MINUTE,2),digits(SECOND,2),separators(','),fractions() );finalStringtext ="31-12-2000 235937,123456";finalDateTimeresult =parser.parse(text);assertThat(result.toString()).isEqualTo("2000-12-31T23:59:37.123456");
DateTimerParser
interface for RFC-3339.
finalDateTimeParserparser =DateTimeParsers.rfc3339();finalStringtext ="2000-12-31 23:59:37.123456";finalDateTimeresult =parser.parse(text);assertThat(result.toString()).isEqualTo("2000-12-31T23:59:37.123456");
DateTimerParser
interface for local time.
finalDateTimeParserparser =DateTimeParsers.localTime();finalStringtext ="23:59:37.123456";finalLocalTimeresult =parser.parse(text).toLocalTime();assertThat(result.toString()).isEqualTo(text);
DateTimerParser
interface for local date.
finalDateTimeParserparser =DateTimeParsers.localDate();finalStringtext ="2013-12-24";finalLocalDateresult =parser.parse(text).toLocalDate();assertThat(result.toString()).isEqualTo(text);
This is a collection of usage examples for formatting.
The simplest and fastest way to format an RFC-3339 timestamp by far!
finalOffsetDateTimeinput =OffsetDateTime.of(2012,12,27,19,7,22,123456789,ZoneOffset.ofHoursMinutes(-3,0));assertThat(ITU.formatUtcNano(input)).isEqualTo("2012-12-27T22:07:22.123456789Z");assertThat(ITU.formatUtcMicro(input)).isEqualTo("2012-12-27T22:07:22.123456Z");assertThat(ITU.formatUtcMilli(input)).isEqualTo("2012-12-27T22:07:22.123Z");assertThat(ITU.formatUtc(input)).isEqualTo("2012-12-27T22:07:22Z");
Format withDateTime
.
finalDateTimeinput =DateTime.of(2020,11,27,12,39,19,null);assertThat(input.toString(Field.MINUTE)).isEqualTo("2020-11-27T12:39");assertThat(input.toString(Field.SECOND)).isEqualTo("2020-11-27T12:39:19");
Parse a valid leap-second (i.e. it is on a date that would allow for it, and it is also in the list of known actual leap-seconds).
try {ITU.parseDateTime("1990-12-31T15:59:60-08:00"); }catch (LeapSecondExceptionexc) {// The following helper methods are available let you decide how to progressassertThat(exc.getSecondsInMinute()).isEqualTo(60);assertThat(exc.getNearestDateTime()).isEqualTo(OffsetDateTime.of(1990,12,31,16,0,0,0,ZoneOffset.ofHours(-8)));assertThat(exc.isVerifiedValidLeapYearMonth()).isTrue(); }
Parses a duration string, a strict subset of ISO 8601 durations.
This method supports time-based durations with the following units:
- Weeks (
W
) - Days (
D
) - Hours (
H
) - Minutes (
M
) - Seconds (
S
), including fractional seconds up to nanosecond precision
The following units areexplicitly not allowed to avoid ambiguity:
- Years (
Y
) - Months (
M
in the date section)
Negative durations are supported and must be prefixed with-P
, as specified in ISO 8601.
The parsed duration will be represented using:
- A
long
for total seconds - An
int
for nanosecond precision
The nanosecond component is always positive, with the sign absorbed by the seconds field,
following Java and ISO 8601 conventions.
P2DT3H4M5.678901234S
→ 2 days, 3 hours, 4 minutes, 5.678901234 secondsPT5M30S
→ 5 minutes, 30 seconds-PT2.5S
→ Negative 2.5 seconds-P1D
→ Negative 1 day
P1Y2M3DT4H
→ ContainsY
andM
PT
→ Missing time values afterT
P-1D
→ Incorrect negative placement
finalDurationduration =ITU.parseDuration("P4W");assertThat(duration.getSeconds()).isEqualTo(2_419_200L);
finalDurationduration =ITU.parseDuration("P4W10DT28H122M1.123456S");assertThat(duration.normalized()).isEqualTo("P5W4DT6H2M1.123456S");
There are an endless amount of APIs with non-standard date/time exchange, and the goal of this project is to make it abreeze to do the right thing!
Some projects use epoch time-stamps for date-time exchange, and from a performance perspective thismay make sensein some cases. With this project one can do-the-right-thing and maintain performance in date-time handling.
Importantly, this project isnot a premature optimization. In real-life scenarios there are examples of date-time parsing hindering optimal performance. The samples include data ingestion intodatabases and search engines, to importing/exporting data on less powerful devices, like cheaper Android devices.
- It is not human-readable, so debugging and direct manipulation is harder
- Limited resolution and/or time-range available
- Unclear resolution and/or time-range
RFC-3339 is a subset/profile defined byW3C of theformats defined inISO-8601, to simplify date and time exhange inmodern Internet protocols.
Typical formats include:
2017-12-27T23:45:32Z
- No fractional seconds, UTC/Zulu time2017-12-27T23:45:32.999Z
- Millisecond fractions, UTC/Zulu time2017-12-27T23:45:32.999999Z
- Microsecond fractions, UTC/Zulu time2017-12-27T23:45:32.999999999Z
- Nanosecond fractions, UTC/Zulu time2017-12-27T18:45:32-05:00
- No fractional seconds, EST time2017-12-27T18:45:32.999-05:00
- Millisecond fractions, EST time2017-12-27T18:45:32.999999-05:00
- Microsecond fractions, EST time2017-12-27T18:45:32.999999999-05:00
- Nanosecond fractions, EST time
Date and Time Formats is anote, meaning it is not endorsed, but it stillserves as a sane subset of ISO-8601, just like RFC-3339.
Typical formats include:
2017-12-27T23:45Z
- Minute resolution, UTC/Zulu time2017-12-27
- Date only, no timezone (like someone's birthday)2017-12
- Year and month only. Like an expiry date.
For the sake of avoiding data integrity issues, this library will not allow offset of-00:00
. Such offset is describedin RFC3339 section 4.3., named "Unknown Local Offset Convention". Such offset is explicitly prohibited in ISO-8601 aswell.
If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of "-00:00". This differs semantically from an offset of "Z" or "+00:00", which implythat UTC is the preferred reference point for the specified time.
Since Java'sjava.time
classes do not support storing leap seconds, ITU will throw aLeapSecondException
if one isencountered to signal that this is a leap second. The exception can then be queried for the second-value. Storing suchvalues is not possible in ajava.time.OffsetDateTime
, the60
is therefore abandoned and the date-time will use59
instead of60
.
About
An extremely fast parser and formatter of standardized date and date-times supporting RFC-3339 (ISO-8601 profile) and more.