SpringBoot JPA time stored in MySQL in local time zone - java

I've written a class which looks like this
#Entity
class Employee {
#Column
private String name;
#Column
private Date joinedDate;
}
Now, I assign value to joinedDate by doing new Date(). When I print this date, java will add the timezone correction to this. But Java will internally store time since epoch with no timezone. Let's say the local date was Wed Nov 29 18:43:43 IST 2017
When I store this object into database, what I expected to be stored was the time in GMT/UTC Zero timezone. (or at least, storing the number of milliseconds since epoch). But the value stored is Nov 29 18:43:43.
Which is wrong value. Because it's not storing the UTC time and it's not storing the timezone either. I expect within my application and DB dates should be stored in UTC timezone.
I would like to store UTC value while reading and writing instead of timezone where the application is running. My application could be running in different timezone but using the same database.
How can I achieve this?

I think you must use the preferred type for java 8 java.time.ZonedDateTime that can storage nanosecond precisition and timezone. Also be sure that in your database the type is TIMESTAMP.
You can check out examples in this page: enter link description here

By default java uses the machine timezone for date and calendar, if you want to change it then you have the follow options
Change JVM timezone
java -Duser.timezone=Europe/Sofia com.acme.Main
Create a date instance with a different timezone
Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime()
Use java.time api
Date convertedDatetime = Date.from(datetime.atZone(ZoneId.off("UTC").toInstant());
Obs: your observation is Right, the milliseconds since epoch have not timezone but you are using a date at the database, then it does.
Update
I found that answer , it promises to change only hibernate jdbc charset

Related

Java, Oracle db - Timezone issues after trunc(nvl) strips timestamp from a datetime object

Trying to fix a bug in our reporting. Currently the issue is as stands:
At 9:45PM on 2/22 in PST someone submits a work order.
It hits our Oracle Database and normalizes to EST (our db is in EST, but we work with clients all over US).
In iReport, we are using the following:
trunc(nvl(ls.date_occurred,ls.date_created)) between TRUNC($P{DATE_FROM}) AND TRUNC($P{DATE_TO})
This STRIPS the timestamp off of the datetime object, so when the report is generated it does not save the hours, only the date which is now 2/23 (at 12:25 AM respectively).
This obviously throws off our reporting feature. All of the data seems to be correct except this date offset that is generated a day after because of the timezone difference, and the adjusted data not having a timestamp asociated with it.
Does anyone have another way to adjust for datetime without using a function that strips the timestamp off of the date?
As I understand your from/to dates are not in EST which makes the discrepancy between the dates you require in your report to the date in you Database. In order to get the correct records instead if truncating the dates you need to adjust the requested to/from dates according to the timezone of the request (If you request from PST timezone first convert the dates to EST then make the query)
also, you can look at : TIMESTAMP WITH TIME ZONE Datatype
https://docs.oracle.com/cd/B19306_01/server.102/b14225/ch4datetime.htm
Turns out that I need to adjust for the timezone of date_created inside the nvl, because date_occurred is trunced whereas date_created is not. This causes data loss.

Save TimeZone with Date in mongodb

I have java.util.Date field in my document class.
E:g:
#Document(collection = "testdoc")
public class TestDoc {
#Id
String id;
Date startDate;
}
Even if I set the Date with UTC and IST, it always save in my collection as below,
"startDate" : ISODate("2015-08-21T18:30:00.000Z")
How can I save the time zone also in mongo collection? What does Z stand in this case?
The 'Z' signifies that the time is stored in UTC. Mongo internally converts all local representations of time in UTC before storing. However, one suggestion would be to store the time along with the timezone which is received from your application. You can later reconstruct the local time from the UTC time and the timezone in your application logic.
Please go through this link. They have given an example on how to model local time data using JavaScript.
https://docs.mongodb.com/v3.2/tutorial/model-time-data/
Do the conversion before storing and save as UTC always. Then reconvert it in the timezone you want before displaying.
If you desperately want to store the timezone with the offset you may have to deal with it as a separate string in db, but it cannot go with date field for MongoDB.
As currently MongoDB doesn't allow saving of timezone.
Below is the open JIRA issue or the same.
https://jira.mongodb.org/browse/SERVER-6310
Dates in MongoDB are stored in UTC. There isn't timestamp with a timezone data type like in some relational databases. Applications that need to access and modify timestamps, based on local time, should store the timezone offset together with the date and offset dates on an application level.
In the MongoDB shell, this could be done using the following format with JavaScript:
let now = new Date();
db.page_views.save({date: now,
offset: now.getTimezoneOffset()});
Then you need to apply the saved offset to reconstruct the original local time, as in the following example:
let record = db.page_views.findOne();
let localNow = new Date( record.date.getTime() - ( record.offset * 60000 ) );
I guess here you'll find a good guideline how to handle timestamps in different scenarios for different data language independent.
As it's recommendet in the document, ALWAYS use UTC to save the data, even it is local timezone. If necessary, save the time zone in a seperate field ( JSON or xml) and follw the format guideline ISO 8601. ( like you did but there are many possible representations)
As far as I know, correct me if i'm wrong, JSOn does not deal with specific date formats but JS does. Microsoft docs recommends the followind format bases on ECMA YYYY-MM-DDTHH:mm:ss.sssZ this
The Z in the Timestamp shows that it's UTC format with zero offset +00:00
if nothing else is added.
If you want to use the "Z" notation, you have to add or substract the offset within the timestamp instead of writing the zero-offset and add the offset at the end.
I recommend you to follw the w3c guideline because it covers different scenatios for different time saving usecases.
Hope this helps

How to refactor my date/time to allow for setting dates for my users based on their zone?

My code currently uses java dates.
Mysql has column datetime.
I want to allow users to set their timezones, and then in my application I will convert the dates to reflect their timezones.
I am using Spring mvc.
How can I do this?
The Datetime type in MySQL does not have timezone associated with it. Instead, by default Connector/J (the MySQL JDBC Driver) gets the date using the server timezone. There was a setting for changing that, but I don't know if it is documented, I had to look through the source to see how it works.
I could save the user's timezone it a different column as VARCHAR, and use that when creating the date objects. If you don't want to change all your data, you can populate the time_zone column with the server default timezone, and then gradually change the timezones for users. Using the following code you can get the UTC Timestamp. The Calendar/Date objects are quite a mess, an you would be better using Joda Time.
ResultSet rs = null;
Date date = rs.getDate("date");
TimeZone userTimeZone = TimeZone.getTimeZone(rs.getString("time_zone"));
Calendar c = Calendar.getInstance(userTimeZone);
c.set(1900 + date.getYear(), date.getMonth(),
date.getDate(), date.getHours(),
date.getMinutes(), date.getSeconds());
//what you are actually interested in
long utcTimestamp = c.getTimeInMillis();
The common strategy is to store date and time information in the database using UTC (+0) and then convert to/from the users time zone preference in the GUI layer.

Query a mysql DATETIME with a given timezone in Java

I am trying to query a mysql DATETIME from Java. I know the the time zone of the server, but I cannot pull the datetime out with the time zone as I would expect.
ResultSet rs = st.executeQuery(...);
Date d1=rs.getTime(i, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
Date d2=rs.getTime(i, Calendar.getInstance(TimeZone.getTimeZone("PST")));
System.out.println("d1: "+d1.getTime());
System.out.println("d2: "+d2.getTime());
This leaves me with:
d1: 40258000
d2: 40258000
Am I missing something basic here?
ResultSet.getDate() does take the Calendar into account. But I cant use it because it truncates the time info. It's still strange ResultSet.getTime() wouldn't handle any timezone conversions.
The documentation states:
This method uses the given calendar to construct an appropriate millisecond value for the time if the underlying database does not store timezone information.
So perhaps the database does store time zone information in this case?
What does your value in the database look like, and what is it supposed to represent?
An alternative approach would be letting the MySQL server convert the time zone:
SELECT CONVERT_TZ(timefield,'PST','UTC') AS tf
for example would convert the DATETIME value in the field timefield from PST to UTC time (and then returning it as tf).
getTime on the Date class returns the epoch time according to http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Date.html
so it would adjust out your timezone. I think that page implies you should use the timezone adjusted calendar to get your local time.

Before writing a Java Date to an SQL TIMESTAMP column, does JDBC translate the date from the JVM time zone to the database session time zone?

Before writing a Java Date to an SQL TIMESTAMP column, does JDBC translate the date from the Java virtual machine time zone to that of the database session?
For example, suppose the Java virtual machine time zone is UTC and the database session time zone is UTC-5. If a Java program attempts to store 2000-01-01 00:00:00 by passing it to PreparedStatement#setTimestamp(int, Timestamp), according to the JDBC standard, will the database store TIMESTAMP '2000-01-01 00:00:00' or TIMESTAMP '1999-12-31 19:00:00'?
No, JDBC is just an API on how the client can access the database. For timestamp storage, this will have to be dependent by the organisation that writes their database drivers that conforms to the JDBC API standard.
Here's an implementation of MySQL's implementation of PreparedStatement. They seem to take Java's JVM timezone to MySQL Timezone (check the setTimestampInternal() method).
Now my requirement is that it should store the value in GMT/UTC irrespective of the timezone of the JVM. Is there a way to set the timezone on the fly and then to unset it once I'm done with JDBC?
Edit:
Ok, I found a way around that issue. Did the following
TimeZone default = TimeZone.getDefault();
try
{
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
//Do stuff with JDBC
}
finally
{
TimeZone.setDefault(default);
}
You can use overloaded setTimestamp setter accepting Calendar instance to specify timezone
Sample (If you're using Joda datetime):
org.joda.time.DateTime sendDateUTC = new DateTime( DateTimeZone.UTC ).withMillis( millis );
statement.setTimestamp (1, sendDateUTC, sendDateUTC.toGregorianCalendar() );
As per javaDoc:
Sets the designated parameter to the given java.sql.Timestamp value, using the given Calendar object. The driver uses the Calendar object to construct an SQL TIMESTAMP value, which the driver then sends to the database. With a Calendar object, the driver can calculate the timestamp taking into account a custom timezone. If no Calendar object is specified, the driver uses the default timezone, which is that of the virtual machine running the application.
void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal)
throws SQLException;
The spec is goofy. The java.util.Date stores milliseconds from epoch in the GMT reference frame. Java.sql.Timestamp is a Date plus nanoseconds in the same reference frame. All the non-deprecated getters and setters use the GMT reference frame. For any sort of sanity, the default time zone for a storing a Timestamp should be GMT.
In a multi-tiered application, the front-end, the driver, and the database server could all be in different time zones. Some of the tiers could be in different time zones at the same time; for instance, if you are doing internet load-balancing across a continent, or if you have a mobile app connecting to a central server. A cloud operating environment would be much the same scenario where you have no idea where the JDBC driver will be running, nor any guarantee that will never change.
The only way I know to achieve consistency in these environments is to only use the parameter setter and ResultSet getter that accept a Calendar, and make sure every app that accesses the data uses the some calender, preferrably GMT or UTC.

Categories

Resources