Search code examples
javadatetimeiso8601java.time.instant

parsing date from "06/25/2021 10:26:33.0" format to "2021-06-25T10:26:33.000-04:00" using java.time


I have to convert a date for the purpose of comparison using junit. I get a date from DB which is "06/25/2021 10:26:33.0" and I have to convert it to "2021-06-25T10:26:33.000-04:00" before I use it in the asserts.

I am trying not to use SimpleDate in java and use the the inbuilt java.time instead. However, I don't think I really understand everything in it. Here is the code snippet I have been playing around with. I have tried many things with this and I always get an error when the parse happens.

 public String test() throws ParseException {
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S");
    LocalDateTime ldt = LocalDateTime.parse("06/25/2021 10:26:33.0", dtf);
    //After the above line I have a date like: 2021-06-25T10:26:33.0
    ZoneId zone = ZoneId.of("UTC-04:00");
    ZonedDateTime zdt = ldt.atZone(zone);
    Instant instant = zdt.toInstant();
    return instant.toString();
}

In my mind, I think I have to first convert the date to an "acceptable" format because I feel like this format of the string - "yyyy-MM-dd HH:mm:ss.S" is not something that java.time can handle. it gives me an error such as "Text could not be parsed at index 19" and then probably in the second pass convert it to into this "2021-06-25T10:26:33.000-04:00".

I have consulted several articles regarding this on SO but haven't been able to find something that helps in converting custom formats. I am aware that "parse" and "format" are 2 API methods that have to be leveraged here but not sure how to go about it. Could someone pls nudge me in the right direction?


Solution

  • You can use LocalDateTime#atOffset to meet this requirement.

    import java.time.LocalDateTime;
    import java.time.OffsetDateTime;
    import java.time.ZoneOffset;
    import java.time.format.DateTimeFormatter;
    import java.util.Locale;
    
    public class Main {
        public static void main(String[] args) {
            String input = "06/25/2021 10:26:33.0";
            DateTimeFormatter dtfInput = DateTimeFormatter.ofPattern("M/d/u H:m:s.S", Locale.ENGLISH);
            LocalDateTime ldt = LocalDateTime.parse(input, dtfInput);
            OffsetDateTime odt = ldt.atOffset(ZoneOffset.of("-04:00"));
            System.out.println(odt);
    
            // Formatted output
            DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
            String formatted = dtfOutput.format(odt);
            System.out.println(formatted);
        }
    }
    

    Output:

    2021-06-25T10:26:33-04:00
    2021-06-25T10:26:33.000-04:00
    

    ONLINE DEMO

    Notes:

    1. If the fraction-of-second can be of zero to nine digits in the input, use the pattern, M/d/u H:m:s[.[SSSSSSSSS][SSSSSSSS][SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]] where optional patterns have been specified using the square bracket.
    2. OffsetDateTime#toString omits the second and the fraction-of-second part if they are zero. Use a DateTimeFormatter to get them in the formatted string.

    Learn more about the modern Date-Time API* from Trail: Date Time.


    * For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.