{"id":15492275,"url":"https://github.com/ethlo/itu","last_synced_at":"2025-05-16T11:04:37.781Z","repository":{"id":17974494,"uuid":"83111285","full_name":"ethlo/itu","owner":"ethlo","description":"An extremely fast parser and formatter of standardized date and date-times supporting RFC-3339 (ISO-8601 profile) and more.","archived":false,"fork":false,"pushed_at":"2025-02-21T16:45:41.000Z","size":1178,"stargazers_count":31,"open_issues_count":0,"forks_count":10,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-12T08:16:30.563Z","etag":null,"topics":["date","dateparser","datetime","formatter","iso-8601","java","library","no-dependencies","optimized","parser","performance","rfc3339","time"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ethlo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-25T06:21:40.000Z","updated_at":"2025-03-03T17:54:36.000Z","dependencies_parsed_at":"2024-01-05T09:39:22.022Z","dependency_job_id":"6386e623-af76-4f5a-92a0-5d43ecd377b1","html_url":"https://github.com/ethlo/itu","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethlo%2Fitu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethlo%2Fitu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethlo%2Fitu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethlo%2Fitu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ethlo","download_url":"https://codeload.github.com/ethlo/itu/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254518384,"owners_count":22084374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["date","dateparser","datetime","formatter","iso-8601","java","library","no-dependencies","optimized","parser","performance","rfc3339","time"],"created_at":"2024-10-02T07:59:56.000Z","updated_at":"2025-05-16T11:04:32.771Z","avatar_url":"https://github.com/ethlo.png","language":"Java","readme":"# Internet Time Utility\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.ethlo.time/itu.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.ethlo.time%22%20a%3A%22itu%22)\n[![javadoc](https://javadoc.io/badge2/com.ethlo.time/itu/javadoc.svg)](https://javadoc.io/doc/com.ethlo.time/itu/latest/com/ethlo/time/ITU.html)\n[![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)](../../LICENSE)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/598913bc1fe9405c82be73d9a4f105c8)](https://app.codacy.com/gh/ethlo/itu/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![codecov](https://codecov.io/gh/ethlo/itu/graph/badge.svg?token=V3H15LKC5V)](https://codecov.io/gh/ethlo/itu)\n\nAn extremely fast parser and formatter of ISO-8601 date-times. Handle\n[RFC-3339 Timestamps](https://www.ietf.org/rfc/rfc3339.txt) and W3C [Date and Time Formats](https://www.w3.org/TR/NOTE-datetime) with ease!\nNow also supports a subset of duration strings!\n\n## Features\n\n Low ceremony, high productivity with a very easy to use API.\n* [Well-documented](https://javadoc.io/doc/com.ethlo.time/itu/latest/com/ethlo/time/ITU.html).\n* Aim for 100% specification compliance.\n* Handling leap-seconds.\n* Zero dependencies.\n* Java 8 compatible.\n* Apache 2 licensed.\n\n## Performance\n\nTypically, **10x to 30x faster** than parsing and formatting with Java JDK classes.\n\nThe details and tests are available in a separate repository, [date-time-wars](https://github.com/ethlo/date-time-wars).\n\n## Usage\n\nAdd dependency\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.ethlo.time\u003c/groupId\u003e\n    \u003cartifactId\u003eitu\u003c/artifactId\u003e\n    \u003cversion\u003e1.14.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nBelow you find some samples of usage of this library. Please check out the [javadoc](https://javadoc.io/doc/com.ethlo.time/itu/latest/com/ethlo/time/ITU.html) for more details.\n\n\n\n## Parsing\n\nThis is a collection of usage examples for parsing.\n\n\n\n#### parseRfc3339\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L60C5-L69C6)\u003c/smaller\u003e\n\nThe simplest and fastest way to parse an RFC-3339 timestamp by far!\n```java\nfinal String text = \"2012-12-27T19:07:22.123456789-03:00\";\nfinal OffsetDateTime dateTime = ITU.parseDateTime(text);\nassertThat(dateTime.toString()).isEqualTo(text);\n```\n\n#### parseLenient\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L71C5-L83C6)\u003c/smaller\u003e\n\nParses a date-time with flexible granularity. Works for anything from a year to a timestamp with nanoseconds, with or without timezone offset.\n```java\nfinal String text = \"2012-12-27T19:07:23.123\";\nfinal DateTime dateTime = ITU.parseLenient(text);\nfinal String formatted = dateTime.toString();\nassertThat(formatted).isEqualTo(text);\n```\n\n#### parseLenientWithCustomSeparators\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L85C5-L97C6)\u003c/smaller\u003e\n\nIn case you encounter the need for a somewhat different time-separator or fraction separator\n you can use the `ParseConfig` to set up you preferred delimiters.\n```java\nfinal ParseConfig config = ParseConfig.DEFAULT\n                .withDateTimeSeparators('T', '|')\n                .withFractionSeparators('.', ',');\nfinal DateTime result = ITU.parseLenient(\"1999-11-22|11:22:17,191\", config);\nassertThat(result.toString()).isEqualTo(\"1999-11-22T11:22:17.191\");\n```\n\n#### parsePosition\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L99C5-L109C6)\u003c/smaller\u003e\n\nThis allows you to track where to start reading. Note that the check for trailing junk is disabled when using `ParsePosition`.\n```java\nfinal ParsePosition pos = new ParsePosition(10);\nfinal OffsetDateTime result = ITU.parseDateTime(\"some-data,1999-11-22T11:22:19+05:30,some-other-data\", pos);\nassertThat(result.toString()).isEqualTo(\"1999-11-22T11:22:19+05:30\");\nassertThat(pos.getIndex()).isEqualTo(35);\n```\n\n#### explicitGranularity\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L111C5-L134C6)\u003c/smaller\u003e\n\nThis is useful if you need to handle different granularity with different logic or interpolation.\n```java\nfinal TemporalHandler\u003cOffsetDateTime\u003e handler = new TemporalHandler\u003cOffsetDateTime\u003e()\n        {\n            @Override\n            public OffsetDateTime handle(final LocalDate localDate)\n            {\n                return localDate.atTime(OffsetTime.of(LocalTime.of(0, 0), ZoneOffset.UTC));\n            }\n\n            @Override\n            public OffsetDateTime handle(final OffsetDateTime offsetDateTime)\n            {\n                return offsetDateTime;\n            }\n        };\nfinal OffsetDateTime result = ITU.parse(\"2017-12-06\", handler);\nassertThat(result.toString()).isEqualTo(\"2017-12-06T00:00Z\");\n```\n\n#### lenientTimestamp\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L136C5-L146C6)\u003c/smaller\u003e\n\nIn 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`.\n\n Note the limitations and the assumption of UTC time-zone, as mentioned in the javadoc.\n```java\nfinal Instant instant = ITU.parseLenient(\"2017-12-06\").toInstant();\nassertThat(instant.toString()).isEqualTo(\"2017-12-06T00:00:00Z\");\n```\n\n#### parseCustomFormat\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L148C5-L170C6)\u003c/smaller\u003e\n\nIn case the format is not supported directly, you can build your own parser.\n```java\nfinal DateTimeParser parser = DateTimeParsers.of(\n                digits(DAY, 2),\n                separators('-'),\n                digits(MONTH, 2),\n                separators('-'),\n                digits(YEAR, 4),\n                separators(' '),\n                digits(HOUR, 2),\n                digits(MINUTE, 2),\n                digits(SECOND, 2),\n                separators(','),\n                fractions()\n        );\nfinal String text = \"31-12-2000 235937,123456\";\nfinal DateTime result = parser.parse(text);\nassertThat(result.toString()).isEqualTo(\"2000-12-31T23:59:37.123456\");\n```\n\n#### parseUsingInterfaceRfc33939\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L172C5-L182C6)\u003c/smaller\u003e\n\n`DateTimerParser` interface for RFC-3339.\n```java\nfinal DateTimeParser parser = DateTimeParsers.rfc3339();\nfinal String text = \"2000-12-31 23:59:37.123456\";\nfinal DateTime result = parser.parse(text);\nassertThat(result.toString()).isEqualTo(\"2000-12-31T23:59:37.123456\");\n```\n\n#### parseUsingInterfaceLocalTime\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L184C5-L194C6)\u003c/smaller\u003e\n\n`DateTimerParser` interface for local time.\n```java\nfinal DateTimeParser parser = DateTimeParsers.localTime();\nfinal String text = \"23:59:37.123456\";\nfinal LocalTime result = parser.parse(text).toLocalTime();\nassertThat(result.toString()).isEqualTo(text);\n```\n\n#### parseUsingInterfaceLocalDate\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/parsing/ITUParserSamples.java#L196C5-L206C6)\u003c/smaller\u003e\n\n`DateTimerParser` interface for local date.\n```java\nfinal DateTimeParser parser = DateTimeParsers.localDate();\nfinal String text = \"2013-12-24\";\nfinal LocalDate result = parser.parse(text).toLocalDate();\nassertThat(result.toString()).isEqualTo(text);\n```\n\n\n\n\n## Formatting\n\nThis is a collection of usage examples for formatting.\n\n\n\n#### formatRfc3339WithUTC\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/formatting/ITUFormattingSamples.java#L43C5-L54C6)\u003c/smaller\u003e\n\nThe simplest and fastest way to format an RFC-3339 timestamp by far!\n```java\nfinal OffsetDateTime input = OffsetDateTime.of(2012, 12, 27, 19, 7, 22, 123456789, ZoneOffset.ofHoursMinutes(-3, 0));\nassertThat(ITU.formatUtcNano(input)).isEqualTo(\"2012-12-27T22:07:22.123456789Z\");\nassertThat(ITU.formatUtcMicro(input)).isEqualTo(\"2012-12-27T22:07:22.123456Z\");\nassertThat(ITU.formatUtcMilli(input)).isEqualTo(\"2012-12-27T22:07:22.123Z\");\nassertThat(ITU.formatUtc(input)).isEqualTo(\"2012-12-27T22:07:22Z\");\n```\n\n#### formatWithDateTime\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/formatting/ITUFormattingSamples.java#L56C5-L65C6)\u003c/smaller\u003e\n\nFormat with `DateTime`.\n```java\nfinal DateTime input = DateTime.of(2020, 11, 27, 12, 39, 19, null);\nassertThat(input.toString(Field.MINUTE)).isEqualTo(\"2020-11-27T12:39\");\nassertThat(input.toString(Field.SECOND)).isEqualTo(\"2020-11-27T12:39:19\");\n```\n\n\n\n\n## Leap-second handling\n\n\n\n#### parseLeapSecond\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/leapsecond/ITULeapSecondSamples.java#L40C5-L57C6)\u003c/smaller\u003e\n\nParse 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).\n```java\ntry\n        {\n            ITU.parseDateTime(\"1990-12-31T15:59:60-08:00\");\n        }\n        catch (LeapSecondException exc)\n        {\n            // The following helper methods are available let you decide how to progress\n            assertThat(exc.getSecondsInMinute()).isEqualTo(60);\n            assertThat(exc.getNearestDateTime()).isEqualTo(OffsetDateTime.of(1990, 12, 31, 16, 0, 0, 0, ZoneOffset.ofHours(-8)));\n            assertThat(exc.isVerifiedValidLeapYearMonth()).isTrue();\n        }\n```\n\n\n## Duration Parser\n\nParses a duration string, a strict subset of ISO 8601 durations.\n\n### Supported Units\nThis method supports time-based durations with the following units:\n\n- **Weeks** (`W`)\n- **Days** (`D`)\n- **Hours** (`H`)\n- **Minutes** (`M`)\n- **Seconds** (`S`), including fractional seconds up to nanosecond precision\n\n#### Not Allowed Units\nThe following units are **explicitly not allowed** to avoid ambiguity:\n\n- **Years** (`Y`)\n- **Months** (`M` in the date section)\n\n### Negative Durations\nNegative durations are supported and must be prefixed with `-P`, as specified in ISO 8601.  \nThe parsed duration will be represented using:\n\n- A **`long`** for total seconds\n- An **`int`** for nanosecond precision\n\nThe nanosecond component is always positive, with the sign absorbed by the seconds field,  \nfollowing Java and ISO 8601 conventions.\n\n### Examples\n\n#### Valid Input\n- `P2DT3H4M5.678901234S` → 2 days, 3 hours, 4 minutes, 5.678901234 seconds\n- `PT5M30S` → 5 minutes, 30 seconds\n- `-PT2.5S` → Negative 2.5 seconds\n- `-P1D` → Negative 1 day\n\n#### Invalid Input\n- `P1Y2M3DT4H` → Contains `Y` and `M`\n- `PT` → Missing time values after `T`\n- `P-1D` → Incorrect negative placement\n\n\n\n#### simple\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/durationparsing/DurationParsingSamples.java#L32C5-L37C6)\u003c/smaller\u003e\n\n\n```java\nfinal Duration duration = ITU.parseDuration(\"P4W\");\nassertThat(duration.getSeconds()).isEqualTo(2_419_200L);\n```\n\n#### fullNotNormalizedToNormalized\n\u003csmaller style=\"float:right;\"\u003e[source \u0026raquo;](src/test/java/samples/durationparsing/DurationParsingSamples.java#L39C5-L44C6)\u003c/smaller\u003e\n\n\n```java\nfinal Duration duration = ITU.parseDuration(\"P4W10DT28H122M1.123456S\");\nassertThat(duration.normalized()).isEqualTo(\"P5W4DT6H2M1.123456S\");\n```\n\n\n## Q \u0026 A\n\n### Why this little project?\n\nThere are an endless amount of APIs with non-standard date/time exchange, and the goal of this project is to make it a\nbreeze to do the right thing!\n\n### Why the performance focus?\n\nSome projects use epoch time-stamps for date-time exchange, and from a performance perspective this *may* make sense\nin some cases. With this project one can do-the-right-thing and maintain performance in date-time handling.\n\nImportantly, 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\ndatabases and search engines, to importing/exporting data on less powerful devices, like cheaper Android devices.\n\n### What is wrong with epoch timestamps?\n\n* It is not human-readable, so debugging and direct manipulation is harder\n* Limited resolution and/or time-range available\n* Unclear resolution and/or time-range\n\n### What is RFC-3339?\n\n[RFC-3339](https://www.ietf.org/rfc/rfc3339.txt) is a subset/profile defined by [W3C](https://www.w3.org/) of the\nformats defined in [ISO-8601](http://www.iso.org/iso/home/standards/iso8601.htm), to simplify date and time exhange in\nmodern Internet protocols.\n\nTypical formats include:\n\n* `2017-12-27T23:45:32Z` - No fractional seconds, UTC/Zulu time\n* `2017-12-27T23:45:32.999Z` - Millisecond fractions, UTC/Zulu time\n* `2017-12-27T23:45:32.999999Z` - Microsecond fractions, UTC/Zulu time\n* `2017-12-27T23:45:32.999999999Z` - Nanosecond fractions, UTC/Zulu time\n* `2017-12-27T18:45:32-05:00` - No fractional seconds, EST time\n* `2017-12-27T18:45:32.999-05:00` - Millisecond fractions, EST time\n* `2017-12-27T18:45:32.999999-05:00` - Microsecond fractions, EST time\n* `2017-12-27T18:45:32.999999999-05:00` - Nanosecond fractions, EST time\n\n### What is W3C - Date and Time Formats\n\n[Date and Time Formats](https://www.w3.org/TR/NOTE-datetime) is a _note_, meaning it is not endorsed, but it still\nserves as a sane subset of ISO-8601, just like RFC-3339.\n\nTypical formats include:\n\n* `2017-12-27T23:45Z` - Minute resolution, UTC/Zulu time\n* `2017-12-27` - Date only, no timezone (like someone's birthday)\n* `2017-12` - Year and month only. Like an expiry date.\n\n## Limitations\n\n### Local offset\n\nFor the sake of avoiding data integrity issues, this library will not allow offset of `-00:00`. Such offset is described\nin RFC3339 section 4.3., named \"Unknown Local Offset Convention\". Such offset is explicitly prohibited in ISO-8601 as\nwell.\n\n\u003e 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\n\u003e that UTC is the preferred reference point for the specified time.\n\n### Leap second parsing\n\nSince Java's `java.time` classes do not support storing leap seconds, ITU will throw a `LeapSecondException` if one is\nencountered to signal that this is a leap second. The exception can then be queried for the second-value. Storing such\nvalues is not possible in a `java.time.OffsetDateTime`, the `60` is therefore abandoned and the date-time will use `59`\ninstead of `60`.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethlo%2Fitu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fethlo%2Fitu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethlo%2Fitu/lists"}