How can I convert Date.toString back to Date? - java

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.

Related

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.

DateTime Manipulation in C# vs Java

I'm new to Java. I have a time I am getting from a web-page, this is in the "hh:mm" format (not 24 hour). This comes to me as a string. I then want to combine this string with todays date in order to make a Java Date I can use.
In C#:
string s = "5:45 PM";
DateTime d;
DateTime.TryParse(s, out d);
in Java I have attempted:
String s = "5:45 PM";
Date d = new Date(); // Which instantiates with the current date/time.
String[] arr = s.split(" ");
boolean isPm = arr[1].compareToIgnoreCase("PM") == 0;
arr = arr[0].split(":");
int hours = Integer.parseInt(arr[0]);
d.setHours(isPm ? hours + 12 : hours);
d.setMinutes(Integer.parseInt(arr[1]));
d.setSeconds(0);
Is there a better way to achieve what I want?
Is there a better way to achieve what I want?
Absolutely - in both .NET and in Java, in fact. In .NET I'd (in a biased way) recommend using Noda Time so you can represent just a time of day as a LocalTime, parsing precisely the pattern you expect.
In Java 8 you can do the same thing with java.time.LocalTime:
import java.time.*;
import java.time.format.*;
public class Test {
public static void main(String[] args) {
String text = "5:45 PM";
DateTimeFormatter format = DateTimeFormatter.ofPattern("h:mm a");
LocalTime time = LocalTime.parse(text, format);
System.out.println(time);
}
}
Once you've parsed the text you've got into an appropriate type, you can combine it with other types. For example, to get a ZonedDateTime in the system time zone, using today's date and the specified time of day, you might use:
ZonedDateTime zoned = ZonedDateTime.now().with(time);
That uses the system time zone and clock by default, making it hard to test - I'd recommend passing in a Clock for testability.
(The same sort of thing is available in Noda Time, but slightly differently. Let me know if you need details.)
I would strongly recommend against using java.util.Date, which just represents an instant in time and has an awful API.
The key points here are:
Parse the text with a well-specified format
Parse the text into a type that represents the information it conveys: a time of day
Combine that value with another value which should also be carefully specified (in terms of clock and time zone)
All of these will lead to clear, reliable, testable code. (And the existing .NET code doesn't meet any of those bullet points, IMO.)
To parse the time, you can do as explained in #Jon Skeet's answer:
String input = "5:45 PM";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
LocalTime time = LocalTime.parse(input, parser);
Note that I also used a java.util.Locale because if you don't specify it, it'll use the system's default locale - and some locales can use different symbols for AM/PM field. Using an explicit locale avoids this corner-case (and the default locale can also be changed, even at runtime, so it's better to use an explicit one).
To combine with the today's date, you'll need a java.time.LocalDate (to get the date) and combine with the LocalTime, to get a LocalDateTime:
// combine with today's date
LocalDateTime combined = LocalDate.now().atTime(time);
Then you can format the LocalDateTime using another formatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
System.out.println(combined.format(fmt));
The output is:
16/08/2017 17:45
If you want to convert the LocalDateTime to a java.util.Date, you must take care of some details.
A java.util.Date represents the number of milliseconds since 1970-01-01T00:00Z (aka Unix Epoch). It's an instant (a specific point in time). Check this article for more info.
So, the same Date object can represent different dates or times, depending on where you are: think that, right now, at this moment, everybody in the world are in the same instant (the same number of milliseconds since 1970-01-01T00:00Z), but the local date and time is different in each part of the world.
A LocalDateTime represents this concept of "local": it's a date (day, month and year) and a time (hour, minute, second and nanosecond), but without any relation to a specific timezone.
The same LocalDateTime object can represent different instants in time in different timezones. So, to convert it to a Date, you must define in what timezone you want it.
One option is to use the system's default timezone:
// convert to system's default timezone
ZonedDateTime atDefaultTimezone = combined.atZone(ZoneId.systemDefault());
// convert to java.util.Date
Date date = Date.from(atDefaultTimezone.toInstant());
But the default can vary from system/environment, and can also be changed, even at runtime. To not depend on that and have more control over it, you can use an explicit zone:
// convert to a specific timezone
ZonedDateTime zdt = combined.atZone(ZoneId.of("Europe/London"));
// convert to java.util.Date
Date date = Date.from(zdt.toInstant());
Note that I used Europe/London. The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
And there's also the corner cases of Daylight Saving Time (when a LocalDateTime can exist twice or can't exist due to overlaps and gaps). In this case, Jon's solution using ZonedDateTime avoids this problem).

Format a String to a Fixed Date at UTC+0 in Java

I want to convert a String date - 2017-01-01 to java.util.Date with UTC+0. So, what I am expecting is.
"2017-01-01" -> 2017-01-01T00:00:00 UTC+0100
Here is how I am trying to do, but as my default Timezone is UTC+1, I am getting that 1 hour added to the Date.
Date d = Date.from(Instant.parse("2017-01-01T00:00:00Z"));
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss 'UTC'ZZZZZ");
String output = sf.format(d);
System.out.println(output);
Here is the output:
2017-01-01T01:00:00 UTC+0100
Can somebody help?
Your code is mixing oldfashioned and modern classes. Date and SimpleDateFormat are long outdated. Instant is modern (from 2014). I recommend you stick to the modern ones unless you are working with an old API that requires and/or gives you an instance of an oldfashioned class. So the answer is
String output = LocalDate.parse("2017-01-01")
.atStartOfDay(ZoneOffset.ofHours(1))
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss 'UTC'XX"));
The result is the one you asked for
2017-01-01T00:00:00 UTC+0100
The code is not really shorter than yours, but once you get used to the fluent style you will find it clearer and more natural. The room for confusion and errors is considerably reduced.
If you want the start of day in whatever time zone the user is in, just fill in ZoneId.systemDefault() instead of ZoneOffset.ofHours(1).
LocalDate parses your date string — "2017-01-01" — without an explicit format. The string conforms to ISO 8601, and the modern classes use this standard as their default for parsing and also for their toString().
You can set the timezone first and then format it.
sf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = sf.parse(d);
And now format as per your requirements:
String output = sf.format(date);
System.out.println(output);
I wonder please try this also:
Date date = new Date();
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
date = cal.getTime();

Java Types: What is the format for representing a variable of type `Date`? `Time`?

I have a varaibles:
Date date;
Time time;
and methods:
MyDateMethod(Date date){
//do stuff
}
MyTimeMethod(Time time){
//do stuff
}
I tried using MyDateMethod() with the following call:
MyDateMethod(1995-03-7);
I get an error saying I've supplied it with type int when it expected type Time.
I also tried using MyTimeMethod() with the following call:
MyTimeMethod(03:04:55);
I get an error saying Type mismatch: Cannot convert type int to boolean.
What is the format to put in a variable of these different types? Date is obviously not xxxx-xx-xx and Time is obviously not xx:xx:xx.
There are a few options,
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date d = sdf.parse("1995-03-07");
System.out.println(d);
} catch (ParseException e) {
e.printStackTrace();
}
Output is
Tue Mar 07 00:00:00 EST 1995
Or, you could use
// -1 because January is 0... I didn't design it!
Calendar c = new GregorianCalendar(1995, 3 - 1, 7);
System.out.println(c.getTime());
with the same output as before.
SimpleDateFormat is what You need. It's need to be initialized by format of date.
Use it like this:
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy/MM/dd HH:mm:ss", Locale.getDefault());
and then:
Date date = (Date) dateFormat.parse("2014/04/02 22:22:22");
Take a look at DateFormat and particularly SimpleDateFormat.
Your example would be coded like this, using SimpleDateFormat:
Date date = new SimpleDateFormat("yyyy-MM-dd").parse("1995-03-07")
(I'm assuming you have months before days here, you will need to interchange the MM and dd if not).
Java does not support Date literals. You need to use a constructor or a static factory method to obtain an instance of this class.
1995-03-07 returns 1985 because you have three int literals here and the hyphens are interpreted as subtraction operators.
Take a look at the documentation for the Date class and the DateFormat class
Here's a way you could represent these values:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = dateFormat.parse("1995-03-07");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
Date time = timeFormat.parse("03:46:16");
You can use the same format objects to perform the reverse of this conversion. Please mind that these Date instances represent a specific moment in time, down to millisecond level. Internally, this is represented as the number of milliseconds since January 1, 1970, 00:00:00 GMT
This API is hardly the prettiest one in Java and by looking at the docs you can see how many changes it has undergone. Just look at the number of deprecated methods.
I recommend taking a look at the Joda-Time library instead.
Alternatively, if using Java 8 is an option for you, you can try the brand new API that comes with it
The answer by Tom is correct. No date-time literals inJava.
And as he stated, the old bundled java.util.Date and .Calendar classes are notoriously troublesome. Avoid them. Use either the Joda-Time library or the new java.time package in Java 8.
Joda-Time
In Joda-Time:
If you want only a date without time and time zone, use the LocalDate class.
Similarly to use only time while ignoring time zone and date, use the LocalTime class.
But most often you'll probably want to use the DateTime class which tracks date, time, and time zone all in one object.
A Date-Time Is Not Text
A DateTime object does not contain text. No String. If you need a string representation, use a formatter object to generate one. Search StackOverflow for many examples.
Built into Joda-Time are formatters for the sensible and increasingly common ISO 8601 standard. For example, the toString implementation on the DateTime class produces a String like this…
2014-04-01T20:17:35-08:00

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

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

Categories