I am facing an issue with MySql Select statement for date field with Day Light Saving.
Cause: java.sql.SQLException: HOUR_OF_DAY: 2 -> 3
The only solution worked for is setting serverTimezone=GMT-6 in MySql Connection String. But this I will need to change when daylight saving is not there.
If I am trying to set serverTimezone=America/Denver then it give same error.
Cause: java.sql.SQLException: HOUR_OF_DAY: 2 -> 3
My Database time zone is America/Denver
My project is Jersey project with Java 8 and Mybatis.
I tried multiple suggested solution but nothing is working concrete. Please guide me on this.
You should be tracking moments in your database in a column of type TIMESTAMP in MySQL version 8. This type is akin to the SQL standard type TIMESTAMP WITH TIME ZONE.
Using JDBC 4.2 or later, exchange java.time objects with your database.
ZoneId z = ZoneId.of( "America/Denver" ) ;
LocalDate ld = LocalDate.of( 2020 , Month.JANUARY , 23 ) ;
LocalTime lt = LocalTime.of( 2 , 0 ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
The ZonedDateTime class automatically adjusts for anomalies such as Daylight Saving Time (DST). If 2 AM does not exist on a certain date on a certain date with a “Spring ahead” DST cutover, the time-of-day is adjusted to 3 AM.
Oddly, JDBC 4.2 requires support for OffsetDateTime but not the more commonly used Instant and ZonedDateTime. So convert.
myPreparedStatement.setObject( … , zdt.toOffsetDateTime() ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
ZonedDateTime zdt = odt.withZoneSameInstant( z ) ;
Notice that using this approach means we don’t care about the current default time zone on The server or the client, nor do we care about the current default time zone in your session.
Related
My Oracle table define with date type for one of the date column requestedDate.
While persisting, I am setting the value like below
.setRequestedDate(Date.valueOf(LocalDate.now())))
And JPA entity defined like below,
#Column(name = "REQ_DT")
#Temporal(TemporalType.DATE)
private Date requestedDate;
I am expecting value like "2022-05-12", but it gets stored like "2022-05-12 00:00:00.0"? I want to get rid of the timestamp when it gets inserted into the database.
Do I need to change the date definition in Oracle DB like creating view to truncate the timestamp?
The Oracle DATE data type always includes a time component. If you don't specify one, then midnight is assumed. If you don't want the time portion to be displayed, then use the to_char function or some other native function in your programming language of choice in your application to control the display when the date is retrieved at runtime:
select to_char(sysdate,'YYYY-MM-DD') from dual;
TO_CHAR(SY
----------
2022-11-24
Oracle DATE = SQL standard TIMESTAMP WITHOUT TIME ZONE
The DATE type in Oracle database in misnamed. As others noted, that type represents a date with a time-of-day. So values in a column of this type always have a time-of-day.
Furthermore, that type lacks the context of a time zone or offset-from-UTC.
You said:
I want to get rid of the timestamp when it gets inserted into the database.
You cannot.
JDBC
This Oracle DATE type is equivalent to the SQL standard type TIMESTAMP WITHOUT TIME ZONE.
While I do not use Oracle Database, I expect the following code works when using JDBC 4.2 or later.
LocalDateTime
The Oracle type DATE matching in Java is java.time.LocalDateTime.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
LocalDate
Extract your date-only value as a java.time.LocalDate.
LocalDate ld = ldt.toLocalDate() ;
Today
Note that a time zone is involved in determining the current date. For any given moment, the date varies around the globe by time zone, “tomorrow” in Tokyo while simultaneously “yesterday” in Toledo.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ; // Or ZoneId.systemDefault()
LocalDate ld = LocalDate.now( z ) ;
Write today’s date to Oracle DATE column.
LocalDateTime ldt = ld.atStartOfDay() ; // Assign a time-of-day of 00:00:00 to the date-only `LocalDate` object, returning a `LocalDateTime` object.
myPreparedStatement.setObject( … , ldt ) ;
If you want to insert value without time component, then truncate it. You can't get rid of it because DATE datatype - in Oracle - always contains both date and time
Have a look at the following example:
SQL> create table test (id number, datum date);
Table created.
Altering the session, just to display full format while selecting DATE datatype values:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SYSDATE is a function that returns current date and time; as you can see, DATE_ONLY contains date component, while time is set to midnight (but it is still here):
SQL> select sysdate value_with_date_and_time,
2 trunc(sysdate) date_only
3 from dual;
VALUE_WITH_DATE_AND DATE_ONLY
------------------- -------------------
25.11.2022 07:11:21 25.11.2022 00:00:00
If you select such values into a table:
SQL> insert into test (id, datum)
2 select 1, sysdate from dual union all
3 select 2, trunc(sysdate) from dual;
2 rows created.
Select from it:
SQL> select * from test order by id;
ID DATUM
---------- -------------------
1 25.11.2022 07:11:48 --> both date and time
2 25.11.2022 00:00:00 --> also date AND time, but time is set to midnight
If you want to display values without time, either alter session and set another format model, or use to_char function; once again: this will just display date only, but value - stored in the table - will still have both date and time:
SQL> select id, to_char(datum, 'dd.mm.yyyy') display_date
2 from test order by id;
ID DISPLAY_DA
---------- ----------
1 25.11.2022
2 25.11.2022
SQL>
So I have:
Date startDate which is Sun Mar 27 17:32:01 EEST 2022
and
String period which is PT240H
And I need to generate a new date based on those 2 values. I need to add that 240H period to the startDate. The 240H meaning 10 days which I need to add to startDate and I will eventually need to have a new date which should be Wed Apr 6 17:32.01 EEST 2022.
PS. I am new to Java, hopefully I don't ask stupid things.
tl;dr
java.util.Date.from(
myJavaUtilDate
.toInstant()
.plus( Duration.parse( "PT240H" ) )
)
Details
Putting together those posted Comments…
You are using terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310. Avoid using Date, Calendar, and such.
If handed a java.util.Date object, immediately convert to its replacement class, java.time.Instant. Use new conversion methods added to the old classes.
Instant instant = myJavaUtilDate.toInstant() ;
Parse your input string in standard ISO 8601 format as a Duration object.
Duration d = Duration.parse( "PT240H" ) ;
Add to our Instant to produce a second Instant, per immutable objects.
Instant later = instant.plus( d ) ;
You said:
The 240H meaning 10 days
Incorrect, 240 hours is not necessarily 10 days. Adding a value of 240 hours may or may not result in a moment ten days later, if you adjust into a time zone. Some dates in some time zones vary in length, running 23, 23.5, 25, or other numbers of hours long.
And be aware that both java.util.Date and Instant represent a moment as seen in UTC, that is, with an offset of zero hours-minutes-seconds. Unfortunately, the Date#toString method dynamically applies the JVM’s current default time zone while generating its text — giving a false illusion. This confusing behavior is one of the many design flows in the legacy date-time classes.
If you must interoperate with old code not yet updated to java.time, you can convert back to Date. But I strongly recommend moving away from these legacy classes ASAP.
java.util.Date date = Date.from( someInstant ) ;
Example code
FYI, EEST is not a time zone. Such 2-4 letter pseudo-zones indicate whether Daylight Saving Time (DST) is in effect, and hint at possible time zones. These should be used only for presentation to the user, never in your business logic, data storage, nor data exchange.
Real time zones are named in format of Continent/Region such as Africa/Casablanca and Asia/Tokyo.
The pseudo-zone EEST implies many different time zones. In this example code I use the real time zone "Europe/Bucharest". I am guessing that is your zone, given your user profile.
First we need to recreate your moment reported by Date#toString as ‘Sun Mar 27 17:32:01 EEST 2022’.
// Recreate original conditions.
LocalDate ld = LocalDate.of( 2022 , Month.MARCH , 27 ); // Sun Mar 27 17:32:01 EEST 2022
LocalTime lt = LocalTime.of( 17 , 32 , 1 );
ZoneId z = ZoneId.of( "Europe/Bucharest" );
TimeZone.setDefault( TimeZone.getTimeZone( z ) );
ZonedDateTime zdtStarting = ZonedDateTime.of( ld , lt , z );
Instant then = zdtStarting.toInstant();
java.util.Date startingPoint = Date.from( then );
Convert from legacy class to modern.
Instant instant = startingPoint.toInstant();
Add your desired 240 hours. Adjust into a time zone to obtain a ZonedDateTime, so we can better see its true meaning.
Duration duration = Duration.parse( "PT240H" );
Instant later = instant.plus( duration );
Date endingPoint = Date.from( later );
ZonedDateTime zdtLater = later.atZone( z );
Dump to console.
System.out.println( "-------| Start |--------------------" );
System.out.println( "zdtStarting = " + zdtStarting );
System.out.println( "startingPoint = " + startingPoint );
System.out.println( "instant = " + instant );
System.out.println( "-------| End |--------------------" );
System.out.println( "later = " + later );
System.out.println( "endingPoint = " + endingPoint );
System.out.println( "zdtLater = " + zdtLater );
When run.
-------| Start |--------------------
zdtStarting = 2022-03-27T17:32:01+03:00[Europe/Bucharest]
startingPoint = Sun Mar 27 17:32:01 EEST 2022
instant = 2022-03-27T14:32:01Z
-------| End |--------------------
later = 2022-04-06T14:32:01Z
endingPoint = Wed Apr 06 17:32:01 EEST 2022
zdtLater = 2022-04-06T17:32:01+03:00[Europe/Bucharest]
Input to my method will be a String containing a date in UTC. I need to compare the input date with current date and time and check the difference between two dates. The result should be in days.
I tried the following with no success.
String dateString = "2019-06-18T16:23:41.575 UTC";
final DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'").withZone(ZoneId.of("UTC"));
OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
System.out.println("======================:"+parsedDate.format(formatter1));
OffsetDateTime currentUTC = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println("Until (with crono): " + parsedDate.until(currentUTC, ChronoUnit.DAYS));
I need the result in an int (i.e., number of days).
The line OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1); throws an exception with the following stack trace:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2019-06-18T16:23:41.575 UTC' could not be parsed: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1959)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1894)
at java.base/java.time.OffsetDateTime.parse(OffsetDateTime.java:402)
at thiagarajanramanathan.misc.App.main(App.java:86)
Caused by: java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:370)
at java.base/java.time.format.Parsed.query(Parsed.java:235)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1890)
... 3 more
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {InstantSeconds=1560875021},ISO,UTC resolved to 2019-06-18T16:23:41.575 of type java.time.format.Parsed
at java.base/java.time.ZoneOffset.from(ZoneOffset.java:348)
at java.base/java.time.OffsetDateTime.from(OffsetDateTime.java:359)
... 5 more
As you can see from this thread: Unable to obtain OffsetDateTime from TemporalAccessor
I changed the following lines:
//OffsetDateTime parsedDate = OffsetDateTime.parse(dateString, formatter1);
ZonedDateTime parsedDate = ZonedDateTime.parse(dateString, formatter1);
When your code is run with this modification, I could get the following results
for "2019-06-18T16:23:41.575 UTC" :
======================:2019-06-17T16:23:41.575 UTC
Until (with crono): 0
Since it's less than 24 hours, it returns 0
for "2019-06-17T16:23:41.575 UTC" :
======================:2019-06-17T16:23:41.575 UTC
Until (with crono): 1
Similarly, since it's over 24 hours but under 2 days, it returns 1.
I think this is what you want. Please try it and let me know if this works for you.
Parsing
I would simplify the parsing if your input by getting it to comply with the ISO 8601 standard.
String input = "2019-06-18T16:23:41.575 UTC".replace( " UTC", "Z" ) ;
Instant instant = Instant.parse( input ) ;
Days as 24-hour chunks
If your definition of elapsed days is 24-hour chunks of time, use Duration.
Duration d = Duration.between( instant , Instant.now() ;
long days = d.toDays() ;
Days according to calendar
If you want a count of days elapsed as seen on the calendar, meaning dates rather than 24-hour chunks of time, you must specify a time zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;
Extract the dates.
LocalDate start = zdt.toLocalDate() ;
LocalDate stop = now.toLocalDate() ;
long days = ChronoUnit.DAYS.between( start , stop ) ;
The difference between an time zone and an offset
You have got two good answers already. You are touching on an interesting and a bit tricky part of java.time, so I should like to make my contribution too. My key point is that a time zone and a UTC offset are not the same. To obtain an OffsetDateTime you need an offset. You provide a time zone through the call .withZone(ZoneId.of("UTC")) on the formatter, but it doesn’t help you. Yes, you and I know that UTC is the base of all offsets and therefore itself defines an offset of 0. But Java didn’t discover that from your code.
I admit I was surprised to discover that the following simple change was enough that your code runs on Java 9:
final DateTimeFormatter formatter1
= DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS 'UTC'")
.withZone(ZoneOffset.UTC);
However on Java 8 I still get the same exception as before. The output I got on Java 9.0.4 was:
======================:2019-06-18T16:23:41.575 UTC
Until (with crono): 0
The only change is that I am now passing a ZoneOffset rather than a ZoneId object to withZone (this is possible because ZoneOffset is a subclass of ZoneId).
A formatter that works on Java 8 too is one where we supply a default offset. For that we need a DateTimeFormatterBuilder:
final DateTimeFormatter formatter1 = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendLiteral(" UTC")
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
Yet another and perhaps simpler option would be to parse into a LocalDateTime first (which requires neither offset nor time zone) and then convert to OffsetDateTime by calling .atOffset(ZoneOffset.UTC).
I've switched to threeten for date times but I've still got a 3rd party tool that uses joda to write timestamp with timezone to the database and I need to convert from one to the other.
What's the best way?
As a workaround I tried DateTime.parse(zdt.toString) but it falls over because joda doesn't like the zone format
Invalid format: "2015-01-25T23:35:07.684Z[Europe/London]" is malformed at "[Europe/London]"
Please notice that using DateTimeZone.forID(...) is not safe, which might throw DateTimeParseException as usually ZoneOffset.UTC has a ID "Z" which cannot be recognized by DateTimeZone.
What I would recommend in order to convert ZonedDateTime to DateTime is:
return new DateTime(
zonedDateTime.toInstant().toEpochMilli(),
DateTimeZone.forTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone())));
ZonedDateTime zdt =
ZonedDateTime.of(
2015, 1, 25, 23, 35, 7, 684000000,
ZoneId.of("Europe/London"));
System.out.println(zdt); // 2015-01-25T23:35:07.684Z[Europe/London]
System.out.println(zdt.getZone().getId()); // Europe/London
System.out.println(zdt.toInstant().toEpochMilli()); // 1422228907684
DateTimeZone london = DateTimeZone.forID(zdt.getZone().getId());
DateTime dt = new DateTime(zdt.toInstant().toEpochMilli(), london);
System.out.println(dt); // 2015-01-25T23:35:07.684Z
In case the zone id transformation might crash for any unsupported or unrecognized id, I recommend to
catch and log it,
do updates of tz-repositories (for Joda: update to latest version, for JDK: use tz-updater-tool)
That is usually the better strategy than to just silently fall back to any arbitrary tz-offset like UTC.
here's a kotlin extension to do the same (in case you code that way)
fun ZonedDateTime.toDateTime(): DateTime =
DateTime(this.toInstant().toEpochMilli(),
DateTimeZone.forTimeZone(TimeZone.getTimeZone(this.zone)))
I'm working on site that is writing on java 8 and spring 4, we are using java 8 standard time api, now on pages always is shown server time (in user profile is shown date of his registration). With old java time api there was way use SimpleDateFormat, but it doesn't fit now. Is there a way to convert server time to client time in jsp pages or in controllers?
One of the way to handle this is get users timezone String ( GMT , IST etc ) in request ( using AJAX or other relevant technology. )
Now by using new Java 8 Local date API we can achieve this as following:
Step 1 : Get Zone from giving zone query parameter that we got from ajax Using Java 8 ZoneId class. :
ZoneId losAngeles = ZoneId.of("America/Los_Angeles");
Step 2: Get current time based on that timezone using LocalTime API like below:
ZoneId losAngeles = ZoneId.of("America/Los_Angeles");
LocalDateTime currentTimeInLosAngeles = LocalTime.now(losAngeles );
Step 3 : Get DATETIME of particular zone By Using Java 8 ZonedDateTime class
ZoneId losAngeles = ZoneId.of("America/Los_Angeles");
LocalDateTime currentTimeInLosAngeles = LocalTime.now(losAngeles );
ZonedDateTime losAngelsDateTime = ZonedDateTime.of(currentTimeInLosAngeles , losAngeles );