Skip to content

Commit

Permalink
Merge pull request #5 from ethlo/issue-4-leap-second-parsing
Browse files Browse the repository at this point in the history
Do not fall over on parsing leap-second date-time
  • Loading branch information
ethlo committed Aug 10, 2019
2 parents b8d4411 + 7276929 commit cb68490
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -81,10 +81,17 @@ Typical formats include:

## 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, it is not obvious how to support this.

ITU will parse the date-time correctly, however 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`.

15 changes: 15 additions & 0 deletions src/main/java/com/ethlo/time/FastInternetDateTimeUtil.java
Expand Up @@ -23,6 +23,7 @@
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.Year;
import java.time.YearMonth;
Expand All @@ -33,6 +34,7 @@

public class FastInternetDateTimeUtil extends AbstractRfc3339 implements W3cDateTimeUtil
{
public static final int LEAP_SECOND_SECONDS = 60;
private final StdJdkInternetDateTimeUtil delegate = new StdJdkInternetDateTimeUtil();

private static final char PLUS = '+';
Expand Down Expand Up @@ -402,6 +404,19 @@ else if (remaining == 0)
throw new DateTimeException("Unexpected character at position 19:" + chars[19]);
}

if (second == LEAP_SECOND_SECONDS)
{
// Do not fall over trying to parse leap seconds
final int utcHour = hour - (offset.getTotalSeconds() / 3_600);
final int utcMinute = minute - ((offset.getTotalSeconds() % 3_600) / 60);
if (((month == Month.DECEMBER.getValue() && day == 31) || (month == Month.JUNE.getValue() && day == 30))
&& utcHour == 23
&& utcMinute == 59)
{
// Consider it a leap second
return OffsetDateTime.of(year, month, day, hour, minute, 59, fractions, offset).plusSeconds(1);
}
}
return OffsetDateTime.of(year, month, day, hour, minute, second, fractions, offset);
}

Expand Down
34 changes: 33 additions & 1 deletion src/test/java/com/ethlo/time/CorrectnessTest.java
Expand Up @@ -51,7 +51,39 @@ public abstract class CorrectnessTest extends AbstractTest<Rfc3339>
"2017-02-21T15:27:39.123456", "2017-02-21T15:27:39.123456789",
"2017-02-21T15:27:39+0000", "2017-02-21T15:27:39.123+0000",
"201702-21T15:27:39.123456+0000", "20170221T15:27:39.123456789+0000"};


@Test
public void testParseLeapSecondUTC()
{
final String leapSecondUTC = "1990-12-31T23:59:60Z";
final OffsetDateTime result = instance.parseDateTime(leapSecondUTC);
assertThat(instance.formatUtc(result)).isEqualTo("1991-01-01T00:00:00Z");
}

@Test
public void testParseLeapSecondPST()
{
final String leapSecondPST = "1990-12-31T15:59:60-08:00";
final OffsetDateTime result = instance.parseDateTime(leapSecondPST);
assertThat(instance.formatUtc(result)).isEqualTo("1991-01-01T00:00:00Z");
}

@Test
public void testParseLeapSecondUTCJune()
{
final String leapSecondUTC = "1992-06-30T23:59:60Z";
final OffsetDateTime result = instance.parseDateTime(leapSecondUTC);
assertThat(instance.formatUtc(result)).isEqualTo("1992-07-01T00:00:00Z");
}

@Test
public void testParseLeapSecondPSTJune()
{
final String leapSecondPST = "1992-06-30T15:59:60-08:00";
final OffsetDateTime result = instance.parseDateTime(leapSecondPST);
assertThat(instance.formatUtc(result)).isEqualTo("1992-07-01T00:00:00Z");
}

@Test(expected=DateTimeException.class)
public void testFormat1()
{
Expand Down
36 changes: 36 additions & 0 deletions src/test/java/com/ethlo/time/StdJdkCorrectnessTest.java
Expand Up @@ -20,7 +20,10 @@
* #L%
*/

import static org.fest.assertions.api.Assertions.assertThat;

import java.time.DateTimeException;
import java.time.OffsetDateTime;

import org.junit.Ignore;
import org.junit.Test;
Expand Down Expand Up @@ -48,4 +51,37 @@ public void testParseUnknownLocalOffsetConvention()
{
// For ignore marker only
}


@Override
@Test
@Ignore
public void testParseLeapSecondUTC()
{
// For ignore marker only
}

@Override
@Test
@Ignore
public void testParseLeapSecondPST()
{
// For ignore marker only
}

@Override
@Test
@Ignore
public void testParseLeapSecondUTCJune()
{
// For ignore marker only
}

@Override
@Test
@Ignore
public void testParseLeapSecondPSTJune()
{
// For ignore marker only
}
}

0 comments on commit cb68490

Please sign in to comment.