Gibbs
Gibbs

Reputation: 22974

Convert date from YYYY-MM-DD to YYYY-MM-DDTHH:mm:ssZ

I want to convert the following patterns to yyyy-mm-ddThh:mm:ssz.

         2017-feb-02
         2017-05-02
         2017-01-01 03:00:00
         2017-01-01 00:00:00.0

I have gone through the SimpleDateFormat class of Java. But I haven’t been able to achieve it.

Upvotes: -1

Views: 6312

Answers (4)

Sara Beridze
Sara Beridze

Reputation: 41

java.time

As Basil Bourque and Anonymous say, use java.time, the modern Java date and time API, for your date and time work. You mentioned SimpleDateFormat. Avoid it. It was a notorious troublemaker of a class and was obsoleted by java.time in Java 8 in 2014.

Don’t convert from one string format to another

In the simplest of run-once-and-throw-away programs it’s probably OK to convert a date and time in a string to a string in a different format. For all other purposes store your dates and times in proper date-time objects, for example a ZonedDateTime object for a date and time in a known time zone. When accepting string input, parse into such an object. Only when giving string output, format the date and time into a string again.

Parsing

For parsing your three or four different formats you may use the following. We may regard the last two of your example strings as having the same format, only without and with a decimal fraction on the seconds.

/** Formatter for parsing a string like "2017-feb-02" */
private static final DateTimeFormatter dateWithMonthAbbreviationParser
        = new DateTimeFormatterBuilder()
                .parseCaseInsensitive() // Allow "feb" in lower case.
                .appendPattern("uuuu-MMM-dd")
                // Before Java 19 use Locale.forLanguageTag("en-IN")
                .toFormatter(Locale.of("en", "IN"));

/** Formatter for parsing a string like "2017-01-01 03:00:00" or "2017-01-01 00:00:00.0" */
private static final DateTimeFormatter dateTimeParser
        = new DateTimeFormatterBuilder()
                // Reuse existing formatters for date and time.
                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                .appendLiteral(' ')
                .append(DateTimeFormatter.ISO_LOCAL_TIME)
                .toFormatter(Locale.ROOT);

private static ZonedDateTime parseDifferentFormats(String dateTimeString) {
    if (dateTimeString.length() == 10) {
        // ISO 8601 format; no explicit formatter needed.
        return LocalDate.parse(dateTimeString)
                .atStartOfDay(ZoneId.systemDefault());
    }
    if (dateTimeString.length() < 15) {
        return LocalDate.parse(dateTimeString, dateWithMonthAbbreviationParser)
                .atStartOfDay(ZoneId.systemDefault());
    }
    return LocalDateTime.parse(dateTimeString, dateTimeParser)
            .atZone(ZoneId.systemDefault());
}

Try it out:

    List<String> inputExamples = List.of(
            "2017-feb-02", "2017-05-02",
            "2017-01-01 03:00:00", "2017-01-01 00:00:00.0");
    for (String input : inputExamples) {
        ZonedDateTime zdt = parseDifferentFormats(input);
        System.out.format(Locale.ENGLISH, "%-21s parsed to %s%n", input, zdt);
    }

Output so far when running in Asia/Kolkata time zone:

2017-feb-02           parsed to 2017-02-02T00:00+05:30[Asia/Kolkata]
2017-05-02            parsed to 2017-05-02T00:00+05:30[Asia/Kolkata]
2017-01-01 03:00:00   parsed to 2017-01-01T03:00+05:30[Asia/Kolkata]
2017-01-01 00:00:00.0 parsed to 2017-01-01T00:00+05:30[Asia/Kolkata]

Formatting

You want a result in format YYYY-MM-DDTHH:mm:ssZ. This is ISO 8601 format with a trailing Z implying date and time in UTC (so-called Zulu time). I suggest this formatter:

private static final DateTimeFormatter isoFormatter
        = new DateTimeFormatterBuilder()
                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                .appendLiteral('T')
                .appendPattern("HH:mm:ssX")
                .toFormatter(Locale.ROOT);

We need to convert to UTC before formatting:

        String formattedDateTime = zdt.toOffsetDateTime()
                .withOffsetSameInstant(ZoneOffset.UTC)
                .format(isoFormatter);
        System.out.format(Locale.ENGLISH, "%s formatted to %s%n", zdt, formattedDateTime);

Example output:

2017-02-02T00:00+05:30[Asia/Kolkata] formatted to 2017-02-01T18:30:00Z

Links

Upvotes: 1

Basil Bourque
Basil Bourque

Reputation: 340098

Avoid legacy date-time classes

The legacy date-time classes are a terrible awful mess. Avoid them. Avoid Date, Calendar, SimpleDateFormat, etc.

java.time

Instead, use only the modern java.time classes built into Java 8+.

ISO 8601

Your second input complies with the ISO 8601 standard format for a date-only value: YYYY-MM-DD. The java.time classes use ISO 8601 formats by default when parsing/generating text. So no need for any formatting pattern.

LocalDate ld = LocalDate.parse ( "2017-05-02" ) ;

Your desired format of YYYY-MM-DDTHH:mm:ssZ ends with a Z. That letter is an abbreviation for an offset of zero hours-minutes-seconds from UTC, pronounced “Zulu”. Text in this format represents a date, with time-of-day, as seen with an offset-from-UTC of zero. Such a value represents a moment, a point on the timeline.

So we are in bit of a pickle. Your input is a date-only, but you want a date with time-of-day in an offset. To resolve this, we need to find the first moment of the day as seen in an offset of zero.

LocalDate ld = LocalDate.parse ( "2017-05-02" );
ZonedDateTime zdt = ld.atStartOfDay ( ZoneOffset.UTC );

zdt.toString() = 2017-05-02T00:00Z

Your third & fourth inputs have a date and a time but lack the offset. So parse as LocalDateTime objects. Standard ISO 8601 format requires a T in the middle, where you have a SPACE. So swap, then parse.

String input = "2017-01-01 03:00:00".replace ( " " , "T" ) ;
LocalDateTime ldt = LocalDateTime.parse ( input ) ;

Assign the desired offset of zero.

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

odt.toString() = 2017-01-01T03:00Z

Upvotes: 1

kism3t
kism3t

Reputation: 1361

I would solve your problem like this, with the help mentioned in the comment section of your question:

public static void main(final String[] args) throws Exception {

    String[] formatStrings = { "yyyy-MMM-dd", "yyyy-MM-dd", "yyyy-MMM-dd hh:mm:ss", "yyyy-MMM-dd hh:mm:ss.S" };

    String yourDate = "2017-Mar-02";
    Date date = null;
    for (String formatString : formatStrings) {
        try {
            date = new SimpleDateFormat(formatString, Locale.US).parse(yourDate);
        } catch (ParseException e) {
        }
    }
    DateFormat formatTo = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssz", Locale.US);
    System.out.println(formatTo.format(date));
}

Upvotes: 0

alexandrum
alexandrum

Reputation: 427

The pattern you want to convert it should be like: yyyy-MM-dd'T'HH:mm:ssz. See more here. You can do it with SimpleDateFormat, a small example:

    String date = "2017-05-02";
    String oldFormat = "yyyy-MM-dd";
    String newFormat = "yyyy-MM-dd'T'HH:mm:ssz";
    SimpleDateFormat sdf = new SimpleDateFormat(oldFormat);
    try {
        Date newDate = sdf.parse(date);
        sdf.applyPattern(newFormat);
        System.out.println(sdf.format(newDate));
    } catch (ParseException e) {
        e.printStackTrace();
    }

Upvotes: 0

Related Questions