Itu
An extremely fast parser and formatter of standardized date and date-times supporting RFC-3339 (ISO-8601 profile) and more.
Install / Use
/learn @ethlo/ItuREADME
Internet Time Utility
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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-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);
explicitGranularity
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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
<smaller style="float:right;">source »</smaller>
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 (
Min the date section)
Negative Durations
Negative durations are supported and must be prefixe
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
