diff --git a/README.md b/README.md index 298829d..2178800 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![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) [![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) [![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)](LICENSE) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/598913bc1fe9405c82be73d9a4f105c8)](https://app.codacy.com/gh/ethlo/itu/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) An extremely fast parser and formatter of ISO format date-times. @@ -107,7 +108,7 @@ class Test { ``` ### Handle different granularity (ISO format) -Validate to different required granularity: +#### Validate with specified granularity ```java import com.ethlo.time.ITU; import com.ethlo.time.TemporalType; @@ -119,7 +120,7 @@ class Test { } ``` -Allowing handling different levels of granularity: +#### Handling different levels of granularity explicitly ```java import com.ethlo.time.ITU; import com.ethlo.time.TemporalHandler; @@ -146,7 +147,20 @@ class Test { }); } } +``` +#### Parsing leniently to a timestamp +In some real world scenarios, the need to parse a best-effort timestamp is needed. To ease this, we can use `ITU.parseLenient()` with `DateTime.toInstant()` like this: +```java +import com.ethlo.time.ITU; +import com.ethlo.time.TemporalHandler; +import java.time.temporal.TemporalAccessor; + +class Test { + void parseTest() { + final Instant instant = ITU.parseLenient("2017-12-06").toInstant(); + } +} ``` ## Q & A diff --git a/src/main/java/com/ethlo/time/DateTime.java b/src/main/java/com/ethlo/time/DateTime.java index 64fbac0..25d3296 100644 --- a/src/main/java/com/ethlo/time/DateTime.java +++ b/src/main/java/com/ethlo/time/DateTime.java @@ -26,6 +26,7 @@ import static com.ethlo.time.internal.EthloITU.finish; import java.time.DateTimeException; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; @@ -38,6 +39,7 @@ import java.util.Objects; import java.util.Optional; +import com.ethlo.time.internal.DateTimeMath; import com.ethlo.time.internal.LimitedCharArrayIntegerUtil; /** @@ -274,11 +276,6 @@ public LocalDateTime toLocalDatetime() public OffsetDateTime toOffsetDatetime() { assertMinGranularity(Field.MINUTE); - return toOffsetDatetimeNoGranularityCheck(); - } - - public OffsetDateTime toOffsetDatetimeNoGranularityCheck() - { if (offset != null) { return OffsetDateTime.of(year, month, day, hour, minute, second, nano, offset.toZoneOffset()); @@ -506,6 +503,34 @@ else if (temporalField.equals(ChronoField.NANO_OF_SECOND)) { return nano; } + else if (temporalField.equals(ChronoField.INSTANT_SECONDS)) + { + if (offset != null) + { + return toEpochSeconds(); + } + } + throw new UnsupportedTemporalTypeException("Unsupported field: " + temporalField); } + + /** + *
This method will attempt to create an Instant from whatever granularity is available in the parsed year/date/date-time.
+ *Missing fields will be replaced by their lowest allowed value: 1 for month and day, 0 for any missing time component.
+ *NOTE: If there is no time-zone defined, UTC will be assumed
+ * + * @return An instant representing the point in time. + */ + public Instant toInstant() + { + return Instant.ofEpochSecond(toEpochSeconds(), nano); + } + + private long toEpochSeconds() + { + final long secsSinceMidnight = hour * 3600L + minute * 60L + second; + final long daysInSeconds = DateTimeMath.daysFromCivil(year, month != 0 ? month : 1, day != 0 ? day : 1) * 86_400; + final long tsOffset = offset != null ? offset.getTotalSeconds() : 0; + return daysInSeconds + secsSinceMidnight - tsOffset; + } } diff --git a/src/main/java/com/ethlo/time/internal/DateTimeMath.java b/src/main/java/com/ethlo/time/internal/DateTimeMath.java new file mode 100644 index 0000000..86c590d --- /dev/null +++ b/src/main/java/com/ethlo/time/internal/DateTimeMath.java @@ -0,0 +1,27 @@ +package com.ethlo.time.internal; + +/** + * CREDIT: Public domain math for converting between epoch and date-time + */ +public class DateTimeMath +{ + public static long daysFromCivil(int y, final int m, final int d) + { + // Returns number of days since civil 1970-01-01. Negative values indicate + // days prior to 1970-01-01. + // Preconditions: y-m-d represents a date in the civil (Gregorian) calendar + // m is in [1, 12] + // d is in [1, last_day_of_month(y, m)] + // y is "approximately" in + // [numeric_limits