Reputation: 285
/*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
Reputation: 79075
There are three major problems in your code:
.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.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.DD
for Day in month whereas the correct pattern for this would be dd
. The symbol, DD
is used for Day in yearApart 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 Locale
s.
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:
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.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:
.SSSSSSSSS
with .n
.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.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: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
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
.
//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]
//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
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.
Upvotes: 3