how to parse output of new Date().toString() - java

I need a date format (maybe SimpleDateFormat) that parses reliable the output I get when I call toString() on a Date object. Output on my german(!) system is: "Sun Dec 12 13:45:12 CET 2010", so it doesn't seem to honor locales, which seems to make it easy.
Anyone?

That format is specified in the Date#toString().
Converts this Date object to a String of the form:
dow mon dd hh:mm:ss zzz yyyy
So, in SimpleDateFormat pattern terms:
EEE MMM dd HH:mm:ss zzz yyyy
Unrelated to the problem, I wonder if it wasn't in first place a bad idea to use Date#toString() instead of SimpleDateFormat#format() to output dates. I would consider to fix it straight there.

BalusC gave you the correct format, you I'd say - don't. The toString() method must not be used for anything other than logging.
You can use SimpleDateFormat for both formatting and parsing.

TL;DR
Instant parsedBack = Instant.parse(Instant.now().toString());
System.out.println(parsedBack);
2019-05-30T08:36:47.966274Z
Use ISO 8601 and java.time
If your real goal is to serialize and deserialize a date and time (for data transfer or for persistence, for example), serialize to ISO 8601, the standard format for date and time data.
Skip the long outdated Date class. The modern Java date and time API known as java.time is so much nicer to work with. The class you need from it is probably Instant (this depends on your more exact requirements).
The two points go nicely hand in hand:
Instant i = Instant.now();
String s = i.toString();
Instant theSameInstant = Instant.parse(s);
The modern classes’ toString methods produce ISO 8601 format (e.g., 2018-01-11T10:59:45.036Z), and their parse methods read the same format back. So this snippet is all you need, and you get an instant equal to the first, with nanosecond precision.
If you cannot control the string you get, and you get the result from Date.toString(), the format pattern string in BalusC’s answer works with java.time too:
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
Date d = new Date();
String s = d.toString();
Instant nearlyTheSameInstant = ZonedDateTime.parse(s, dtf).toInstant();
Some warnings, though:
Milliseconds from the original Date are lost since they are not in the string, leading to an inaccuracy of up to 999 milliseconds (which was why I named the variable nearlyTheSameInstant).
The era from the original Date is not in the string either. So if your original Date was in year 44 BCE, you will get the corresponding date in year 44 CE (AD) (in which case the variable name nearlyTheSameInstant was a lie anyway).
The time zone abbreviation in the string is often (most often?) ambiguous, so there is a great risk of getting the wrong time zone and hence a wrong time. To make matters worse, an ambiguous time zone abbreviation will be interpreted differently on different JVMs
It’s essential to provide a locale. Otherwise the JVM’s default locale will be used, and if it’s not English, parsing will fail. In the worst case you will see your code running fine for many years and suddenly it will break when one day someone runs it on a computer or device with a different locale setting. I use Locale.ROOT for “the locale neutral locale” or “don’t apply any locale specific processing”. It seems to be the correct approach here.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601

you can use jdk8's DateTimeFormatter
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
Date d = new Date();
String s = d.toString();
Instant nearlyTheSameInstant = ZonedDateTime.parse(s, dtf).toInstant();
Date nearlyTheSameInstantDate = Date.from(nearlyTheSameInstant);
DateTime dateTime = new DateTime(nearlyTheSameInstantDate);
String str = dateTime.toString("yyyy/MM/dd");
System.out.println("str = " + str);

Related

How to convert a LocalDateTime date to a particular format?

Hi I am having small Java code snippet. I am using Java 8. I have a Java LocalDateTime object and I want to format it. Please see my code below.
String dateUTC = "2021-10-21T10:32:38Z";
Instant i = Instant.parse(dateUTC);
LocalDateTime ldt = i.atZone(ZoneId.of("Europe/London")).toLocalDateTime();
I want to print the LocalDateTime object ldt as string in the following format:
Oct 21 2021 11:32:38 AM
How can I achieve that?
Use a built-in localized format
Can you live with commas in the output?
DateTimeFormatter dateTimeFormatter
= DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(Locale.US);
System.out.println(ldt.format(dateTimeFormatter));
Output:
Oct 21, 2021, 11:32:38 AM
The immediate advantage is that we don’t need to fiddle with any format pattern string, which is always an error-prone task. Two further advantages are: 1. users likely will be happier with the built-in format since this is constructed to fit the expectations of the people in the locale (USA in the example); 2. the code trivially lends itself well to localization: just specify a different locale to make people of a different culture happy.
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm:ss a");
System.out.println( ldt.format(dateTimeFormatter));

Convert or format string to date

I am struggling with this ..
I have an input string - like this: 2021-10-13 11:33:16.000-04
Using Java.
I need to get a Date object from it.
which formatting pattern can I use ?
I try with these
SimpleDateFormat inFormatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS'-'ZZ");
and
SimpleDateFormat inFormatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSSZZ");
and I keep getting
java.text.ParseException: Unparseable date: "2021-10-13 11:33:16.000-04"
at java.base/java.text.DateFormat.parse(DateFormat.java:396)
at com.dima.tests.DatesConversions.main(DatesConversions.java:24)
Please, help !!
Don't use Date as it is outdated. Use the classes in the java.time
OffsetDateTime odt = OffsetDateTime.parse(str,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSX"));
System.out.println(odt);
Prints
2021-10-13T11:33:16-04:00
java.time
Even though you need to give an old-fashionede Date object to a legacy API beyond your control, I still recommend that you use java.time, the modern Java date and time API, in your own code. The final conversion to Date is pretty straight-forward.
I’d use this formatter for maximum reuse of existing formatters:
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendOffset("+HHmm", "+00")
.toFormatter(Locale.ROOT);
Then we parse and convert like this:
String input = "2021-10-13 11:33:16.000-04";
OffsetDateTime dateTime = OffsetDateTime.parse(input, PARSER);
System.out.println(dateTime);
Instant i = dateTime.toInstant();
Date oldfashionedDate = Date.from(i);
System.out.println(oldfashionedDate);
Output in my time zone, Europe/Copenhagen:
2021-10-13T11:33:16-04:00
Wed Oct 13 17:33:16 CEST 2021
Denmark is at offset +02:00 at this time of year, so 6 hours ahead of the UTC offset -04 from your string. Therefore Date.toString() confusingly prints a clock hour that is 6 hours ahead of the original time of day.
Note: if your forward service accepts anything else than an old-fashioned Date, you should not be using that class. For example, if a String is required, the OffsetDateTime that we got can be formatted into a new string using a second DateTimeFormatter (or in lucky cases, its toString method).
What went wrong in your code?
First, a UTC offset can have positive or negative sign. Instead of -04 you could have had for example +09. Formatters are designed for to take the sign, + or -, as part of the offset. Therefore hardcoding the minus sign as a literal, as in your first attempt, is bound to fail. In your second attempt, yyyy-MM-dd HH:mm:ss.SSSZZ, you are already closer. However, ZZ is for an offset with sign and four digits (like +0530 or -0400; hour and minute), so does not work for a two-digit offset like -04. Your SimpleDateFormat expected more digits where your string ended and therefore threw the exception that you saw.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Since you are using ISO 8601 time zone timezone, you have the use the below pattern.
SimpleDateFormat inFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX");
And then, to get the date:
Date date = inFormatter.parse("2021-10-13 11:33:16.000-04");
Always check the documentation.

Different time conversion by using java.util.Date in Java

I am using the below code for epoch to time conversion by using java.util.Date class in Java.
Long scheduledTime = 1602258300000L;
Date date = new Date(scheduledTime);
System.out.println("Date obj :" + date);
Below are the outputs while running the same code on two different timezone server :
On EDT server-
Date obj :Fri Oct 09 11:45:00 EDT 2020
On IST server -
Date obj :Fri Oct 09 21:15:00 IST 2020
Why does this happen? I am only passing milliseconds. This data is supposed to be treated as 21:15 on all servers. Why does Date class change the data?
Please share a sample piece of code for getting the same time data regardless of the timezone of the server.
A Date object represents a specific instant in time, represented by a given number of milliseconds since the Unix epoch.
The toString() method converts that instant in time into a local time based on the default time zone. It's not that the Date value itself "has" a time zone - it's just toString() that uses the default one.
This data is supposed to be treated as 21:15 on all servers.
That suggests you want to use the Indian time zone in all servers, at least when converting the instant in time for display. Without knowing anything more about your application, that's all we can say... other than "don't use java.util.Date or java.util.Calendar; use the java.time classes instead". They're much better designed, and you're less likely to run into problems like this.
java.time
I recommend you use java.time, the modern Java date and time API, for your date and time work.
long scheduledTime = 1_602_258_300_000L;
Instant pointInTime = Instant.ofEpochMilli(scheduledTime);
System.out.println(pointInTime);
Output from this snippet will be the same on all servers in all time zones:
2020-10-09T15:45:00Z
Since you want 21:15, specify the time zone for India:
ZoneId serverTimeZone = ZoneId.of("Asia/Kolkata");
ZonedDateTime dateTime = pointInTime.atZone(serverTimeZone);
System.out.println(dateTime);
2020-10-09T21:15+05:30[Asia/Kolkata]
What went wrong?
The epoch is one point in time independent of time zone. so a count of milliseconds also denotes one point in time. In your case that point in time is Friday 9. October 2020 15:45:00 UTC. And at that point in time it was 21:15 in India and 11:45 on the East coast of North America. It’s a confusing trait of the outdated Date class that on one hand it represents just a point in time, on the other hand its toString method grabs the time zone setting of the JVM and uses it for rendering the string to be returned, thus giving you the false impression that you get different Date objects in different time zones when in fact they are equal.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Epoch & Unix Timestamp Conversion Tools where you can check what’s the equivalent of your milliseconds in UTC/GMT and in your own time zone.
As pointed by others you should now use the java.time package for working with time. If you look at the documentation of the toString() method of java.util.Date, it says that it coverts the Date object to a String of form:
EEE MMM d m dd hh:mm:ss zzz yyyy
It is like the following code is running in the background:
public String toString(){
Date date=this;
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(
"EEE MMM d m dd hh:mm:ss zzz yyyy");
simpleDateFormat.setTimeZone(TimeZone.getDefault()); //This line is important.
return simpleDateFormat.format(date);
}
Now, if you wanna format your Date object for a certain timezone you can do the same including setting the timezone:
Long scheduledTime = 1602258300000L;
Date date = new Date(scheduledTime);
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(
"EEE MMM d m dd hh:mm:ss zzz yyyy");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("IST"));
String dateStr = simpleDateFormat.format(date);
System.out.println("Date obj :" + dateStr);

Single class to parse any Date Format in Java

I have been parsing dates in the below formats. I maintain an array of these formats and parse every date string in all these formats.
The code I used was -
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
simpleDateFormat.setTimeZone(timeZone); //timeZone is a java.util.TimeZone object
Date date = simpleDateFormat.parse(dateString);
Now I want to parse yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX format as well but using SimpleDateFormat the 6 digit microseconds are not considered. So I looked into java.time package.
To parse yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX formats I will be needing OffsetDateTime class and for other formats, I need ZonedDateTime class. The format will be set in DateTimeFormatter class.
Is there a way to use a single class like SimpleDateFormat to pass all the formats?
Since your Java 8 doesn’t behave as would be reasonably expected, I suggest that a workaround is trying to parse without zone first. If a zone or an offset is parsed from the string, this will be used. If the parsing without zone fails, try with a zone. The following method does that:
private static void parseAndPrint(String formatPattern, String dateTimeString) {
// Try parsing without zone first
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern);
Instant parsedInstant;
try {
parsedInstant = formatter.parse(dateTimeString, Instant::from);
} catch (DateTimeParseException dtpe) {
// Try parsing with zone
ZoneId defaultZone = ZoneId.of("Asia/Calcutta");
formatter = formatter.withZone(defaultZone);
parsedInstant = formatter.parse(dateTimeString, Instant::from);
}
System.out.println("Parsed instant: " + parsedInstant);
}
Let’s try it:
parseAndPrint("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX", "2018-10-22T02:17:58.717853Z");
parseAndPrint("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", "2018-10-22T02:17:58.717853");
parseAndPrint("EEE MMM d HH:mm:ss zzz yyyy", "Mon Oct 22 02:17:58 CEST 2018");
Output on Java 8 is:
Parsed instant: 2018-10-22T02:17:58.717853Z
Parsed instant: 2018-10-21T20:47:58.717853Z
Parsed instant: 2018-10-22T00:17:58Z
The first example has an offset in the string and the last a time zone abbreviation in the string, and in both cases are these respected: the instant printed has adjusted the time into UTC (since an Instant always prints in UTC, its toString method makes sure). The middle example has got neither offset nor time zone in the string, so uses the default time zone of Asia/Calcutta specified in the method.
That said, parsing a three or four letter time zone abbreviation like CEST is a dangerous and discouraged practice since the abbreviations are often ambiguous. I included the example for demonstration only.
Is there a way to use a single class…?
I have used Instant for all cases, so yes there is a way to use just one class. The limitation is that you do not know afterward whether any time zone or offset was in the string nor what it was. You didn’t know when you were using SimpleDateFormat and Date either, so I figured it was OK?
A bug in Java 8?
The results from your demonstration on REX tester are disappointing and wrong and do not agree with the results I got on Java 11. It seems to me that you have been hit by a bug in Java 8, possibly this one: Parsing with DateTimeFormatter.withZone does not behave as described in javadocs.

How can I convert Date.toString back to Date?

I have a string obtained by calling the toString method of an instance of the class Date. How can I get a Date object from this string?
Date d = new Date();
String s = d.toString;
Date theSameDate = ...
UPDATE
I've tried to use SimpleDateFormat, but I get java.text.ParseException: Unparseable date
What is the date format produced by Date.toString ()?
If your real goal is to serialize a Date object for some kind of custom made persistence or data transfer, a simple solution would be:
Date d = new Date();
long l = d.getTime();
Date theSameDate = new Date(l);
You could do it like this
Date d = new Date();
String s = d.toString;
Date theSameDate = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy").parse(s);
If your real goal is to serialize and deserialize a date and time (for data transfer or for persistence, for example), serialize to ISO 8601, the standard format for date and time data.
Skip the long outdated Date class. The modern Java date and time API known as java.time is so much nicer to work with. The class you need from it is probably Instant (this depends on your more exact requirements).
The two points go nicely hand in hand:
Instant i = Instant.now();
String s = i.toString();
Instant theSameInstant = Instant.parse(s);
The modern classes’ toString methods produce ISO 8601 format (e.g., 2018-01-11T10:59:45.036Z), and their parse methods read the same format back. So this snippet is all you need, and you get an instant equal to the first, with nanosecond precision.
If you cannot control the string you get, and you get the result from Date.toString(), the format pattern string in Sedalb’s answer works with java.time too:
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
Date d = new Date();
String s = d.toString();
Instant nearlyTheSameInstant = ZonedDateTime.parse(s, dtf).toInstant();
It’s essential to provide a locale. Otherwise the JVM’s default locale will be used, and if it’s not English, parsing will fail. In the worst case you will see your code running fine for many years and suddenly it will break when one day someone runs it on a computer or device with a different locale setting.
The point from jambjo’s answer still applies: The three and four letter time zone abbreviations used in Date.toString() are very often ambiguous, so there is no guarantee that the time zone is interpreted correctly, and again, it will be interpreted differently on different JVMs.
Finally, Date.toString() does not render the milliseconds that the Date holds, which leads to an inaccuracy of up to 999 milliseconds. If using the string from Date.toString(), there is nothing we can do about it (which was why I named the variable nearlyTheSameInstant).
Take a look at SimpleDateFormat#parse(). It should provide the functionality you're looking for.
Date theSameDate = new Date(Date.parse(s));
For some not so obvious reasons, this is not a particularly good idea. You can find details on that in the API documentation for the parse method. One problem is e.g. that the time zone abbreviations are ambiguous, so that the parser may fail in interpreting the correct time zone.

Categories