Convert Long to LocalDateTime [duplicate] - java

This question already has answers here:
Jackson deserialize elasticsearch long as LocalDateTime with Java 8
(2 answers)
Closed 1 year ago.
I receive this error when trying to get response from Api Call:
I receive this field like this from the object:
"createdAt":1620133356550
My Dto has this field, so i can get the value with object mapper:
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createdAt;
And i receive this error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: raw timestamp (1620133356550) not allowed for `java.time.LocalDateTime`: need additional information such as an offset or time-zone (see class Javadocs)
Possible setter of the object:
public void setCreatedAt(long createdAt) {
Instant instant = Instant.ofEpochMilli(createdAt);
LocalDateTime localMark =
instant.atZone(ZoneId.of("Europe/Amsterdam"))
.toLocalDateTime();
this.createdAt = localMark;
}

That's because you're asking jackson to silently convert an apple into a pear and it won't do that. There is no way to do this without additional information.
This: 1620133356550 looks like a milliseconds-since-epoch value.
This represents an instant in time. Instants in time and LocalDateTime are a real guns and grandmas situation: They are almost entirely unrelated, and one cannot be transformed into the other; at least, not without parameters that are simply not present here.
First you need to 'localize' the timestamp: That's milliseconds since some instant in time. Which instant in time? A common format is 'milliseconds since midnight, new years, 1970, in the UTC timezone'. If that's indeed what it is, all you need to do is to say Instant.ofEpochMilli(1620133356550L), and you now have that properly represented (namely, as an Instant).
This still isn't a LocalDateTime though. The problem is: Local to whom?
UTC isn't actually used by anybody except space flight, aviation in general, and computers talking to each other. Unless you're writing software specifically intended to run on the Dragon space capsule and nowhere else, by definition then this isn't yet ready to be put in terms of local date and time.
First you need to transform your instant to a locale. THEN you can have a LocalDateTime.
For example, if you want to know: Okay, so if I walk around Amsterdam at the exact instant as represented by this java.time.Instant I made, and I ask somebody the date and time, what would they say? Then you do:
Instant instant = Instant.ofEpochMilli(1620133356550L);
LocalDateTime localMark =
instant.atZone(ZoneId.of("Europe/Amsterdam"))
.toLocalDateTime();
In case this is some bizarro format where a LocalDateTime is serialized by way of: Take the local date time, turn that into an instant by assuming you're asking the current date and time at that exact instant by asking someone zipping about the international space station, and then turn that into epochmillis and put that on the wire, okay, then, write that. Use ZoneOffset.UTC instead of ZoneId.of("Europe/Amsterdam") in the above code.

Related

Correct java.time representation of XMLGregorianCalendar

I have a XML element with the following content:
<lastModified>2019-10-09T19:20:45.677+02:00</lastModified>
This is mapped to Java's XMLGregorianCalendar.
I need to convert this value in an appropriate java.time instance.
I am a little confused about which java.time class is the "correct" (i.e. lossless) representation of this XMLGregorianCalendar value.
I suppose it should be ZonedDateTime or is OffsetDateTime the better choice?
The String you have ("2019-10-09T19:20:45.677+02:00") is in an ISO format that doesn't even need an extra formatter in order to parse it. The main reason for the use of an OffsetDateTime are the last 6 characters: +02:00, which denote an offset of 2 hours from UTC (more than just one time zone may actually have this offset at the same time).
You can convert this value into the proper java.time instance like this, for example:
public static void main(String[] args) throws DatatypeConfigurationException {
// your example datetime
String lastModified = "2019-10-09T19:20:45.677+02:00";
// create an XMLGregorianCalendar for example purpose
XMLGregorianCalendar xmlGC = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(lastModified);
// print it once in order to see the values
System.out.println("XMLGregorianCalendar: " + xmlGC.toString());
// parse its toString() method to an OffsetDateTime
OffsetDateTime lastModOdt = OffsetDateTime.parse(xmlGC.toString());
// format the content of the OffsetDateTime in ISO standard
System.out.println("OffsetDateTime: "
+ lastModOdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
}
Output:
XMLGregorianCalendar: 2019-10-09T19:20:45.677+02:00
OffsetDateTime: 2019-10-09T19:20:45.677+02:00
This should be correct (lossless enough by losing no information).
I've been struggling with a similar problem all morning and Ole's comments were more insightful than the accepted answer so thought I should post the conclusion I came to.
Technically yes, the exact equivalent to XMLGregorianCalendar is OffsetDateTime (assuming your example, where you have y-m-d h:m:s+z fields populated) as the time zone offset is known but which actual time zone it is is unknown (as per other comments, there are many time zones all with offset +2:00). You can convert it easily like so, without going via an intermediary string:
xmlGC.toGregorianCalendar ().toZonedDateTime ().toOffsetDateTime ()
But I think the real answer is it depends what you want to do with it. In my case, I want to display the date/time to the user. If that is true, do you really care what time zone the date/time happened to be stored as in the XML? The following are both effectively identical, i.e. they both represent the same point in time.
<lastModified>2019-10-09T19:20:45.677+02:00</lastModified>
<lastModified>2019-10-09T20:20:45.677+01:00</lastModified>
If you don't care about the time zone from the XML and just the point in time it reperesents, then the correct answer is Instant, which you can get like
xmlGC.toGregorianCalendar ().toInstant ()
and from there apply whatever time zone the user is in, rather than the time zone from the XML. So for me the right answer was to do:
xmlGC.toGregorianCalendar ().toInstant ().atZone (ZoneId.systemDefault ())

Convert Timestamp as per TimeZone in java

I have a Timestamp object and a TimeZone object
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
TimeZone userTimeZone = preferenceService.getPreferredTimezone();
I want a function which modifies timestamp as per the userTimeZone . The function should return Timestamp. Could someone help ?
Your question is non-sensical. A Timestamp instance represent an instant in time. It doesn't have timezone info and isn't a timezoned stamp.
Instead, when showing a Timestamp to a user, in the step of 'convert this computer-time-kept concept to something to show the user', you'd do the job there. You wouldn't do that by first 'converting the timestamp to this timezone', you'd either do that in one go, or better yet, get to the java.time package as fast as possible because all other time libraries (including those in almost all DB engines) are non-sensical.
In java.time terms, you'd get to a ZonedDateTime, and hten you can convert that to another zone, and from there, potentially via a LocalDateTime, to rendering it to the user.
Given that you said the function must return a Timestamp, the code that uses this method is broken, so fix it there.

How can we decide whether to use ZonedDateTime or LocalDateTime?

Sometimes, we find it is difficult to make judgement, whether to use ZonedDateTime or LocalDateTime, when we want to solve certain date/ time problem.
For instance, given an epoch, we would like to know the day of the week.
We find we can accomplish this task, with either ZonedDateTime or LocalDateTime. Here's the code example
import java.time.*;
public class Main {
public static void main(String[] args) {
long currentTimeMillis = System.currentTimeMillis();
// Yield correct result.
System.out.println("useLocalDateTime -> " + useLocalDateTime(currentTimeMillis));
// Also yield correct result.
System.out.println("useZonedDateTime -> " + useZonedDateTime(currentTimeMillis));
}
public static DayOfWeek useLocalDateTime(long currentTimeMillis) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(currentTimeMillis),
ZoneId.systemDefault()
);
DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
return dayOfWeek;
}
public static DayOfWeek useZonedDateTime(long currentTimeMillis) {
ZonedDateTime zonedDateTime = Instant.ofEpochMilli(currentTimeMillis).atZone(ZoneId.systemDefault());
DayOfWeek dayOfWeek = zonedDateTime.getDayOfWeek();
return dayOfWeek;
}
}
In the above case, is it better to use ZonedDateTime or LocalDateTime? Is there any guideline, so that we can pick up the correct class as tool?
I always have the impression that ZonedDateTime is more "feature rich" than LocalDateTime. Whatever can be accomplished by LocalDateTime, it can be accomplished by ZonedDateTime too, but not vice-versa. Hence, if I get stuck on which to choose, I will go to ZonedDateTime as default. Is that a correct concept?
Do you need to store time data that is attached to a specific time zone, or do you need to process time data that has an associated offset?
If you do, use ZonedDateTime.
If you don't, use LocalDateTime.
Some examples of when I would want to use ZonedDateTime:
I'm parsing an ISO 8601 timestamp with zone information.
I'm looking at data from two different sources located in two physically different locations.
I'm trying to calculate what the day of the week is given a timestamp.
Some examples of when I would want to use LocalDateTime:
I'm assured that my system only needs to care about one time zone - mine.
The data that I'm parsing does not have time stamp information.
I want to know how many seconds have passed between two time stamps. This may get converted to a ZonedDateTime first before it eventualy decants into a Duration if the time stamps are in ISO 8601 format.
Definitely be careful about days of the week across time zones, since the International Date Line can offset the day of the week depending on where you are physically located.
Instead of using System.currentTimeMillis() use ZonedDateTime.now(ZoneId) or Instant.now(). You should almost never need currentTimeMillis() in modern Java. Use the dedicated java.time APIs throughout your application, so that you're working with well-typed data structures instead of primitives like long currentTimeMillis.
given an epoch, we would like to know the day of the week
It's worth recognizing that this isn't a meaningful question without a time zone; at any moment in time there are two (or more?) days of the week in different places on earth. So before we go further we need to ask which time zone(s) do you care about?
Generally speaking, the systemDefault() time zone is not what you want. Instead the caller should provide the time zone they expect. If your program is running locally and only ever needs your machine's clock it may be fine, but the very reason for the split between LocalDateTime and ZonedDateTime is because the system is very often not the correct time zone to be using.
For trivial cases, e.g. a Java process running on your local machine that doesn't care about time zone changes over time, you might correctly use the system time zone. But in such cases it's a good idea to query the system near your main() method and then pass that zone through your application. This makes the application more scalable and testable, if the system zone stops being the right approach down the road.

Java: Fix incorrect timezone in date object

An external API returns an object with a date.
According to their API specification, all dates are always reported in GMT.
However, the generated client classes (which I can't edit) doesn't set the timezone correctly. Instead, it uses the local timezone without converting the date to that timezone.
So, long story short, I have an object with a date that I know to be GMT but it says CET. How can I adjust for this mistake withouth changing my local timezone on the computer or doing something like this:
LocalDateTime.ofInstant(someObject.getDate().toInstant().plus(1, ChronoUnit.HOURS),
ZoneId.of("CET"));
Thank you.
tl;dr ⇒ use ZonedDateTime for conversion
public static void main(String[] args) {
// use your date here, this is just "now"
Date date = new Date();
// parse it to an object that is aware of the (currently wrong) time zone
ZonedDateTime wrongZoneZdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
// print it to see the result
System.out.println(wrongZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// extract the information that should stay (only date and time, NOT zone or offset)
LocalDateTime ldt = wrongZoneZdt.toLocalDateTime();
// print it, too
System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// then take the object without zone information and simply add a zone
ZonedDateTime correctZoneZdt = ldt.atZone(ZoneId.of("GMT"));
// print the result
System.out.println(correctZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
Output:
2020-01-24T09:21:37.167+01:00[CET]
2020-01-24T09:21:37.167
2020-01-24T09:21:37.167Z[GMT]
Explanation:
The reason why your approach did not just correct the zone but also adjusted the time accordingly (which is good when desired) is your use of a LocalDateTime created from an Instant. An Instant represents a moment in time which could have different representations in different zones but it stays the same moment. If you create a LocalDateTime from it and put another zone, the date and time are getting converted to the target zone's. This is not just replacing the zone while keeping the date and time as they are.
If you use a LocalDateTime from a ZonedDateTime, you extract the date and time representation ignoring the zone, which enables you to add a different zone afterwards and keep the date and time as it was.
Edit: If the code is running in the same JVM as the faulty code, you can use ZoneId.systemDefault() to get the same time zone as the faulty code is using. And depending on taste you may use ZoneOffset.UTC instead of ZoneId.of("GMT").
I am afraid you will not get around some calculations here. I'd strongly suggest to follow an approach based on java.time classes, but alternatively you might use the java.util.Calendar class and myCalendar.get(Calendar.ZONE_OFFSET) for those calculations:
https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#ZONE_OFFSET

Is there a simple way to change a timestamp value containing seconds and ms to a timestamp value having hours and minutes?

So I have an object ('Task') that has an attribute 'Start Date' which is basically a Timestamp object. So this date is in this format 'YYYY/MM/dd hh:mm:ss:ms'. But for a test case I am authoring, I need this date to be in this format 'YYYY/MM/dd hh:mm'. Also it needs to be a timestamp object as I have to set this value back to the 'Task' object.
I have tried several approaches including the snippet shown below:
SimpleDateFormat formatter = new SimpleDateFormat("YYYY-MM-dd hh:mm");
if (task.getStartDate() != null) {
String newDate = formatter.format(task.getStartDate());
Date date = formatter.parse(newDate);
task.setStartDate(new Timestamp(date.getTime()));
}
I expected the value of the timestamp to be in the format '2018-12-30 09:54' but it resulted in '2018-12-30 09:54:00.0'. So the questions that I have in mind is:
Is there a way to not consider the seconds and millis in the Timestamp object?
If no, then, is the snippet provided an efficient way to update the Timestamp object?
TL;DR
Avoid the Timestamp class if you can. It’s poorly designed and long outdated.
To answer your questions, no, a Timestamp hasn’t got, as in cannot have a format (the same holds true for its modern replacement, Instant (or LocalDateTime)).
Under all circumstances avoid SimpleDateFormat and Date. The former in particular is notoriously troublesome, and both are long outdated too.
Don’t put a format into your model class
You should not want an Instant nor a Timestamp with a specific format. Good practice in all but the simplest throw-away programs is to keep your user interface apart from your model and your business logic. The value of the Instant object belongs in your model, so keep your Instant or Timestamp there and never let the user see it directly. I hope that it’s clear to you that 2018-12-30 09:54 and 2018-12-30 09:54:00.0 represent the same value, the same Timestamp. Just like 17, 0017 and 0x11 represent the same integer value. When you adhere to what I said, it will never matter which format the Instant has got.
Whenever the user should see the date and time, this happens in the UI, not in the model. Format it into a String and show the string to the user. Similarly if you need a specific format for persistence or exchange with another system, format the Instant into a string for that purpose.
java.time and JDBC 4.2
Also for exchange with your database over JDBC, provided that you’ve got a JDBC 4.2 compliant driver, prefer to use a type from java.time over Timestamp. If the datatype on the database side is timestamp with time zone, very clearly recommended for a timestamp, pass an OffsetDateTime like
OffsetDateTime dateTime = yourInstant.atOffset(ZoneOffset.UTC);
yourPreparedStatement.setObject(4, dateTime);
Use setObject, not setTimestamp. Some drivers accept the Instant directly, without conversion to OffsetDateTime. If on the database side you need a mere timestamp (without time zone), use LocalDateTime in Java instead and pass one to setObject in the same way as above.
PS There are errors in your format pattern string
In a format pattern string, uppercase YYYY is for week based year and only useful with a week number. For year use either uuuu or lowercase yyyy. Similarly lowercase hh is for hour within AM or PM from 01 through 12 and only useful with an AM or PM marker. For hour of day from 00 through 23 you need uppercase HH. These errors will give you incorrect dates and times in most cases. Using the wrong case of format pattern letters is a very common mistake. SimpleDateFormat generally doesn’t mind, it just gives incorrect results. The modern DateTimeFormatter does a somewhat better job of notifying you of such errors.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related questions
Formatting timestamp in Java about getting rid of the .0 decimal on the second of a Timestamp.
timestamp formatting in scala [duplicate] about getting a Timestamp with only date and hour (no minute, second or fraction of second).
java parsing string to date about uppercase Y for year in a format pattern string.
Comparing two times in android about lowercase h for hour of day in a format pattern string.

Categories

Resources