I am trying to persist java.time.LocalDateTime using Hibernate and JPA. I used Jadira Framework ("org.jadira.usertype:usertype.core:3.2.0.GA" & "org.jadira.usertype:usertype.extended:3.2.0.GA"). I created package-info.java file and created #TypeDefs({#TypeDef(defaultForType = java.time.LocalDateTime.class, typeClass = org.jadira.usertype.dateandtime.threeten.PersistentLocalDateTime.class)}) there. I tested the solution and the java.time.LocalDateTime fields are stored/retrieved to my MySQL database in DATETIME columns (almost) correctly.
The only problem is that the values in database are +2 hours to the correct time value from fields in Java. I'm in CEST (UTC+2) so I understood that this is some problem with time zones. I debugged the code of PersistentLocalDateTime and this is what I found.
PersistentLocalDateTime is using org.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper
AbstractTimestampThreeTenColumnMapper has field ZoneOffset databaseZone by default set to ZoneOffset.of("Z") (UTC).
Because it is thinking that my database is in UTC timezone (and the application is in UTC+2) it adds two hours to my time during conversion to database (and subtracts two hours from my time during conversion from database). So in the application I see the correct date and time but in database I not.
I found that a can add parameters to the #TypeDef so I specified them as below:
#TypeDef(defaultForType = LocalDateTime.class, typeClass = PersistentLocalDateTime.class,
parameters = {
#Parameter(name = "databaseZone", value = "+02:00")
}),
but I've got an exception:
java.lang.IllegalStateException: Could not map Zone +02:00 to Calendar
at org.jadira.usertype.dateandtime.threeten.columnmapper.AbstractTimestampThreeTenColumnMapper.getHibernateType(AbstractTimestampThreeTenColumnMapper.java:59)
I debugged a little bit more. AbstractTimestampThreeTenColumnMapper has two methods:
public final DstSafeTimestampType getHibernateType() {
if (databaseZone == null) {
return DstSafeTimestampType.INSTANCE;
}
Calendar cal = resolveCalendar(databaseZone);
if (cal == null) {
throw new IllegalStateException("Could not map Zone " + databaseZone + " to Calendar");
}
return new DstSafeTimestampType(cal);
}
private Calendar resolveCalendar(ZoneOffset databaseZone) {
String id = databaseZone.getId();
if (Arrays.binarySearch(TimeZone.getAvailableIDs(), id) != -1) {
return Calendar.getInstance(TimeZone.getTimeZone(id));
} else {
return null;
}
}
getHibernateType method throws the exception because resolveCalendar method returns null. Why it returns null? Because time zones IDs from java.time.ZoneOffset and java.util.TimeZone does not match. As far as I see the only possible value which match is Z. Any other values causes exceptions.
Is there any way to setup this correctly? Or is it a bug in the Jadira Framework?
It looks like a serious bug. The problem is that jadira.usertype.databaseZone parameter is parsed to ZoneOffset instead ZoneId. This way, resolveCalendar method compares 2 different types Zone and Offset. What is funny, parameter is named databaseZone but it does not contain zone. It contains only offset.
https://github.com/JadiraOrg/jadira/issues/42
https://github.com/JadiraOrg/jadira/issues/43
Related
i all, i have a spring boot application. what i want in specific is to convert a class (that have nestet object field) in his corrispective entity. example:
public class example{
String string;
ObjectExample object;
}
public class ObjectExample{
String oneString;
XMLGregorianCalendar date;
}
this 2 object are also marked in another package as entities, but ovviusly in the ObjectExampleEntity i have Date date instead XMLGregorianCalendar, like this with the example
#Entity
public class example{
String string;
ObjectExample object;
}
#Entity
public class ObjectExample{
String oneString;
Date date;
}
because i have a big model and big entity (this above is only an example) with a lot of nested classes , i use dozer to convert from the model to the class.
consider for example that the repository jpa is only created for the father example class.
i want to know how i can with dozer convert from Date (entity) to XMLGregorianCalendar (model) and reverse. the model and the entity,i repeat are equal. the only difference is the type of the date. thanks
I am assuming:
Since your variable is named date it contains a calendar date (without time of day).
You are tied to XMLGregorianCalendar because of a WSDL outside your control, but you can change type on the entity side.
Based on these assumptions I recommend LocalDate on the entity side. It’s part of java.time, the modern Java date and time API, and represents exactly a date without time of day. The Date class that you used is poorly designed, long outdated and not recommended. Also despite the name a Date never represented a date, but a point in time.
There are more options. I am presenting three.
Option 1: transfer individual fields
From XMLGregorianCalendar to LocalDate:
DatatypeFactory xmlFactory = DatatypeFactory.newInstance();
XMLGregorianCalendar wsDate = xmlFactory
.newXMLGregorianCalendarDate(2019, DatatypeConstants.MARCH, 30,
DatatypeConstants.FIELD_UNDEFINED);
// Validate
if ((wsDate.getHour() != 0 && wsDate.getHour() != DatatypeConstants.FIELD_UNDEFINED)
|| (wsDate.getMinute() != 0 && wsDate.getMinute() != DatatypeConstants.FIELD_UNDEFINED)
|| (wsDate.getSecond() != 0 && wsDate.getSecond() != DatatypeConstants.FIELD_UNDEFINED)
|| (wsDate.getMillisecond() != 0 && wsDate.getMillisecond() != DatatypeConstants.FIELD_UNDEFINED)) {
System.out.println("Warning: time of day will be lost in conversion");
}
if (wsDate.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
System.out.println("Warning: UTC offset will be lost in conversion");
}
// Convert
LocalDate entityDate = LocalDate.of(wsDate.getYear(), wsDate.getMonth(), wsDate.getDay());
System.out.println(entityDate);
The output is in this case:
2019-03-30
From LocalDate to XMLGregorianCalendar:
LocalDate entityDate = LocalDate.of(2019, Month.MARCH, 31);
XMLGregorianCalendar wsDate = xmlFactory.newXMLGregorianCalendarDate(
entityDate.getYear(),
entityDate.getMonthValue(),
entityDate.getDayOfMonth(),
DatatypeConstants.FIELD_UNDEFINED);
System.out.println(wsDate);
2019-03-31
Advantage of this way: It’s pretty straightforward. Disadvantage: You and your reader need to take care that the fields are mentioned in the right order.
Option 2: convert via strings
// Validate as before
// Convert
LocalDate entityDate = LocalDate.parse(wsDate.toXMLFormat());
Result is as before.
XMLGregorianCalendar wsDate
= xmlFactory.newXMLGregorianCalendar(entityDate.toString());
Advantage: it’s brief, and there’s no surprise that the results are correct. Disadvantage: To me it feels like a waste to format into a string only to parse it back.
Option 3: convert via GregorianCalendar and ZonedDateTime
ZonedDateTime zdt = wsDate.toGregorianCalendar().toZonedDateTime();
// Validate
if (! zdt.toLocalTime().equals(LocalTime.MIN)) {
System.out.println("Warning: time of day will be lost in conversion");
}
if (! zdt.getZone().equals(ZoneId.systemDefault())) {
System.out.println("Warning: UTC offset will be lost in conversion");
}
// Finish conversion
LocalDate entityDate = zdt.toLocalDate();
And the other way:
// It doesn’t matter which time zone we pick
// since we are discarding it after conversion anyway
ZonedDateTime zdt = entityDate.atStartOfDay(ZoneOffset.UTC);
GregorianCalendar gCal = GregorianCalendar.from(zdt);
XMLGregorianCalendar wsDate = xmlFactory.newXMLGregorianCalendar(gCal);
wsDate.setTime(DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED,
DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED);
wsDate.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
The validation I present here is a bit simpler but also not quite so strict. If you want strict validation, you can just use the validation from before.
Advantages: I think it’s the official way; at least it uses the conversion methods offered. What I like is that the conversion itself is direct and brief. Disadvantage: When converting to XMLGregorianCalendar we need to set the unused fields to undefined manually, which makes it wordy.
Conclusion
I have presented three options each with their pros and cons. You may also mix, of course, but using a similar conversion both ways is probably less confusing in the end.
Suppose two people run the same code and the first one creates a certificate with createDate and expireDate and a file is valid if
createDate < currentTimeOfTheUserComputer < expireDate .
Now if the first person sends this certificate(suppose for him it is valid) to someone else who is 8 hours behind around the world ,for the second person it will be invalid.In such a scenario how is this problem resolved or is there a policy how it should be if we want the second person to be able to use it without waiting 8 hours?
RFC 5280, which handles X.509 certificates specifies two methods to encode validity times in certificates:
UTCTime
GeneralizedTime
Both rely on the GMT zone as reference. If both implementations use the correct time zone for generation and checking, your scenario should not present a problem.
See RFC 5280 on validity
EDIT: Sorry, I didn't realize that Date always uses UTC Time. So any time you want to get the date, you access it like this:
import java.util.Date;
public class CurrentDateTime {
public static String getCurrDateTime() {
Date date = new Date();
return date;
}
}
public class DateCertificate {
public static void createCertificate() {
Date createDate = CurrentDateTime.getCurrDateTime();
/* Save createDate to file or something */
}
public static boolean certificateValid() {
Date currentDate = CurrentDateTime.getCurrDateTime();
/* Get createDate and expireDate from file or something */
return currentDate.after(createDate) && currentDate.before(expireDate);
}
}
You can use getCurrDateTime() and some other Date methods to get the createDate and expireDate and save them to a file or however you want to store them, then use the certificateValid() method to check if the currentDate is between the two.
Thanks Jim for pointing out my mistake.
Note: This could be easily spoofed/tricked because it is dependent on the time reading from the client machine that is given to the JVM. So do not use this for free trial validation or the like.
Recently there is a process we’re working on taking a lot more memory than expected. Upon checking the heap there are a lot of java.util.LocalTime created. Which can be explained because we do store some data in a timemap with LocalTime as key. Probably there is too much of that data generated from upstream compared to the expected.
However, there is a surprise that also a lot of sun.util.calendar.ZoneInfo are created. We don't use this class; will it generated by LocalTime? But if we check the source code of java.time.LocalTime, I don't see ZoneInfo is used; does anyone have an idea about that?
Sorry for late answer but now I have found some time to inspect the JDK-sources.
If you call LocalTime.now() then java.time will use the system timezone (an implicit dependency).
public static LocalTime now() {
return now(Clock.systemDefaultZone());
}
And then Clock loads ZoneId.systemDefault():
public static Clock systemDefaultZone() {
return new SystemClock(ZoneId.systemDefault());
}
Finally, the class java.util.TimeZone is used to find the default zone.
public static ZoneId systemDefault() {
return TimeZone.getDefault().toZoneId();
}
And if you look into the source of TimeZone.getDefault() then you will see at some depth down the stack:
private static TimeZone getTimeZone(String ID, boolean fallback) {
TimeZone tz = ZoneInfo.getTimeZone(ID);
if (tz == null) {
tz = parseCustomTimeZone(ID);
if (tz == null && fallback) {
tz = new ZoneInfo(GMT_ID, 0);
}
}
return tz;
}
Voilá, the class sun.util.calendar.ZoneInfo will be loaded, too.
I need to check if a given timestamp is today. I am using Joda-Time. Is there a method or a simple way to check this? What Joda-Time class is better suited for this? LocalDate? DateTime?
The date can be compared by single statement so why you need a special function.
when dateTimeis an object of DateTime()
if((dateTime.toLocalDate()).equals(new LocalDate()))
when date is an object of java.util.date
if((new DateTime(date).toLocalDate()).equals(new LocalDate()))
What Joda-time class is better suited for this? LocalDate? DateTime?
The understanding that you need to know what is LocalDate and DateTime.
LocalDate() is an immutable datetime class representing a date without a
time zone. So is not having a time part.
DateTime() is the standard implementation of an unmodifiable datetime
class. Its having all the attributes of the Date, which includes
date, time and timezone.
So if you need to compare both the date and time better go with datetime, if you just need to check the date you must use localDate because the datetime will produce a false if an .equal operator is used, unless the time including the seconds part are same for both the objects.
Here are some simple methods to check if a DateTime is today, tomorrow or yesterday:
public boolean isToday(DateTime time) {
return LocalDate.now().compareTo(new LocalDate(time)) == 0;
}
public boolean isTomorrow(DateTime time) {
return LocalDate.now().plusDays(1).compareTo(new LocalDate(time)) == 0;
}
public boolean isYesterday(DateTime time) {
return LocalDate.now().minusDays(1).compareTo(new LocalDate(time)) == 0;
}
One possibility is to create an interval covering the whole day in question, and then check if the various timestamps are contained in this interval.
Constructing the initial interval could look like:
Interval today = new Interval(DateTime.now().withTimeAtStartOfDay(), Days.ONE);
Then the timestamps could be checked like so:
today.contains(DateTime.now()); // True
today.contains(DateTime.now().minusDays(1)); // False
today.contains(DateTime.now().plusDays(1)); // False
today.contains(someOtherTimeStamp.toDateTime()); // And so on...
The recommended way to do this would be:
DateTime midnightToday = DateTime.now().withTimeAtStartOfDay();
DateTime myDateTime = <whatever>;
if(myDateTime.isAfter(midnightToday)) {
}
I think you need Joda 2.5 to do this, but that should do the trick.
I like #JustinMorris's answer. But I found this even better:
public static boolean isToday(DateTime time) {
return LocalDate.now().equals(new LocalDate(time));
}
public static boolean isTomorrow(DateTime time) {
return LocalDate.now().plusDays(1).equals(new LocalDate(time));
}
public static boolean isYesterday(DateTime time) {
return LocalDate.now().minusDays(1).equals(new LocalDate(time));
}
AFAIK there is no direct method available by which you can check the Date is Today Date or not.
The simplest approach will be constructing two DateTime one with the Timestamp, and another with today Date and then comparing day from dayOfYear() and year from year() but do remember whether both Date are in UTC or in Local Time Zone.
A small sample,
DateTime date = new DateTime(TimeStamp);
DateTime todayDate = new DateTime();
if(date.dayOfYear().get() == todayDate.dayOfYear().get() && date.year().get() == todayDate.year().get())
{
System.out.println("Date is today date");
}
Joda time actually have a method for this:
DateUtils#isToday(ReadablePartial);
DateUtils#isToday(ReadableInstant);
Simplest way I've found:
public boolean isToday(DateTime dateTime) {
return dateTime.withTimeAtStartOfDay().getMillis() ==
new DateTime().withTimeAtStartOfDay().getMillis();
}
I am trying to get two dates from a SQL query, and compare them. So to compare them, I believe I will need to use the "Date" type. Here is what I am trying, I know I am getting the date from the resultSet incorrectly, but I am not sure how to do it.
Date validDate = new Date(0);
Date currentDate = new Date(0);
// query
if (result.next()) {
validDate = (result.getObject("validDate")!=null)?result.getObject("validDate").toDate():"";
currentDate = (result.getObject("currentDate")!=null)?result.getObject("currentDate").toDate():"";
}
if (currentDate > validDate) {
//do something
}
So again, this was my attempt, but I cant seem to get it to run. Thanks in advance.
EDIT: the query has TO_CHAR(column, 'MM-DD-YYYY') on the two dates that I am getting.
EDIT: Now you've mentioned that your query converts the date to a string, stop doing that. You'll end up reparsing it on the calling side - so why perform two conversions pointlessly? Keep string conversions to the absolute minimum - stay in the most appropriate data type wherever possible.
Original answer
You haven't shown what result is, but you probably want something like ResultSet.getDate() to fetch the date values.
Note that your comparison code won't work either because there's no > for Date - you'd need something like:
if (currentDate.after(validDate))
Or fetch the underlying number of millis:
if (currentDate.getTime() > validDate.getTime())
Additionally:
You can't assign "" to a Date variable - a string isn't a Date.
You can just call ResultSet.getDate() and check whether the returned value is null, rather than calling getObject first and then getDate()
Try currentDate.after(validDate)
To compdare dates I always use the before and after methodes of Date.
Some nasty things can happen when accessing dates via the getObject method. You should try to use the rs.getTimestamp (with timeinfo) or the rs.getDate (without timeinfo) methods.
Also, because of the rather complex hierarchy of Date-objects you should compare Dates only using the date1.compareTo(date2) > 0 method.
if your result object is ResultSet, then
Date validDate = result.getTimestamp("validDate");
Date currentDate= result.getTimestamp("currentDate");
// you can add null checks here too....
// you can also use if (currentDate.getTime() > validDate.getTime()){}
if (currentDate.before(validDate)) {
//some code inhere...
}
There are at least three things wrong with your code:
"" is a String literal, so you cannot use it int your ternary expressions to be assigned to a variable of type Date - use null instead so you don't need a ternary
ResultSet.getObject() returns an Object, which does not have a toDate() method. Instead, simply use ResultSet.getDate()
You cannot compare Date instances using a > operator. You have to use the before() and after() methods of the Date class
Taking all this together, the following code might work:
Date validDate = new Date(0);
Date currentDate = new Date(0);
if (result.next()) {
validDate = result.getDate("validDate");
currentDate = result.getDate("currentDate");
}
if (currentDate.after(validDate)) {
//do something
}
The if clause may have to include some extra logic to deal with null values though. It's better to do that than to leave that to implicit conversions, too.