Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/ itu Public

An extremely fast parser and formatter of standardized date and date-times supporting RFC-3339 (ISO-8601 profile) and more.

License

Notifications You must be signed in to change notification settings

ethlo/itu

Internet Time Utility

Maven Central javadoc Hex.pm Codacy Badge codecov

An extremely fast parser and formatter of ISO-8601 date-times. Handle RFC-3339 Timestamps and W3C Date and Time Formats with ease! Now also supports a subset of duration strings!

Features

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.

Performance

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.

Usage

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 the javadoc for more details.

Parsing

This is a collection of usage examples for parsing.

parseRfc3339

source »

The simplest and fastest way to parse an RFC-3339 timestamp by far!

final String text = "2012-12-27T19:07:22.123456789-03:00";
final OffsetDateTime dateTime = ITU.parseDateTime(text);
assertThat(dateTime.toString()).isEqualTo(text);

parseLenient

source »

Parses a date-time with flexible granularity. Works for anything from a year to a timestamp with nanoseconds, with or without timezone offset.

final String text = "2012-12-27T19:07:23.123";
final DateTime dateTime = ITU.parseLenient(text);
final String formatted = dateTime.toString();
assertThat(formatted).isEqualTo(text);

parseLenientWithCustomSeparators

source »

In case you encounter the need for a somewhat different time-separator or fraction separator you can use the ParseConfig to set up you preferred delimiters.

final ParseConfig config = ParseConfig.DEFAULT
 .withDateTimeSeparators('T', '|')
 .withFractionSeparators('.', ',');
final DateTime result = ITU.parseLenient("1999-11-22|11:22:17,191", config);
assertThat(result.toString()).isEqualTo("1999-11-22T11:22:17.191");

parsePosition

source »

This allows you to track where to start reading. Note that the check for trailing junk is disabled when using ParsePosition.

final ParsePosition pos = new ParsePosition(10);
final OffsetDateTime result = ITU.parseDateTime("some-data,1999年11月22日T11:22:19+05:30,some-other-data", pos);
assertThat(result.toString()).isEqualTo("1999-11-22T11:22:19+05:30");
assertThat(pos.getIndex()).isEqualTo(35);

explicitGranularity

source »

This is useful if you need to handle different granularity with different logic or interpolation.

final TemporalHandler<OffsetDateTime> handler = new TemporalHandler<OffsetDateTime>()
 {
 @Override
 public OffsetDateTime handle(final LocalDate localDate)
 {
 return localDate.atTime(OffsetTime.of(LocalTime.of(0, 0), ZoneOffset.UTC));
 }
 @Override
 public OffsetDateTime handle(final OffsetDateTime offsetDateTime)
 {
 return offsetDateTime;
 }
 };
final OffsetDateTime result = ITU.parse("2017-12-06", handler);
assertThat(result.toString()).isEqualTo("2017-12-06T00:00Z");

lenientTimestamp

source »

In some real world scenarios, it is useful to parse a best-effort timestamp. To ease usage, we can easily convert a raw DateTime instance into Instant.

Note the limitations and the assumption of UTC time-zone, as mentioned in the javadoc.

final Instant instant = ITU.parseLenient("2017-12-06").toInstant();
assertThat(instant.toString()).isEqualTo("2017-12-06T00:00:00Z");

parseCustomFormat

source »

In case the format is not supported directly, you can build your own parser.

final DateTimeParser parser = 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()
 );
final String text = "31-12-2000 235937,123456";
final DateTime result = parser.parse(text);
assertThat(result.toString()).isEqualTo("2000-12-31T23:59:37.123456");

parseUsingInterfaceRfc33939

source »

DateTimerParser interface for RFC-3339.

final DateTimeParser parser = DateTimeParsers.rfc3339();
final String text = "2000-12-31 23:59:37.123456";
final DateTime result = parser.parse(text);
assertThat(result.toString()).isEqualTo("2000-12-31T23:59:37.123456");

parseUsingInterfaceLocalTime

source »

DateTimerParser interface for local time.

final DateTimeParser parser = DateTimeParsers.localTime();
final String text = "23:59:37.123456";
final LocalTime result = parser.parse(text).toLocalTime();
assertThat(result.toString()).isEqualTo(text);

parseUsingInterfaceLocalDate

source »

DateTimerParser interface for local date.

final DateTimeParser parser = DateTimeParsers.localDate();
final String text = "2013-12-24";
final LocalDate result = parser.parse(text).toLocalDate();
assertThat(result.toString()).isEqualTo(text);

Formatting

This is a collection of usage examples for formatting.

formatRfc3339WithUTC

source »

The simplest and fastest way to format an RFC-3339 timestamp by far!

final OffsetDateTime input = 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");

formatWithDateTime

source »

Format with DateTime.

final DateTime input = 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");

Leap-second handling

parseLeapSecond

source »

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 (LeapSecondException exc)
 {
 // The following helper methods are available let you decide how to progress
 assertThat(exc.getSecondsInMinute()).isEqualTo(60);
 assertThat(exc.getNearestDateTime()).isEqualTo(OffsetDateTime.of(1990, 12, 31, 16, 0, 0, 0, ZoneOffset.ofHours(-8)));
 assertThat(exc.isVerifiedValidLeapYearMonth()).isTrue();
 }

Duration Parser

Parses a duration string, a strict subset of ISO 8601 durations.

Supported Units

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

Not Allowed Units

The following units are explicitly not allowed to avoid ambiguity:

  • Years (Y)
  • Months (M in the date section)

Negative Durations

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.

Examples

Valid Input

  • P2DT3H4M5.678901234S → 2 days, 3 hours, 4 minutes, 5.678901234 seconds
  • PT5M30S → 5 minutes, 30 seconds
  • -PT2.5S → Negative 2.5 seconds
  • -P1D → Negative 1 day

Invalid Input

  • P1Y2M3DT4H → Contains Y and M
  • PT → Missing time values after T
  • P-1D → Incorrect negative placement

simple

source »

final Duration duration = ITU.parseDuration("P4W");
assertThat(duration.getSeconds()).isEqualTo(2_419_200L);

fullNotNormalizedToNormalized

source »

final Duration duration = ITU.parseDuration("P4W10DT28H122M1.123456S");
assertThat(duration.normalized()).isEqualTo("P5W4DT6H2M1.123456S");

Q & A

Why this little project?

There are an endless amount of APIs with non-standard date/time exchange, and the goal of this project is to make it a breeze to do the right thing!

Why the performance focus?

Some projects use epoch time-stamps for date-time exchange, and from a performance perspective this may make sense in some cases. With this project one can do-the-right-thing and maintain performance in date-time handling.

Importantly, this project is not a premature optimization. In real-life scenarios there are examples of date-time parsing hindering optimal performance. The samples include data ingestion into databases and search engines, to importing/exporting data on less powerful devices, like cheaper Android devices.

What is wrong with epoch timestamps?

  • 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

What is RFC-3339?

RFC-3339 is a subset/profile defined by W3C of the formats defined in ISO-8601, to simplify date and time exhange in modern Internet protocols.

Typical formats include:

  • 2017年12月27日T23:45:32Z - No fractional seconds, UTC/Zulu time
  • 2017年12月27日T23:45:32.999Z - Millisecond fractions, UTC/Zulu time
  • 2017年12月27日T23:45:32.999999Z - Microsecond fractions, UTC/Zulu time
  • 2017年12月27日T23:45:32.999999999Z - Nanosecond fractions, UTC/Zulu time
  • 2017年12月27日T18:45:32-05:00 - No fractional seconds, EST time
  • 2017年12月27日T18:45:32.999-05:00 - Millisecond fractions, EST time
  • 2017年12月27日T18:45:32.999999-05:00 - Microsecond fractions, EST time
  • 2017年12月27日T18:45:32.999999999-05:00 - Nanosecond fractions, EST time

What is W3C - Date and Time Formats

Date and Time Formats is a note, meaning it is not endorsed, but it still serves as a sane subset of ISO-8601, just like RFC-3339.

Typical formats include:

  • 2017年12月27日T23:45Z - Minute resolution, UTC/Zulu time
  • 2017年12月27日 - Date only, no timezone (like someone's birthday)
  • 2017-12 - Year and month only. Like an expiry date.

Limitations

Local offset

For the sake of avoiding data integrity issues, this library will not allow offset of -00:00. Such offset is described in RFC3339 section 4.3., named "Unknown Local Offset Convention". Such offset is explicitly prohibited in ISO-8601 as well.

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 imply that UTC is the preferred reference point for the specified time.

Leap second parsing

Since Java's java.time classes do not support storing leap seconds, ITU will throw a LeapSecondException if one is encountered to signal that this is a leap second. The exception can then be queried for the second-value. Storing such values is not possible in a java.time.OffsetDateTime, the 60 is therefore abandoned and the date-time will use 59 instead of 60.

About

An extremely fast parser and formatter of standardized date and date-times supporting RFC-3339 (ISO-8601 profile) and more.

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Contributors 7

Languages

AltStyle によって変換されたページ (->オリジナル) /