How to parse this date in java? - java

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

Related

Preserving the Timestamp details in java

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.

Trying to parse a datetime in PDT to a ZonedDateTime representation

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.

Java 8 epoch-millis time stamp to formatted date, how?

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

Is String.format() on Android supposed to be timezone-aware?

I use the following code to convert the timestamp of a GPS location to a human-readable form:
String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", location.getTime())
As per the docs, GPS location timestamps should be in UTC. However, the string I get back is in local time (tested on two different devices).
I have tried using the other form of String.format(), which takes an additional Locale argument, and passing it a null locale (which, as per the docs, means "no localization") – still the same. (And the documentation of Locale doesn't mention time zones at all, thus I doubt locales are the issue here.)
My other suspicion was that the GPS stack might not behave as specified, supplying local time instead of UTC. I tested this with
String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", (long) 0)
which returns
1970-01-01 01:00:00
which is the beginning of the epoch plus the timezone offset for CET (the time zone of the device). Thus the offset is clearly added by String.format().
Is String.format() supposed to do any time zone conversion? How can I influence this behavior, i.e. choose which time zone to convert to or suppress conversion altogether?
String.format represent a date/time in default timezone. To format in UTC, use a SimpleDateFormat instead, where you can explicitly set timezone which you want to use:
String formatInUtc(long millis) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT);
df.setTimeZone(TimeZone.getTimeZone("UTC"));
return df.format(new Date(millis));
}
E. g.:
System.out.println(formatInUtc(0L)); // 1970-01-01 00:00:00
By the way, locale and timezone are orthogonal things: locale determines aspects of textual representation (digits, signs, separators, language), while timezone determines how the clock is shifted from UTC.
String.format is not time zone aware. Use Joda time library. The datetime zone and datetimeformat classes in the library will let you format datetime that will be timezone aware. You will get a lot of examples online on how to do that, so I am not getting into details here. :)

Java jackson configuration date

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.

Categories