Parsing a timestamp with timezone

2

I was making a small code to implement a treatment case in a friend application module and got stuck with a problem.

This is a unit test. To put in the application module I would have to make some adjustments when inserting into the native code of it, but basically I'm getting a String from a database in the format similar to the String given below:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
  public static void main(String args[]) throws ParseException {
    String value = "2018-4-25 0.0.0.0 -3:00";
    SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy HH:mm:ss");

    if (value.charAt(6) == '-') {
        SimpleDateFormat myformat = new SimpleDateFormat("yyyy-M-dd H.m.s.S X");
        Date d1 = myformat.parse(value);
        String result = sdf.format(d1);
        result = result + ", " + "000000000 " + "-03:00";
        System.out.println(result);

    } else {
        SimpleDateFormat myformat = new SimpleDateFormat("yyyy-MM-dd H.m.s.S X");
        Date d1 = myformat.parse(value);
        String result = sdf.format(d1);
        result = result + ", " + "000000000 " + "-03:00";
        System.out.println(result);
      }
  }
}

But the message I get is:

"C:\Program Files\Java\jdk-10.0.1\bin\java.exe" "-javaagent:C:\Program Files (x86)\IntelliJ IDEA\lib\idea_rt.jar=63052:C:\Program Files (x86)\IntelliJ IDEA\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\...\IdeaProjects\untitled\out\production\untitled Main

Exception in thread "main" java.text.ParseException: Unparseable date: "2018-4-25 0.0.0.0 -3:00"
    at java.base/java.text.DateFormat.parse(DateFormat.java:395)
    at Main.main(Main.java:23)

Process finished with exit code 1

Could anyone help me?

    
asked by anonymous 01.05.2018 / 02:36

2 answers

3

Doubling of the format X :

Letter  Component     Presentation         Examples
X       Time zone     ISO 8601 time zone   -08; -0800; -08:00

and more

ISO8601TimeZone:
         OneLetterISO8601TimeZone
         TwoLetterISO8601TimeZone
         ThreeLetterISO8601TimeZone
 OneLetterISO8601TimeZone:
         Sign TwoDigitHours
          Z
 TwoLetterISO8601TimeZone:
         Sign TwoDigitHours Minutes
          Z
 ThreeLetterISO8601TimeZone:
         Sign TwoDigitHours  : Minutes
          Z

That is, the format for the time zone, using the format X , requires two digits for the time. If you try to interpret the text "2018-4-25 0.0.0.0 -03:00" it should work. Another option: add "GMT" before spindle - "2018-4-25 0.0.0.0 GMT-3:00" - and use the z or Z format.

Your code is also a bit strange - there is no need to test whether the seventh letter (index 6) is - . For example, for interpretation with y format, the SimpleDateFormat class only differentiates between the abbreviated format ( "y" or "yy" ) and complete with more than two reps of y . Similar to the interpretation of the month ...

This response was based on official documentation

    
02.05.2018 / 17:17
1

According to Carlos's answer, SimpleDateFormat only supports offsets with 2 digits in the hour (something like -03:00 ).

One way around this problem is to break the String into 2 parts: one with the date and time, and another with the offset , and use this value to set the timezone of SimpleDateFormat :

String value = "2018-4-25 0.0.0.0 -3:00";
// separar data e hora (2018-4-25 0.0.0.0) do offset (-3:00)
Matcher matcher = Pattern.compile("^(.*) ([\+\-]\d{1,2}:\d\d)$").matcher(value);
if (matcher.matches()) {
    String datetime = matcher.group(1);
    String offset = matcher.group(2);

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd H.m.s.S");
    // felizmente, a classe TimeZone aceita offsets com um dígito na hora
    sdf.setTimeZone(TimeZone.getTimeZone("GMT" + offset));

    Date date = sdf.parse(datetime);
}

Depending on the version of Java you use, you can also use the java.time (from Java 8) API. You use this API to parse and then convert to java.util.Date .

In Java 6 and 7, you can also use these classes through ThreeTen Backport - the classes are almost the same, the only differences are that they are in the package org.threeten.bp (instead of java.time ) and the conversion to Date is slightly different from Java 8 (I will explain below).

In any case, Java 8 also has this problem of not accepting offsets with a digit in the hours. This has only been fixed in Java 9 .

The code in Java 8, using java.time (or Java 6 and 7 using ThreeTen Backport) is below. Note that the date / time and offset are treated separately, and then merged to create a OffsetDateTime , which is then converted to Date :

String value = "2018-4-25 0.0.0.0 -3:00";
Matcher matcher = Pattern.compile("^(.*) ([\+\-]\d{1,2}:\d\d)$").matcher(value);
if (matcher.matches()) {
    String datetime = matcher.group(1);
    String offset = matcher.group(2);
    DateTimeFormatter datetimeParser = DateTimeFormatter.ofPattern("uuuu-M-dd H.m.s.S");
    DateTimeFormatter offsetParser = DateTimeFormatter.ofPattern("O");

    LocalDateTime dt = LocalDateTime.parse(datetime, datetimeParser);
    ZoneOffset zOffset = ZoneOffset.from(offsetParser.parse("GMT" + offset));
    OffsetDateTime odt = dt.atOffset(zOffset);

    // converte para java.util.Date
    // Java 8
    Date date = Date.from(odt.toInstant());

    // Java 6 e 7 (ThreeTen Backport)
    Date date = DateTimeUtils.toDate(odt.toInstant());
}

From Java 9, you can parse at once:

String value = "2018-4-25 0.0.0.0 -3:00";
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // data e hora
    .appendPattern("uuuu-M-dd H.m.s.S ")
    // offset com um dígito nas horas
    .appendOffset("+H:MM", "+0:00")
    // criar formatter
    .toFormatter();
OffsetDateTime odt = OffsetDateTime.parse(value, fmt);

Date date = Date.from(odt.toInstant());
    
03.05.2018 / 20:16