I am using jackson configurator to serialize - deserialize date.
I am using this
SerializationConfig serConfig = mapper.getSerializationConfig();
serConfig.setDateFormat(new SimpleDateFormat("MM/dd/yyyy HH:mm:ss z"));
DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
deserializationConfig.setDateFormat(new SimpleDateFormat("MM/dd/yyyy HH:mm:ss z"));
Now if I pass 10/10/2013 02:30:00 EST it allows me to do so but 10/10/2013 02:30:00 gives me error.
How to make configurator parse both?
You should not be passing a date-time without a time zone. To do so is nonsense, without meaning. Like saving a local phone number without an area code.
Jackson saves its dates, as it should, in UTC time zone. That means no time zone at all. So the date-time you pass must have a time zone so that Jackson may adjust the value to UTC. I don't actually know or use Jackson, but that's what it's doc says.
If the code passing the date-time knows or can deduce or infer the time zone, it should do so and append a time zone.
If you are passing a date-time that is already in UTC, then add a time zone of "Z", for Zulu.
Beware: Using 3-letter time zone codes is not a good practice. They are not standardized and frequently have duplicates. The EST value in your example means at least 3 time zones around the world: US, Australia, and Brazil. Use name of time zone instead.
Tip: If you do any work with dates in Java, get the third-party Joda-Time library rather than use the java.util.Date/Calendar classes.
Related
how to parse this date format in java?
2022-01-19T18:14:17+03:00
I tried SimpleDateFormat("yyyy-MM-dd HH:mm:ssz"), but it didn't work.
Use a ZonedDateTime.
ZonedDateTime t = ZonedDateTime.parse("2022-01-19T18:14:17+03:00");
This uses the default zone you are in (only the offset +03:00 is given. toString is the inverse operation.
The format shown is that of an OffsetDateTime, which needs some time zone, a (default) time zone ID, to have a full ZonedDateTime. Note that an OffsetDateTime is incomplete without the time zone. It migh be used to indicate a latitude on earth, but the local clocks might provide several differing times. Normally choose a ZonedDateTime.
The format of the input is the ISO standard with T for time.
This means you do not need a DateTimeFormatter.
If you need to use the obsolete old Date:
Date date = Date.fromInstant(t.toInstant());
Maybe it doesn't read 'T' "2022-01-19T18:14:17+03:00", or try to fit the data to Examples formats
I've an input string as
2020-01-21T02:16:51.8320Z
I need to parse this string into a java Date object.
I tried using following code.
LocalDate localDate = LocalDate.parse(date, flexUtcDtf);
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
Output:
Wed Jan 22 00:00:00 EST 2020
Is it possible to get the output as following Date object instead?(i.e. preserving the time details as well)
Wed Jan 21 02:46:51.8320 EST 2020
Thanks.
java.util.Date
.from(
Instant.parse(
"2020-01-21T02:16:51.8320Z"
)
)
.toString()
Beware of data loss. Your input has a fourth digit of decimal fraction of a second. That means microseconds. The Instant class can handle that. But the legacy Date class you asked for cannot, and is limited to milliseconds. So any microseconds will be lopped off, truncated to milliseconds.
The terrible legacy classes such as java.util.Date have been given new methods to facilitate converting back and forth between the modern java.time classes. Here we are using Date.from( Instant ) to produce a legacy date from the modern Instant parsed from your input.
Beware that Date has many flaws and problems. Among those is the behavior of its toString method. That method takes the value of the Date which is a moment in UTC, and then applies the JVM’s current default time zone while generating the text. This creates the illusion of that time zone being part of the Date.
I suggest you avoid Date entirely, and use only the java.time classes. But my code here answers the Question as asked.
Also, your desired output format is a terrible one. Instead, use ISO 8601 standard formats for data exchange. For presentation to the user, use DateTimeFormatter.ofLocalizedDateTime. Both of these topics have been addressed many times on Stack Overflow, so search to learn more.
First - congratulations on using the Java 8 time functions - wise choice!
Per your question:
This is the way to convert "LocalDate" to "java.util.Date":
Date myDate = Date.from(localDate.atZone(ZoneId.systemDefault()).toInstant());
... or ...
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
Per the documentation:
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
LocalDate is an immutable date-time object that represents a date,
often viewed as year-month-day. Other date fields, such as
day-of-year, day-of-week and week-of-year, can also be accessed. For
example, the value "2nd October 2007" can be stored in a LocalDate.
This class does not store or represent a time or time-zone. Instead,
it is a description of the date, as used for birthdays. It cannot
represent an instant on the time-line without additional information
such as an offset or time-zone.
So a better choice might be LocalDateTime
In either case, "java.util.Date" automatically has "everything you need". It is a "date/time" object. It stores date and time, irrespective of any time zone.
I am aware that SimpleDateFormat.parse rely on the Calendar API which depends on local JVM timezone (computer's). Assume JVM timezone is IST.
SimpleDateFormat srcDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
srcDateFormat.setTimeZone(TimeZone.getTimeZone("EST"));
Date objDt = srcDateFormat.parse("2018-10-16 11:28:25"); //Time : 21:58:25
From the output it seems it converts from EST to IST(JVM local timezone).
SimpleDateFormat srcDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
srcDateFormat.setTimeZone(TimeZone.getTimeZone("IST"));
Date objDt = srcDateFormat.parse("2018-10-16 11:28:25"); //Time : 11:28:25
It keeps time unmodified in this case. In this case I set timezone same as JVM local timezone.
Please help me to understand the behavior of the parse method. Nevertheless, I am curious to know the reason behind such behavior.
I know that java.util.Date and java.text.SimpleDateFormat legacy classes are obsolete now.
References:
Why SimpleDateFormat.format() and SimpleDateFormat.parse() are giving different time though setting only one TimeZone?
How do I convert date/time from one timezone to another?
SimpleDateFormat parses a string to wrong time
https://www.codeproject.com/Tips/1190426/When-Parsing-Formatting-Dates-in-Java-Make-Sure-Yo
First, you are correct that Date and SimpleDateFormat are legacy classes and now obsolete. So I recommend you don’t use them and use java.time, the modern Java date and time API, instead. Among many advantages it is much more explicit about conversions between time zones, which I would expect to help you understand what the code does.
Second, you are doing a number of things incorrectly or at least inadequately:
Don’t store date-times as strings. Always store them as date-time objects in Java. When you do this, you will never need to convert a date-time string from one zone to another. Instant (a class from java.time) is a point in time and as far as I can see the one you should use here.
Don’t rely in three letter time zone abbreviations. Very many of them are ambiguous, including both of IST and EST, and the latter isn’t a true time zone, so what you get at this time of year (when America/New_York zone uses EDT rather than EST), I don’t know.
And repeating myself, use the modern classes, not the obsolete ones.
Out of curiosity what happened?
An old-fashioned Date represents a point in time independently of time zone (internally it stores its value as a count of milliseconds since the epoch, but this is an implementation detail that we need not know or concern ourselves with).
In your first example your string is parsed into a point in time that corresponds to 16:28:25 UTC, 21:58:25 in India, 12:28:25 in New York or 11:28:25 in Jamaica. I mention Jamaica because it’s one of the few places that happens to use Eastern Standard Time (EST) all year. Most of the locations that use EST only do so in winter, not at this time of year. When you look at the Date in your debugger, the debugger calls toString on the Date to get a string to show you. toString in turn uses the JVM’s time zone for generating the string. In your case it’s Asia/Kolkata, which is why you get 21:58:25.
In the second case the same string is parsed into a point in time that corresponds to 05:58:25 UTC, 11:28:25 in India, 01:58:25 in New York or 00:58:25 in Jamaica. Your debugger again calls toString, which again uses your JVM’s time zone and converts back into 11:28:25 IST. When you parse and print in the same time zone, you get the same time of day back.
Links
EST – Eastern Standard Time / Eastern Time (Standard Time)
Oracle tutorial: Date Time explaining how to use java.time.
Time Zone Converter – Time Difference Calculator, online, practical for converting between Asia/Kolkata, UTC, Kingston (Jamaica), New York and other time zones.
How should I parse this datetime value that is in the PDT timezone?
06/24/2017 07:00 AM (PDT)
I want to maintain the timezone so that I can then represent the time in other timezones depending on the website visitors preferences.
I tried using ZonedDateTime but I get a parse error:
java.time.ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)")
The error is:
java.time.format.DateTimeParseException: Text '06/24/2017 07:00 AM (PDT)' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:582) ... 29 elided
Also, do you agree that I should be using a ZonedDateTime?
Since your format is non-standard, you need to specify it to the parser:
ZonedDateTime.parse(
"06/24/2017 07:00 AM (PDT)",
DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm a (zzz)")
);
The parse method expects a String in a specific format, like 2007-12-03T10:15:30+01:00[Europe/Paris]. As your input is in a different format, you need a DateTimeFormatter.
One detail to notice is that the API uses IANA timezones names (always in the format Continent/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.
The API makes some exceptions with specific IDs and provides some defaults for them. For PDT, it defaults to America/Los_Angeles.
Another detail is that in the example below I used lowercase hh in the pattern: the format has AM/PM indication, so I think that hh is the correct pattern, as its value is from 1 to 12 (the common values when there's the AM/PM indicator).
If you use uppercase HH, it allows values from 0 to 23 (and it's not common to use this with AM/PM), and it will throw an exception if the input contains an hour like 07:00 PM.
So the code will be like:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm a (zzz)");
ZonedDateTime z = ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt);
System.out.println(z);
The output is:
2017-06-24T07:00-07:00[America/Los_Angeles]
But not all the 3-letter timezone names will be recognized by the API and will throw an exception.
Anyway, there are other timezones that also are in PDT (like America/Vancouver) - you can get a list of all by calling ZoneId.getAvailableZoneIds(). If you want to use a different timezone as the default, you can create a set of preferred zones and build a formatter with this set:
Set<ZoneId> preferredZones = new HashSet<>();
// set America/Vancouver as preferred zone
preferredZones.add(ZoneId.of("America/Vancouver"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// pattern
.appendPattern("MM/dd/yyyy hh:mm a (")
// append timezone with set of prefered zones
.appendZoneText(TextStyle.SHORT, preferredZones)
// finish the pattern
.appendPattern(")")
// create formatter
.toFormatter();
System.out.println(ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt));
The API will use the preferred zones set (in this case, America/Vancouver) instead of the default (America/Los_Angeles). The output will be:
2017-06-24T07:00-07:00[America/Vancouver]
It's not clear where the input String's come from. If you can't control their format, then you have no choice: they need to be parsed this way. Then you can convert it to another timezone using the withZoneSameInstant method:
// parse the input string
ZonedDateTime z = ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt);
// convert to another timezone
ZonedDateTime other = z.withZoneSameInstant(ZoneId.of("America/Sao_Paulo")); // 2017-06-24T11:00-03:00[America/Sao_Paulo]
The value of other will be 2017-06-24T11:00-03:00[America/Sao_Paulo].
But if you can control the output, it's always better (IMO) to internally work with UTC (java.time.Instant), and convert to some timezone only when displaying to users:
// convert ZonedDateTime to instant
ZonedDateTime z = // parse input
// convert to UTC (Instant is always in UTC)
Instant instant = z.toInstant();
// internally work with instant (as it's always in UTC)
// convert instant to some timezone only when necessary (like displaying to users)
ZonedDateTime converted = instant.atZone(ZoneId.of("Europe/London"));
The error you get is well covered in the other answers already.
Also, do you agree that I should be using a ZonedDateTime?
Yes and no. Your string should definitely be parsed into a ZonedDateTime. I recommend you convert it to an Instant and store this. Then when you need to present it to a user according to his/her time zone preference, you may either convert the Instant to a ZonedDateTime again or just format it using a DateTimeFormatter with the desired default time zone.
Why do it this way? First, common practice is to store Instants. Some prefer to store just milliseconds since the epoch, I think this some (often misunderstood) performance measure. Certainly such milliseconds I quite unreadable while Instants can be deciphered on eye-sight, at least roughly. The only other alternative I respect is when you know for certain that your application will never need to be concerned with a time zone (does this ever happen?), then sometimes LocalDateTime is used for storage.
If I understand your situation correctly, you need to store the point in time for display into multiple time zones. You don’t need to store the time zone in which the time was originally entered (like PDT, except PDT is not really a full time zone). Instant is time zone neutral, which is one reason I prefer it over storing the time in some time zone, as ZonedDateTime would. Also an Instant is simpler conceptually, and my guess is that it is also simpler implementation-wise.
There are a couple of much better answers here: Best practices with saving datetime & timezone info in database when data is dependant on datetime.
Before Java-8 I got accustomed to always keep anything date/time related as milliseconds since Epoch and only ever deal with human readable dates/times on the way out, i.e. in a UI or a log file, or when parsing user generated input.
I think this is still safe with Java-8, and now I am looking for the most concise way to get a formatted date out of a milliseconds time stamp. I tried
df = Dateformatter.ofPattern("...pattern...");
df.format(Instant.ofEpochMilli(timestamp))
but it bombs out with Unsupported field: YearOfEra in Instant.getLong(...) which I half understand. Now what to use instead of Instant?
LocalDateTime.ofEpoch(Instant, ZoneId) seems wrong, since I don't care to have local time. I just want to see the local time zone when applying the formatter. Internally it should be just the Instant.
The same goes for ZonedDateTime.ofInstant(Instant, ZoneId), I thought to apply the ZoneId only when formatting. But I notice that the DateTimeFormatter does not itself deal anymore with time zones, it seems, so I reckon I need to use one of the above.
Which one is preferred and why? Or should I use yet another way to format an epoch-millis time stamp as a date/time with time zone?
An Instant does not contain any information about the time-zone, and unlike in other places, the default time-zone is not automatically used. As such, the formatter cannot figure out what the year is, hence the error message.
Thus, to format the instant, you must add the time-zone. This can be directly added to the formatter using withZone(ZoneId) - there is no need to manually convert to ZonedDateTime *:
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))
* regrettably, in early Java 8 versions, the DateTimeformatter.withZone(ZoneId) method did not work, however this has now been fixed, so if the code above doesn't work, upgrade to the latest Java 8 patch release.
Edit: Just to add that Instant is the right class to use when you want to store an instant in time without any other context.
The error you have when formatting an Instant using a formatter built with a year or other fields is expected; an Instant does not know which year or month or day it is, it only knows how much milliseconds have elapsed since the Epoch. For the same instant, it could be 2 different days on 2 different places of the Earth.
So you need to add a time zone information if you want to print the day. With an Instant, you can call atZone(zone) to combine it with a ZoneId in order to form a ZonedDateTime. This is very much like an instant, only that it has a time zone information. If you want to use the system time zone (the one of the running VM), you can get it with ZoneId.systemDefault().
To print it, you can use the two built-in formatter ISO_OFFSET_DATE_TIME or ISO_ZONED_DATE_TIME. The difference between the two is that the zoned date time formatter will add the zone id to the output.
Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));
when run on my machine, which has a system time zone of "Europe/Paris", you'll get:
2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00
You can of course build your own formatter if those one do not suit you, using ofPattern or the builder DateTimeFormatterBuilder.
I agree that this is somewhat confusing, especially when compared with it's predecessor Joda DateTime.
The most confusing thing is that the documentation for LocalDateTime says that it is "A date-time without a time-zone", and yet LocalDateTime.ofInstant method takes both an instant and a timezone as parameters.
That said, I think that you can achieve what you want by using Instant and LocalDateTime.ofInstant by using the UTC timezone.
public LocalDateTime millisToDateTime(long millis) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
}