How to convert UTC Date String and remove the T and Z in Java?
Asked Answered
P

7

8

Am using Java 1.7.

Trying to convert:

2018-05-23T23:18:31.000Z 

into

2018-05-23 23:18:31

DateUtils class:

public class DateUtils {

    public static String convertToNewFormat(String dateStr) throws ParseException {
        TimeZone utc = TimeZone.getTimeZone("UTC");
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        sdf.setTimeZone(utc);
        Date convertedDate = sdf.parse(dateStr);
        return convertedDate.toString();
    }
}

When trying to use it:

String convertedDate = DateUtils.convertToNewFormat("2018-05-23T23:18:31.000Z");
System.out.println(convertedDate);

Get the following exception:

Exception in thread "main" java.text.ParseException: Unparseable date: "2018-05-23T23:22:16.000Z"
   at java.text.DateFormat.parse(DateFormat.java:366)
   at com.myapp.utils.DateUtils.convertToNewFormat(DateUtils.java:7)

What am I possibly doing wrong?

Is there an easier way to do is (e.g. Apache Commons lib)?

Parsons answered 23/5, 2018 at 23:42 Comment(4)
While you can remove T, Z is the offset and should be parsed as such, or incorrect results are most likely. As an aside consider throwing away the long outmoded and notoriously troublesome SimpleDateFormat and friends, and adding ThreeTen Backport to your project in order to use java.time, the modern Java date and time API. It is so much nicer to work with.Mesic
Coincidentally I just yesterday wrote this answer that shows the correct, modern, good and simple way to parse your string.Mesic
Your input date-time string is in UTC (denoted by the Z suffix). Do you want your output in your local time zone? Or which time zone?Mesic
FYI, the troublesome old date-time classes such as java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat are now legacy, supplanted by the java.time classes built into Java 8 and later. See Tutorial by Oracle.Smoothspoken
C
9

Try this. You have to use one pattern for parsing and another for formatting.

public static String convertToNewFormat(String dateStr) throws ParseException {
    TimeZone utc = TimeZone.getTimeZone("UTC");
    SimpleDateFormat sourceFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    SimpleDateFormat destFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sourceFormat.setTimeZone(utc);
    Date convertedDate = sourceFormat.parse(dateStr);
    return destFormat.format(convertedDate);
}
Cancer answered 24/5, 2018 at 0:2 Comment(1)
Same code in kotlin : val utc = TimeZone.getTimeZone("UTC") val sourceFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") val destFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") sourceFormat.setTimeZone(utc) val convertedDate : Date = sourceFormat.parse(dateStr) val result = destFormat.format(convertedDate)Ferreby
S
13

tl;dr

Instant.parse( "2018-05-23T23:18:31.000Z" )                // Parse this String in standard ISO 8601 format as a `Instant`, a point on the timeline in UTC. The `Z` means UTC.
.atOffset( ZoneOffset.UTC )                                // Change from `Instant` to the more flexible `OffsetDateTime`.
.format(                                                   // Generate a String representing the value of this `OffsetDateTime` object.
    DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss" )   // Specify a formatting pattern as desired.
)                                                          // Returns a `String` object.

2018-05-23 23:18:31

ISO 8601

Your input string is in standard ISO 8601 format.

The java.time classes use these standard formats by default when parsing/generating strings.

The T separates the year-month-day portion from the hour-minute-second. The Z is pronounced Zulu and means UTC.

java.time

You are using troublesome old date-time classes that were supplanted years ago by the java.time classes. The Apache DateUtils is also no longer needed, as you will find its functionality in java.time as well.

Parse that input string as a Instant object. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

String input = "2018-05-23T23:18:31.000Z" ;
Instant instant = Instant.parse( input ) ;

To generate a string in another format, we need a more flexible object. The Instant class is meant to be a basic building block. Lets convert it to a OffsetDateTime`, using UTC itself as the specified offset-from-UTC.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 

Define a formatting pattern to match your desired output.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss" ) ;
String output = odt.format( f ) ;

Tip: Consider using DateTimeFormatter::ofLocalized… methods to automatically localize the String generation per some Locale rather than hard-coding a formatting pattern.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Smoothspoken answered 24/5, 2018 at 5:18 Comment(0)
C
9

Try this. You have to use one pattern for parsing and another for formatting.

public static String convertToNewFormat(String dateStr) throws ParseException {
    TimeZone utc = TimeZone.getTimeZone("UTC");
    SimpleDateFormat sourceFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    SimpleDateFormat destFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sourceFormat.setTimeZone(utc);
    Date convertedDate = sourceFormat.parse(dateStr);
    return destFormat.format(convertedDate);
}
Cancer answered 24/5, 2018 at 0:2 Comment(1)
Same code in kotlin : val utc = TimeZone.getTimeZone("UTC") val sourceFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") val destFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") sourceFormat.setTimeZone(utc) val convertedDate : Date = sourceFormat.parse(dateStr) val result = destFormat.format(convertedDate)Ferreby
T
5

For others without Java 1.7 Restrictions:

Since Java 1.8 you can do it using LocalDateTime and ZonedDateTime from the package java.time

public static void main(String[] args) {
    String sourceDateTime           = "2018-05-23T23:18:31.000Z";
    DateTimeFormatter sourceFormat  = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    DateTimeFormatter targetFormat  = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    LocalDateTime dateTime          = LocalDateTime.parse(sourceDateTime, sourceFormat);
    String formatedDateTime         = dateTime.atZone(ZoneId.of("UTC")).format(targetFormat);
    System.out.println(formatedDateTime);
}

EDIT: (see Comments)

Quote from the Oracle Java documentation of LocalDateTime:

LocalDateTime is an immutable date-time object that represents a date-time, often viewed as year-month-day-hour-minute-second. Other date and time fields, such as day-of-year, day-of-week and week-of-year, can also be accessed. Time is represented to nanosecond precision. For example, the value "2nd October 2007 at 13:45.30.123456789" can be stored in a LocalDateTime.

This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.

the OP is asking to JUST parsing an Input String to a date-time (as year-month-day-hour-minute-second) and the Documentation says

LocalDateTime ... represents a date-time, often viewed as year-month-day-hour-minute-second

so no important information are lost here. And the part dateTime.atZone(ZoneId.of("UTC")) returns a ZonedDateTime so the ZimeZone is handled at this point again if the user needs to work with the timezone ...etc.

so don't try to force users to use the "One and Only" solution you present in your answer.

Tressatressia answered 24/5, 2018 at 0:6 Comment(3)
Using LocalDateTime means you are throwing away important information, with nothing gained.Smoothspoken
This will work nicely in Java 1.7 too when you add ThreeTen Backport to your project. You don’t need a formatter for the input. Instant.parse and the one-arg OffsetDateTime.parse will parse the source string without any explicit formatter.Mesic
@BasilBourque See my Edit!. @ OleV.V: if a user ask such simple Question, so its better to give him/her the straight and informative Answer, so he/she can understand the functionality. After that he/she could compare other Classes/methods available out there. You said "you don't need a formatter for the input" this is only true if the input is always with this "standard format", otherwise you need a formatter, so i mentioned it in my Answer. Thank youTressatressia
E
1

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API.

Also, quoted below is a notice from the Home Page of Joda-Time:

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

Solution using java.time, the modern Date-Time API:

ZonedDateTime.parse("2018-05-23T23:18:31.000Z")
     .format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ENGLISH));

ONLINE DEMO

Note that you do not need a custom DateTimeFormatter to parse the date-time string, 2018-05-23T23:18:31.000Z as it is already in the default pattern used by ZonedDateTime. The modern date-time API is based on ISO 8601.

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

Some helpful answers using java.time API:

  1. 'Z' is not the same as Z.
  2. Never use SimpleDateFormat or DateTimeFormatter without a Locale.
  3. I prefer u to y with a DateTimeFormatter.

For the sake of completeness

For the sake of completeness, given below is a solution using the legacy date-time API:

DateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH);
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
Date dateTime = parser.parse("2018-05-23T23:18:31.000Z");

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
formatter.setTimeZone(parser.getTimeZone());
String formattedDateTimeString = formatter.format(dateTime);
System.out.println(formattedDateTimeString);

ONLINE DEMO

Ezekielezell answered 15/10, 2022 at 22:5 Comment(0)
I
0

YYYY does not match with year part. In java 7 you need yyyy.

For T, use 'T' to match it

You're also missing the faction of millsecond part: .SSS

Try this:

String dateStr="2018-05-23T23:18:31.000Z";
TimeZone utc = TimeZone.getTimeZone("UTC");
SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss.SSS'Z'");
sdf.setTimeZone(utc);
Date convertedDate = sdf.parse(dateStr);
convertedDate.toString();
Isidoro answered 23/5, 2018 at 23:51 Comment(4)
Thank you Manh, but this prints out as May 23 11:18:31 PDT 2018 - I just want it to be 2018-05-23 23:18:31Parsons
You must provide a format to it. SimpleDateFormat sdf2 = new SimpleDateFormat('yyyy-MM-dd HH:mm:ss'); return sdf2.format(convertedDate);Beef
Tried that before responding to you earlier and it still gave a ParseException.Parsons
It works as intended. Which part did you stuck at? I have a typo at above SimpleDateFormat, it needs 4 y instead of yyyBeef
A
0

In Kotlin and using ThreeTenABP,

fun getIsoString(year: Int, month: Int, day: Int): String {
    val localTime = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, ZoneId.of("Z"))

    val utcTime = localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC)

    val isoString = utcTime.toInstant().toString() // 1940-15-12T00:00:00Z

    val formattedIsoString = val formattedIsoString =
        Instant.parse(isoString)
            .atOffset(ZoneOffset.UTC)
            .format(DateTimeFormatter
                .ofPattern("uuuu-MM-dd'T'HH:mm:ss")) // 'T' in quotes so that it is retained.

    return formattedIsoString
}

// print it
print(getIsoString(1940, 15, 12)) // 1940-15-12T00:00:00
Army answered 3/12, 2019 at 3:19 Comment(0)
T
0

You can try this below the idea. I am not an expert in JAVA but I did it in javascript/node.js

import * as momentTimeZone from 'moment-timezone';

let d = new Data(); // d = '2018-05-23T23:18:31.000Z'; or we can take this date
let finalOutput = momentTimeZone(d).tz(this.locationService.locationTimeZone).utc().format('YYYY-MM-DD HH:mm:ss');
console.log('Result: ', finalOutput); // Result: "2018-05-23 23:18:31";

It also works with moment.js. Here is more about format.

Tartar answered 15/9, 2020 at 10:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.