Ramesh Kanuganti
Ramesh Kanuganti

Reputation: 285

StringDate to Date coming in different Time in SimpleDateFormat in java

    /*I want the same Date of my String has
     Tried with couple of options but nothing worked 
     pasting some of code here */

    public void stringToDate() {

        //Current format "13-FEB-20 03.21.08.100000000 PM" in Melbourne Timezone
        //Required Format yyyy-dd-MM HH:mm:ss.SSS in Melbourne Timezone

        String inputAM = "13-FEB-20 03.21.08.100000000 PM";
        try {
            DateFormat df1 = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.S aa");
            Date d1 = df1.parse(inputAM);
            System.out.println("Date-1: " + d1); //Fri Feb 14 19:07:48 AEDT 2020

            DateFormat df2 = new SimpleDateFormat("DD-MMM-yy hh.mm.ss.S aa");
            Date d2 = df2.parse(inputAM);
            System.out.println("Date-2: " + d2); //Tue Jan 14 19:07:48 AEDT 2020

        } catch (ParseException e) {
            e.printStackTrace();
        }

        SimpleDateFormat etDf = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS");
        TimeZone etTimeZone = TimeZone.getTimeZone("Australia/Melbourne");
        etDf.setTimeZone(etTimeZone);

        DateFormat df3 = new SimpleDateFormat("dd-MMM-yy HH.mm.ss.SSSSSSSSS a");
        Date d3;
        try {
            d3 = df3.parse(inputAM);
            System.out.println("Date-3: " + d3); //Fri Feb 14 07:07:48 AEDT 2020
            System.out.println("Date-4: " + etDf.format(d3.getTime())); //2020-14-02 07:07:48.000
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

Upvotes: 1

Views: 910

Answers (2)

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79075

There are three major problems in your code:

  1. You have used .SSSSSSSSS for the fraction of a second whereas the SimpleDateFormat does not support a precision beyond milliseconds (.SSS). It also means that you need to limit the digits in the fraction of a second to three.
  2. You have used HH for a time in 12-Hour format (i.e. with am/pm) whereas the correct pattern for this is hh. The symbol, HH is used for a time in 24-Hour format.
  3. You have used DD for Day in month whereas the correct pattern for this would be dd. The symbol, DD is used for Day in year

Apart from this, I recommend you always use Locale with a date parsing/formatting API because parts of a date-time string are represented in different ways in different Locales.

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

public class Main {
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdfInput = new SimpleDateFormat("dd-MMM-yy hh.mm.ss.SSS a", Locale.ENGLISH);
        Date date = sdfInput.parse("13-FEB-20 03.21.08.100 PM");

        // Print Date#toString
        System.out.println(date);

        // Print the date-time in a custom format
        SimpleDateFormat sdfOutput = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS", Locale.ENGLISH);
        System.out.println(sdfOutput.format(date));

        // If required, format date with a timezone
        SimpleDateFormat sdfOutputWithTz = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS[zzzz]", Locale.ENGLISH);
        sdfOutputWithTz.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"));
        System.out.println(sdfOutputWithTz.format(date));
    }
}

Output:

Thu Feb 13 15:21:08 GMT 2020
2020-13-02 15:21:08.100
2020-14-02 02:21:08.100[Australian Eastern Daylight Time]

Some facts about legacy API:

  1. The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
  2. 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.

Using modern date-time API:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtfInput = new DateTimeFormatterBuilder()
                .parseCaseInsensitive() // For case-insensitive (e.g. AM/am) parsing
                .appendPattern("dd-MMM-uu hh.mm.ss.SSSSSSSSS a")
                .toFormatter(Locale.ENGLISH);       
        
        // Parse the date-time string
        LocalDateTime ldt = LocalDateTime.parse("13-FEB-20 03.21.08.100000000 PM", dtfInput);
        
        // Print LocalDateTime#toString
        System.out.println(ldt);
        
        // Print the date-time in a custom format
        DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("uuuu-dd-MM HH:mm:ss.SSS", Locale.ENGLISH);
        System.out.println(ldt.format(dtfOutput));
        
        // If required, convert the LocalDateTime to ZonedDateTime
        ZonedDateTime zdt = ldt.atZone(ZoneId.of("Australia/Melbourne"));
        System.out.println(zdt);
    }
}

Output:

2020-02-13T15:21:08.100
2020-13-02 15:21:08.100
2020-02-13T15:21:08.100+11:00[Australia/Melbourne]

Note:

  1. If your date-time string has always 9 digits for the fraction of a second, you can replace .SSSSSSSSS with .n.
  2. For DateTimeFormatter, the symbol, u means year whereas the symbol, y means year-of-era. It doesn't make any difference for a year in the AD era, but it matters for a year in the BC era. Check this answer to learn more about it.
  3. LocalDateTime does not have timezone information. If you want to display the timezone information, you should consider using ZonedDateTime or OffsetDateTime. The following table shows an overview of java.time data-time types:

enter image description here

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.

Upvotes: 1

Anonymous
Anonymous

Reputation: 86282

First, for most purposes don’t convert a date and time from one string format to another. In your program keep date and time as proper date-time objects, not strings. When you accept string input, parse it first thing. Only when you need to give string output, format your date-time into a string in the required format.

Second, use java.time, the modern Java date and time API for all of your date and time work. It is so much nicer to work with than the old, poorly designed and long outdated classes including DateFormat, SimpleDateFormat and Date.

Parse your input using java.time

    //Current format "13-FEB-20 03.21.08.100000000 PM" in Melbourne Timezone
    DateTimeFormatter currentFormatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("d-MMM-uu hh.mm.ss.SSSSSSSSS a")
            .toFormatter(Locale.ENGLISH);
    ZoneId zone = ZoneId.of("Australia/Melbourne");

    String inputAM = "13-FEB-20 03.21.08.100000000 PM";

    ZonedDateTime dateTime = LocalDateTime.parse(inputAM, currentFormatter).atZone(zone);

    System.out.println(dateTime);

Output so far is:

2020-02-13T15:21:08.100+11:00[Australia/Melbourne]

Format using java.time

    //Required Format yyyy-dd-MM HH:mm:ss.SSS in Melbourne Timezone
    DateTimeFormatter requiredFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS");
    String formatted = dateTime.format(requiredFormatter);
    System.out.println(formatted);

2020-02-13 15:21:08.100

What went wrong in your code?

When I first tried your code, parsing failed for FEB because your code doesn’t specify locale, and my Java uses a non-English default locale.

Correcting that I was able to parse the string and got the same results as you (only in my time zone). What happens: SimpleDateFormat takes uppercase S to mean milliseconds, 1000ths of seconds, no matter how many S there are and no matter how many digits are in the string to be parsed. So 100 000 000 milliseconds were added to your date and time. That’s a little more than one day. So you got 14 Feb instead of 13 Feb, and also a wrong time of day. In contrast to the modern DateTimeFormatter upper case S means fraction of second, so it handles both SSSSSSSSS and SSS the way we expect.

Uppercase DD is for day of year, so using this for parsing you got the 13th day of the year (the same as 13 January) plus your 100 000 seconds.

Uppercase HH is for hour of day from 00 through 23. Parsing using HH gave you 03:21 of day (same as 03:21 AM) plus your 100 000 seconds no matter that it said PM in your string.

Link

Upvotes: 3

Related Questions