PacificNW_Lover
PacificNW_Lover

Reputation: 5384

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

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)?

Upvotes: 8

Views: 57358

Answers (7)

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79550

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

Upvotes: 1

Ajay Gupta
Ajay Gupta

Reputation: 2957

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.

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 340060

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?

Upvotes: 13

Jose
Jose

Reputation: 2769

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

Upvotes: 0

Rami.Q
Rami.Q

Reputation: 2476

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.

Upvotes: 5

J K
J K

Reputation: 944

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);
}

Upvotes: 9

Mạnh Quyết Nguyễn
Mạnh Quyết Nguyễn

Reputation: 18235

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();

Upvotes: 0

Related Questions