Giter Site home page Giter Site logo

ethlo / itu Goto Github PK

View Code? Open in Web Editor NEW
21.0 4.0 7.0 1.15 MB

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

License: Apache License 2.0

Java 100.00%
rfc3339 time java parser formatter dateparser datetime date library optimized

itu's Introduction

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!

Features

  • Very easy to use.
  • Well-documented API.
  • Aim for 100% specification compliance.
  • Aware of leap-seconds
  • No dependencies, small jar.
  • Apache 2 licensed, can be used in any project, even commercial.
  • Java 8+ compatible

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.10.2</version>
  <!-- If you want to use minified JAR -->  
  <classifier>small</classifier>
</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-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

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();
        }

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-27T23:45:32Z - No fractional seconds, UTC/Zulu time
  • 2017-12-27T23:45:32.999Z - Millisecond fractions, UTC/Zulu time
  • 2017-12-27T23:45:32.999999Z - Microsecond fractions, UTC/Zulu time
  • 2017-12-27T23:45:32.999999999Z - Nanosecond fractions, UTC/Zulu time
  • 2017-12-27T18:45:32-05:00 - No fractional seconds, EST time
  • 2017-12-27T18:45:32.999-05:00 - Millisecond fractions, EST time
  • 2017-12-27T18:45:32.999999-05:00 - Microsecond fractions, EST time
  • 2017-12-27T18: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-27T23: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.

itu's People

Contributors

aalmiray avatar dependabot[bot] avatar ethlo avatar marcospoethatyatta avatar tobi5775 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

itu's Issues

The grammar element time-second may have the value "60" at the end of months in which a leap second occurs

Please, look https://tools.ietf.org/html/rfc3339 examples:

1990-12-31T23:59:60Z - This represents the leap second inserted at the end of 1990.
1990-12-31T15:59:60-08:00 - This represents the same leap second in Pacific Standard Time, 8 hours behind UTC.

ITU cannot parse these date-times.

RFC3339 documentation says:

The grammar element time-second may have the value "60" at the end of
months in which a leap second occurs -- to date: June (XXXX-06-
30T23:59:60Z) or December (XXXX-12-31T23:59:60Z); see Appendix D for
a table of leap seconds. It is also possible for a leap second to be
subtracted, at which times the maximum value of time-second is "58".
At all other times the maximum value of time-second is "59".
Further, in time zones other than "Z", the leap second point is
shifted by the zone offset (so it happens at the same instant around
the globe).

Valid formats not parsed correctly

2016-04-27T11:30:24.952123456 and 2016-04-27T11:30:24.952123 are valid formats but throw expections

Caused by: java.time.DateTimeException: Invalid timezone offset: 952123456
at com.ethlo.time.FastInternetDateTimeUtil.parseTz(FastInternetDateTimeUtil.java:99)
at com.ethlo.time.FastInternetDateTimeUtil.seconds(FastInternetDateTimeUtil.java:383)
at com.ethlo.time.FastInternetDateTimeUtil.doParseLenient(FastInternetDateTimeUtil.java:331)
at com.ethlo.time.FastInternetDateTimeUtil.parseDateTime(FastInternetDateTimeUtil.java:53)

Invalid date time being accepted

This is a regression as previously in v1.8.0 the invalid date time 1963-06-19T08:30:06.28123+01:00Z that has a trailing Z was being rejected but is now being accepted.

This change was introduced at

package com.ethlo.time;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class IssueTest {
	@Test
	void shouldNotAllowTrailingZ() {
		Assertions.assertThrows(Exception.class, () -> ITU.parseDateTime("1963-06-19T08:30:06.28123+01:00Z"));
	}
}

Support OSGI

Greetings,
I am currently trying to use JSON-Schema-Validator in our project that uses OSGI. JSON-Schema-Validator is already OSGI-ready, but it has since started using your ITU as a dependency, which prevents the validator from working in OSGI projects.

Would you be so kind to adapt ITU to an OSGI-bundle? The process should be fairly straightforward and would only require you to add a few lines of text to the pom.xml file. These changes wouldn't affect most people, but would help those using OSGI immensly.

You can reference JSON-Schema-Validator's POM.xml. I've highlighted the required lines for the Maven plugin here. Additionally this single line to set the packaging as a 'bundle'. ITU will be fully OSGI compliant with the Maven plugin and the packaging declaration added.

A more detailed guide can be found here.

If you'd like, I can make these changes myself as a PR for you to check out.

Support Java Modules

v1.7.2 defines an automatic module name

$ jarviz module name --gav com.ethlo.time:itu:1.7.0
subject: itu-1.7.0.jar
name: itu
source: filename
automatic: true
valid: true

$ jarviz module descriptor --gav com.ethlo.time:itu:1.7.0
subject: itu-1.7.0.jar
name: itu
version: 1.7.0
open: false
automatic: true
requires:
  java.base mandated
contains:
  com.ethlo.time
  com.ethlo.time.internal

$ jarviz bytecode show --gav com.ethlo.time:itu:1.7.0
subject: itu-1.7.0.jar
Unversioned classes. Bytecode version: 52 (Java 8) total: 18

I'd be great if the library supplied a full Java module descriptor. It's possible to keep bytecode baseline compatible with Java 8 while providing a full module descriptor thanks to ModiTect. This will help modular projects that consume this library, specifically those that create custom Java Runtimes with jlink, as the latter does not support automatic modules but explicit modules. If interested I can send a PR to make it happen.

Improve leap second detection

Currently, the parser will accept any leap second at either end of June or the end of December. Improve this by using a list of known leap seconds dates in the past and keep the current rule for date-times after the last known leap-second year/date. This will avoid breaking the parsing of valid leap second due to not having the very last updated list of leap-seconds.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.