0

In my framework I need to convert to/from Java Dates and ASN1 UTC Times (Generalized Times). I have tried SimpleDateFormat and OffsetDateTime. Is there a clear simple way to get both directions? The bytes in asn1 are character bytes. For instance below, however I need "YYMMDDhhmmssZ" in bytes.

The decode test passes, due to changes from the first answer, is below:

@Test
public void testUTCTimeDecode() throws IOException {
    byte[] bytes = new byte[] {48, 55, 48, 51, 50, 50, 49, 53, 53, 56, 49, 55, 90};
    ByteArrayInputStream derStream = new ByteArrayInputStream(bytes);

    Date testDate = new Date(OffsetDateTime.parse("2007-03-22T15:58:17+00:00").toEpochSecond() * 1000);

    byte[] decodeBytes = new byte[bytes.length];
    derStream.read(decodeBytes);

    OffsetDateTime actual = OffsetDateTime.parse(
            new String(decodeBytes),
            DateTimeFormatter.ofPattern("uuMMddHHmmssX"));

    Date date = Date.from(actual.toInstant());
    assertTrue(date.equals(testDate));
}

I am still having an issue with encoding. Here is the exception thrown and the test method:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year
at java.time.Instant.getLong(Instant.java:603)
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2540)
at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2179)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
at club.callistohouse.asn1.ASN1TestModel.testUTCTimeEncode(ASN1TestModel.java:47)

Here is the test method I am using which is now fixed.

    @Test
public void testUTCTimeEncode() throws IOException {
    byte[] bytes = new byte[] {48, 55, 48, 51, 50, 50, 49, 53, 53, 56, 49, 55, 90};
    ByteArrayOutputStream derStream = new ByteArrayOutputStream();

    Date testDate = new Date(OffsetDateTime.parse("2007-03-22T15:58:17+00:00").toEpochSecond() * 1000);

    Instant instant = Instant.ofEpochMilli(testDate.getTime());
    DateTimeFormatter utcFormatter = DateTimeFormatter
               .ofPattern ( "uuMMddHHmmssX" ) 
               .withLocale( Locale.US )
               .withZone( ZoneId.of("UTC"));
    String formattedDate = utcFormatter.format(instant);
    derStream.write(formattedDate.getBytes());
    assertTrue(Arrays.equals(bytes, derStream.toByteArray()));
}
rabbit
  • 111
  • 9
  • Without knowing your `ASN1InputStream` and `ASN1OutputStream` classes it’s hard to tell. Would it be possible for you to produce a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve)? – Ole V.V. Sep 26 '17 at 12:38
  • Since you can use `OffsetDateTime`, I recommend you go for a solution without the outdated classes `Date`, `SimpleDateFormat` and `TimeZone`. – Ole V.V. Sep 26 '17 at 12:40
  • I fixed a cast issue, though the decoding is still failing for parse format. I also enabled the testGeneralizedTime, in addition to testUTCTime. – rabbit Sep 26 '17 at 17:29
  • Welcome to Stack Overflow. We could probably help you — and would be glad to — if you could provide a minimal, complete and verifiable example including precise expected and observed output. – Ole V.V. Sep 27 '17 at 02:23
  • 1
    I edited the OP, thank you for helping clear me to a better question, hopefully. – rabbit Sep 27 '17 at 03:16
  • It’s much better now. Thank you! – Ole V.V. Sep 27 '17 at 09:04
  • I can run your encoding code without seeing any exception thrown, and the test passes. – Ole V.V. Sep 28 '17 at 00:02
  • I now have all basic ASN1 types passing tests, including byteArray, time, objectId, utf8 and sequences and sets. Working on definitions and mapping. Thank you for your practical guidance. – rabbit Sep 28 '17 at 00:11

1 Answers1

2

TL;DR

    OffsetDateTime expected = OffsetDateTime.of(2007, 3, 22, 15, 58, 17, 0, ZoneOffset.UTC);
    DateTimeFormatter asn1Formatter = DateTimeFormatter.ofPattern("uuMMddHHmmssX");
    OffsetDateTime actual = OffsetDateTime.parse(dateString, asn1Formatter);
    assertEquals(expected, actual);

Avoid the outdated date and time classes

I recommend you avoid the outdated classes Date, SimpleDateFormat and TimeZone. The modern Java date and time API, from which you are already using OFfsetDateTime, is so much nicer to work with. In particular mixing the two APIs, though possible, is bound to create confusing code.

If for instance in the end you need an oldfashioned Date object for a legacy API, only convert from Instant to Date in the last moment to minimize your use of the old API.

The bugs in you format pattern string

There are two bugs in your format pattern string:

  1. You cannot use uppercase YY. This is for week-based year and only useful with week numbers. Use either lowercase yy or uu, and beware that two-digit year will be interpreted as in the range 2000 through 2099.
  2. Format pattern letter Z does not match zone offset Z. Use X in your format pattern instead.
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • I was able to get this to work for decoding, but I still have an issue with encoding. The test method and new exception are edited into the original question. I am getting an exception now: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year – rabbit Sep 27 '17 at 17:06
  • I found the answer here: https://stackoverflow.com/questions/40211892/unsupported-field-year-when-formatting-an-instant-to-date-iso – rabbit Sep 27 '17 at 17:52
  • Is there a way to override the century selection strategy? Simply using the current century is not correct in this context; if the year % 100 falls in some range, it should be the 21st century, otherwise, it should be the 20th. – erickson Sep 27 '17 at 19:04
  • 1
    @erickson For how to control the century selection, see [Meno Hochschild’s answer here](https://stackoverflow.com/a/29496149/5772882). – Ole V.V. Sep 28 '17 at 00:09
  • @OleV.V. That API documentation is a bit complex, but sounds like it has potential. Thank you! – erickson Sep 28 '17 at 00:20