Java: Save date time without time conversion - mysql

I am using spring boot and mysql.
At first, one of my time field using Date type and I gotta use the #JsonFormat(shape=JsonFormat.Shape.STRING, pattern=Constant.DATE_TIME_FORMAT, timezone = "....") to take care of the time conversion due to different timezone. And then I changed it from Date to LocalDateTime to ensure there's no time conversion and remove the timezone property from above annotation.
But I was wrong, in UAT enrivonment, the input data saved in UI(swagger) changed '12:00:00' to '04:30:00' in database.
I then tried to test by adjusting time zone in my machine and also the current time zone in mysql server.
Here's the result after several attemps:
Back-end Timezone, Database Timezone | Result
BE: +8:00, DB: +5:00 | 12h -> 9h
BE: +0:00, DB: +5:00 | 12h -> 17h
BE: +8:00, DB: +0:00 | 12h -> 4h
The data type for table column is time because I just want to save and display time so it doesn't matter which date so I'm using 1970-01-01 as default date.
The problem is I dont want any of time conversion due to the difference between timezone for BE and DB, I want the data saved in database to be exact as the input time on UI (UI: 12h -> DB: 12h).
This is my field in entity:
#ApiModelProperty(example = "1970-01-01 12:00:00")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern=Constant.DATE_TIME_FORMAT)
private LocalDateTime monStartTime;
Question: is there any way that can avoid time conversion despite the difference in timezone between BE and DB?

You can take String as datatype in Backend.
private Sting monStartTime;
No need to change in Database. Database column should be time as you already have that.
Try this. It will not convert your timezone.

Related

MySQL Date column returns from REST call with incorrect time

I have a MySQL table containing a DATETIME column, my_date.
This column is defined in my domain object as type java.util.Date and annotated as Temporal:
#Entity()
#Table(name="Thing")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Thing {
...
#Column(name="my_date", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private java.util.Date myDate;
...
}
The current my_date column value in the db is 2022-07-17 12:34:56, per MySQL Workbench.
I have a pretty straightforward REST endpoint, created using Spring JPA, that for some reason pulls that value as:
...
"myDate": "2022-07-17T19:34:56.000+00:00"
...
Why would the time portion return with a value 7 hours later than is showing in the database?
Update: I'm actually using MariaDB (10.7.3-MariaDB). Not sure if that's relevant here, but it just dawned on me, so mentioning it just in case that somehow could be the cause of the time portion getting offset.
I think it is the timezone problem, you can add code like below to Dockerfile for container of Mysql or you app
RUN rm /etc/localtime && ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
update 1
according to https://javabydeveloper.com/temporal/
The mapping between the Java 8 Date/Time classes and the SQL types is implicit, there is not need to specify the #Temporal annotation.
try remove #Temporal annotation
update 2
Is java.sql.Timestamp timezone specific?
https://stackoverflow.com/a/14070771/16702058
For example: Your local time zone is GMT+2. You store "2012-12-25 10:00:00 UTC". The actual value stored in the database is "2012-12-25 12:00:00". You retrieve it again: you get it back again as "2012-12-25 10:00:00 UTC" (but only if you retrieve it using getTimestamp(..)), but when another application accesses the database in time zone GMT+0, it will retrieve the timestamp as "2012-12-25 12:00:00 UTC".

how to find out the timezone of given time from mysql db in node js

I am quite new to Node js.
I want to know what timezone the given datetime value, which comes from mysql db, it is .
For example
const conference = await mysqlHelper.query(conferenceSql);
// select query
const time = conference[0].created_datetime
// I wonder how to get the timezone from this time variable here.
console.log(time)
// console.log returns '2021-02-23T01:30:00.000Z' to the terminal
mysql value type: datetime
I know I could directly look up the db table instead of figuring out in node js. However, this is a required task for validation in my server.
Thanks a lot !!
Mysql datetime does not store time zone information.
2021-02-23T01:30:00.000Z is displayed in an ISO 8601 format, being in UTC.

MySql Timezone JDBC issue

I am trying to insert a date value in MySql table name person and column name regdate with data type = datetime. I am setting a value e.g. '2019-08-21 20:25:20' but after saving +5:30 hours get added and value which gets stored is '2019-08-22 03:55:20'. Generating the date value using below Java code
Timestamp curDate = Timestamp.valueOf(Instant.now().atZone(ZoneId.of("Asia/Kolkata")).toLocalDateTime());
and then using .setTimestamp(1, curdate); in INSERT query.
I have checked that the timezone of MySql is set to IST (GMT+0530). App Server timezone is also set to IST. But I am not able to understand why +5:30 hours are getting added even if I explictly setting the date value.
I have tried setting timezone in connection string as ?serverTimezone=Asia/Kolkata but didn't work.
But if I run the same code using my local machine connecting same MySql instance, I get no problem and same value gets stored without addition of 5:30 hours. I checked App Server timezone and it is IST.
MySql version - 5.7.17-log
mysql-connector-java - 8.0.15
Am I missing something?
You have a few problems here.
Avoid legacy date-time classes
First of all, you are mixing the terrible legacy date-time classes (java.sql.Timestamp) with the modern java.time classes. Don’t. Use only classes from the java.time packages.
LocalDateTime cannot represent a moment
You are using LocalDateTime to track a moment, which it cannot. By definition, that class does not represent a point on the time line. That class has a date and a time-of-day but intentionally lacks the context of a time zone or offset-from-UTC. Calling toLocalDateTime strips away vital information about zone/offset.
Tools lie
You are likely getting confused by the well-intentioned but unfortunate behavior of many tools to dynamically apply a time zone while generating text to represent the date-time value retrieved from the database. You can avoid this by using Java & JDBC to get the pure unadulterated value from the database.
TIMESTAMP WITH TIME ZONE
You failed to disclose the exact data type of your column in your database. If you are trying to track a moment, use a data type akin to the SQL-standard type TIMESTAMP WITH TIME ZONE. In MySQL 8 that would, apparently, be the TIMESTAMP type according to this doc. (I am a Postgres guy, not a MySQL user.)
In JDBC 4.2 and later, we can exchange java.time objects with the database. So no need to over touch java.sql.Timestamp again.
Unfortunately, the JDBC spec oddly chose to not require support for Instant (a moment in UTC) nor forZonedDateTime(a moment as seen in some particular time zone). The spec does require support for [OffsetDateTime`]2.
Tip: Learn to work in UTC for the most part. Adjust into a time zone only when required by business logic or for presentation to the user.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ; // Capture current moment in UTC.
Write to the database via a prepared statement.
myPreparedStatement.setObject( … , odt ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
View that moment through the wall-clock time used by the people of some particular region (a time zone).
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

Bypass MySQL automatic GMT conversion when selecting TIMESTAMP fields

There's lots of stuff on the internet about converting MySQL timestamps, how it works etc. But what I need is the opposite: knowing that MySQL stores every datetime data as UTC in TIMESTAMP fields, all I want is to direclty retrieve the stored UTC value without MySQL messing around the datetime with system/server/connection timezones.
You see, we've built a simple node.js feeder which reads from several third-part MySQL databases (so I can't change their timezone settings) and save the gathered data to a Elasticsearch, as a "denormalization process". As the original data comes from different timezones, I need to store them in UTC, so I can easily coordinate further GETs and aggregations.
I know I can set the connection timezone on the fly and I know I can change every timestamp field fetched in my node application, but since MySQL engine already stores timestamps in UTC, why should I add any other step if I could simply get it directly, without converting functions or costly data processings?
In a nutshell, I'd like to know: is there a way to bypass MySQL automatic GMT conversion?
MySQL provides a UNIX_TIMESTAMP function which returns a raw integer value. And that isn't subject to timezone conversions at all.
SELECT UNIX_TIMESTAMP( timestamp_col ) ...
But that returns a raw integer, not a datetime. The client would need to do the conversion into a "datetime" type object, if that's needed.
Another option would be to use the CONVERT_TZ function to convert to UTC from the session time_zone setting.
SELECT CONVERT_TZ( timestamp_col, ##session.time_zone,'+00:00')
But, that doesn't really "bypass" timezone conversion. One downside of this approach is if the session time_zone is affected by daylight saving time changes, there's ambiguity with a one hour period each year when the clock "falls back" one hour. e.g. Sunday, Nov 1 2015 2AM CDT transition to Sunday Nov 1 2015 1AM CST. (Converting back from UTC, if we get 1:30 AM in the session time_zone, we don't "know" if that's CDT or CST. And the conversion back to UTC doesn't know which it is either.)
Another option (which I think you already mentioned) is changing the session time_zone to UTC. Then you could just return the timestamp_col value as UTC. You could save the current time_zone setting, and set it back when you are done, e.g.
SET #save_session_time_zone := ##session.time_zone ;
SET time_zone = '+00:00' ;
SELECT timestamp_col ...
SET time_zone = #save_session_time_zone ;
But your client Connector might do some not-so-helpful conversions when the time_zone of the MySQL database session doesn't match the time_zone of the client, like the funky shenanigans the JDBC driver (MySQL Connector/J) does. (That concern isn't limited to returning UTC; that's a concern whenever the time_zone of the client doesn't match the time_zone of the database session.)
It looks like there's no way to get the original UTC value from a MySQL field; every single function uses the timezone setting, be that SYSTEM or any other you configure.
The way MySQL forces you to use a date conversion is, at least, very constraining. For example, say you have a MySQL server set to a timezone with GMT -03:00 and GMT/DST -02:00 and you store a datetime like '2016-07-01 10:00:00'. If you select this value after the DST has ended, you'll get '2016-07-01 09:00:00'.
You can't tell what time it is for sure unless you store the GMT offset separately or you previously know what timezone the server was when it was stored.
We used the second approach. We saved the server timezone and used it to calculate the offset and return an ISO datetime, so future calculations can be made easily.
DROP FUNCTION IF EXISTS `iso_datetime`;;
CREATE FUNCTION `iso_datetime` (
p_datetime TIMESTAMP
) RETURNS VARCHAR(25)
READS SQL DATA
BEGIN
DECLARE _timezone VARCHAR(255) DEFAULT NULL;
DECLARE _offset VARCHAR(6) DEFAULT NULL;
SET _timezone = (SELECT timezone FROM network);
SET _offset = (SELECT SUBSTRING(TIMEDIFF(p_datetime,CONVERT_TZ(p_datetime, _timezone,'UTC')), 1,6));
RETURN CONCAT(DATE_FORMAT(p_datetime, '%Y-%m-%dT%H:%i:%S'), _offset);
END;
In order to do so, you have to load timezone info into MySQL, so the server can calculate the tz offset of the date for you.

In NodeJS/Sequelize/MySQL, how do I insert time as per local time zone on the server?

We are trying to insert datetime values with the following query -
insert into products (name,createdon) values ('product1', now())
We are firing a raw query using sequelize.
The value is being stored in UTC format.
Firing the same query directly from MySQL, stores the value in local time.
We need the value stored in local time and not in UTC. How do we achieve that here?
I also faced same issue. At the time of connection with sequlize and Mysql you can pass timezone. So it will always using this timezone for saving data. Here is what i found:
var sequelize = new Sequelize('database', 'username', 'password', {
timezone: '+05:30', //here you can pass timezone
host: 'host',
logging: true,
});
But this is not a good solution because if you use your application from 2 different timezone then it will use same timezone for both, so data will be incorrect.
Good solution is always use UTC timezone:'00:00' for saving data and while fetching result show in user's appropriate timezone format. It means if user accessing website from US manipulate DateTime in US's format and show, and if your accessing data from India then manipulate DateTime in India's format and show. For this you need to do some DateTime manipulation. For this manipulation you can use Moment.js.