I'm using IntelliJ (Kotlin and Java language), I'm trying to get a report of time by using query from my database and send that to the browser (I'm using Postman to see the result).
When I debug my code and go through the result from the query the timezone is ok like the way I want it, its even show -7 (the difference time between me and the UTC) but when it comes to the browser (Postman) it's showing UTC time and +0000, for example:
"date": "2017-10-12T15:00:33.000+0000"
instead of
"date": "2017-10-12T15:00:33.000+0007".
I tried many options and waste around 6 hours to find the solution but nothing work.
Postgres stores a TIMESTAMP WITH TIME ZONE in UTC, discarding the passed zone after using it to make the adjustment into UTC. Note that this is Postgres-specific behavior – databases vary widely in their date-time handling, and the SQL spec barely touches on the subject.
As commented by Marlowe, if you need to remember the time zone captured from data-entry, you will need to store that in another column. Capture a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
➠ Here's the rub: A Postgres session in an interactive tool such as pgAdmin dynamically applies a default time zone after fetching the UTC value. While well-intentioned, this is an anti-feature in my opinion as it obscures the true nature of the stored data.
Fetch the value in UTC using the modern java.time classes.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Adjust to your desired time zone.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Generate a string for the web browser using the DateTimeFormatter class. Note the automatic localization features there.
All this has been covered many times on Stack Overflow. Search to learn more.
Solution: so i found the solution to my problem here:
Overriding BeanPropertyRowMapper to Support JodaTime DateTime
in shortly, timestamp type doesn't work well with timezone so i changed it to DateTime in Joda and wrapped the result of the query by costumed Bean (from the link i posted)
Related
I have a simple spring boot REST API application, using plain jdbc to fetch data from a MSSQL DB. I am trying to figure out how best to retrieve a DATETIME2 column from the DB (which stores no timezone info), and serialize it as a UTC timestamp (and treat it as such in general in code).
My DB server timezone is set to UTC. I know that everything stored to this column is stored as UTC and I cannot change the column type unfortunately. It's a bit of a legacy DB, so all the dates will need to fetch will have this same problem, hence looking for a clean neat and tidy solution.
Ideally in my Java app, I would ideally like all my "date" fields to be of type java.time.Instant, since it is easy to handle and will serialize to json looking something like "someDate": "2022-05-30T15:04:06.559896Z".
The options as I see them are:
Use a custom RowMapper to do something like myModel.setDate(rs.getTimestamp("Date").toLocalDateTime().toInstant(ZoneOffset.UTC));, but this just seems verbose. I suppose I could tuck it away in some utility class static function?
Use LocalDateTime everywhere and do myModel.setDate(rs.getTimestamp("Date").toLocalDateTime()). But then Jackson will serialize it without timezone information.
Set the whole app timezone to UTC on startup. But this could be changed by other code, and from what I read is generally a bad idea.
Caveat: I am not a user of Spring.
moment versus not-a-moment
You need to get clear on one fundamental issue with date-time handling: moment versus not-a-moment.
By “moment” I mean a specific point on the timeline. Without even thinking about time zones and such, we all know that time flows forward, one moment at a time. Each moment is simultaneous for everyone around the world (sticking with Newtonian time, ignoring Einstein Relativity here 😉). To track a moment in Java, use Instant, OffsetDateTime, or ZonedDateTime. These are three different ways to represent a specific point on the timeline.
By “not-a-moment” I mean a date with a time-of-day, but lacking the context of a time zone or offset-from-UTC. If I were to say to you, “Call me at noon tomorrow” without the context of a time zone, you would have no way of knowing if you should call at noon time in Tokyo Japan, noon time in Toulouse France, or noon time in Toledo Ohio US — three very different moments, several hours apart. For not-a-moment, use LocalDateTime.
So never mix LocalDateTime with the other three classes, Instant, OffsetDateTime, or ZonedDateTime. You would be mixing your apples with your oranges.
You said:
I would ideally like all my "date" fields to be of type java.time.Instant
Yes, I would agree on generally using Instant as the member field on any Java object tracking a moment. This is generally a good idea — but only for moments. For not-a-moment, as discussed above, you should use LocalDateTime instead.
TIMESTAMP WITH TIME ZONE
Another issue, Instant was not mapped in JDBC 4.2 and later. Some JDBC drivers may optionally handle an Instant object, but doing so is not required.
So convert your Instant to a OffsetDateTime. The OffsetDateTime class is mapped in JDBC to a database column of a type akin to the SQL-standard type TIMESTAMP WITH TIME ZONE.
OffsetDateTime odt = instant.atOffset( Offset.UTC ) ;
Writing to database.
myPreparedStatement.setObject( … , odt ) ; // Pass your `OffsetDateTime` object.
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
TIMESTAMP WITHOUT TIME ZONE
For database columns of a type akin to the SQL-standard type TIMESTAMP WITHOUT TIME ZONE, use LocalDateTime class.
Writing to database.
myPreparedStatement.setObject( … , ldt ) ; // Pass your `LocalDateTime` object.
Retrieval.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
Specify time zone
You said:
My DB server timezone is set to UTC.
That should be irrelevant. Always write your Java code in such as way as to not rely on the JVM’s current default time zone, the host OS’ current default time zone, or the database’s current default time zone. All of those lay outside your control as a programmer.
Specify your desired/expected time zone explicitly.
Retrieve a moment from the database, and adjust into a desired time zone.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
Generate text localized to the user's preferred locale.
Locale locale = Locale.JAPAN ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( locale ) ;
String output = zdt.format( f ) ;
DATETIME2 in MS SQL Server
The type DATETIME2 type in MS SQL Server stores a date with time-of-day, but lacks the context of a time zone or offset-from-UTC.
That is exactly the wrong type to use for storing a moment. As discussed above, that type is akin to the SQL standard type TIMESTAMP WITHOUT TIME ZONE, and maps to the Java class LocalDateTime.
You seem to understand that fact given your comment:
I know that everything stored to this column is stored as UTC and I cannot change the column type unfortunately. It's a bit of a legacy DB …
Let me point out that you do not know the values in that column are intended to represent a moment as seen with an offset of zero. You can expect that, and hope so. But without using the protection of the database’s type system, you cannot be certain. Every user, every DBA, and every SysAdmin must have always been aware of this unfortunate scenario, and must have always done the right thing. You’ll need lots of luck with that.
I must mention that the ideal solution is to refactor your database, to correct this wrong choice of data type for that column. But I understand this could be a burdensome and challenging fix.
So given this unfortunate scenario without a fix being feasible, what to do?
Options 1, 2, & 3 you listed
Option 1
Regarding your option # 1, yes that makes sense to me. Except two things:
I would change the name of your model method to be more precise: setInstant. Or use a descriptive business name such as setInstantWhenContractGoesIntoEffect.
Never use the awful legacy date-time classes in Java such as Timestamp. Change this:
myModel.setDate(rs.getTimestamp("Date").toLocalDateTime().toInstant(ZoneOffset.UTC));
… to:
myModel
.setInstantWhenContractGoesIntoEffect
(
resultSet
.getObject( "Date" , LocalDateTime.class ) // Returns a `LocalDateTime` object.
.toInstant( ZoneOffset.UTC ) // Returns an `Instant` object.
)
;
Option 2
As for your option # 2, I am not quite sure what you have in mind. But my impression is that would be the wrong way to go. I believe the best approach, for long-term maintenance without "technical debt", and for avoiding confusing and mishaps, is to “tell the truth”. Do not pretend to have a zebra when you actually have donkey. So:
On the database side, be clear and explicit that you have a date with time but lack the context of an offset. Add lots of documentation to explain that this is based on a faulty design, and that we are intend to store moments as seen in UTC.
On the app side, the Java side, deal only with Instant, OffsetDateTime, and ZonedDateTime objects, because within the data model we are representing moments. So use classes that represent a moment. So no use of LocalDateTime where you really mean a specific point on the timeline.
Obviously, there is some kind of a dividing line between your database side and your app side. Crossing that line is where you must convert between your Java type for a moment and your database type faking it as a moment. Where you draw that line, that transition zone, is up to you.
Option 3
As for your option # 3, yes that would be a very bad idea.
Setting such a default is not reliable. Any SysAdmin, or even an unfortunate OS update, could change the OS’s current default time zone. Like wise for the database’s current default time zone. And likewise for the JVM’s current default time zone.
So you end up three default time zones that could be changing, with each affecting various parts of your environment. And changing the current default time zone in any of those places immediately affects all other software depending on that default, not just your particular app.
As mentioned above, I recommend just the opposite: Code without any reliance on default time zones anywhere.
The one place for accessing a default time zone is maybe for presentation to the user. But even then, if the context in crucial, you must confirm the desired/expected time zone with the user. And where you do make use of a current default time zone, do so explicitly rather than implicitly. That is, make explicit calls such as ZoneId.getSystemDefault() rather than using omitted optional arguments.
I'm not sure I see a problem.
Instant values are UTC by definition, and java.sql.Timestamps have no zone other than the one implied by the database setting. You know the database is strictly UTC. This is lucky for you since it eliminates one error-prone conversion. Then, reading java.sql.Timestamps and keeping them as Instants at runtime is trivial, given java.sql.Timestamp#toInstant(). DON'T convert through LocalDateTime.
This has nothing to do with setting any "default" timezone in your application. Design and write your code so that internally (i.e. runtime memory and database) you deal ONLY with UTC (i.e. instants). The only point at which you should convert instants to anything local is at external interface points... i.e.
when outputting date/time values, either for human consumption or for other software that expects a specific timezone.
when reading date/time values from the user or another program (for which you will need to know any implied zone if it's not explicit)
Leave your "default" timezone as whatever is given to you by your environment. Then, no matter where your code is running, it will produce meaningful local dates/times.
Establish a strict rule that you deal only with UTC internally. This will make reasoning about your code MUCH simpler in the long run.
I guess the only real stumbling block is realizing that things depending on local conditions, such as day boundaries, have to be done in the local zone... but write your code to "think" UTC internally.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I have heard that postgres supports storing of datetime and time with both timezone and without timezone. Now an application running in JST timezone has to store time on database side. Now for wherever i have read it is suggested that you shall always keep things stored in UTC which can work in case of datetime because upon receiving datetime on application layer i can manipulate it according to my preferred timezone. But if i only want time stored on database side then for that as well shall i store it as UTC time ? If yes then 1 am in JST will result in 4 pm UTC of previous day. so could someone please suggest me what shall be the preferred way of storing time in database ?
As suggest in one of the answer if i read the time in LocalTime then if i store time as 12:00 JST then upon reading on application side it will be 12:00 JST or 12:00 UTC.
If there is even a remote possibility that times will ever be in more than one time zone, then you need to store them in a common well-known time zone, and UTC is the best choice for that.
If you're absolute certain, beyond any shadow of doubt, that the application will only ever be used in a single time zone, and that both application and database servers will run in that time zone, then you can store the times in that local time zone.
Since such certainty is rare, it is recommended to use UTC.
If the original time zone must be retained, you need a separate column to store the time zone, so it can be re-applied when loaded from the database.
tl;dr
Apparently you want to store a time-of-day without a date and without a time zone.
Use the Java class java.time.LocalTime and the standard SQL type TIME WITHOUT TIME ZONE.
LocalTime.of( 15 , 30 )
…and…
myPreparedStatement.setObject( … , LocalTime.of( 15 , 30 ) )
Details
Be aware that an offset-from-UTC is merely a number of hours-minutes-seconds, nothing more. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region.
I have heard that postgres supports storing of datetime and time with both timezone and without timezone.
Actually the SQL standard defines:
TIMESTAMP WITH TIME ZONETrack a moment, a specific point on the timeline. Represents a date, a time-of-day, and an offset-from-UTC or a time zone. Common implementations use UTC.
TIMESTAMP WITHOUT TIME ZONETracks a date and time-of-day but without the context of an offset or zone. So this type cannot represent a moment, is not a point on the timeline. For example, for a value of "noon on the 23rd of January in 2020", we do not know if this means noon in Tokyo, noon in Paris, or noon in Montréal — all very different moments, hours apart.
Now an application running in JST timezone
JST is not a true time zone. Proper time zones have a name in Continent/Region format. Never use the 3-4 letter pseudo-zones such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
If by JST you meant the time in Japan, use Asia/Tokyo.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
to store time on database side to be aware of the time slots.
I have no idea what that means.
Now for wherever i have read it is suggested that you shall always keep things stored in UTC
When tracking actual moments, yes, generally best to do so in UTC, that is, an offset-from-UTC of zero hours-minutes-seconds. Think of UTC as The One True Time, and other offsets & zones as mere variations, as localization issues for presentation of data to users. Do most of your business logic, data storage, data exchange, debugging, and logging in UTC. Keep a second clock on your desk set to UTC, seriously. Programmers and sysadmins should learn to think and work in UTC, leaving behind your parochial time zone while on the job.
which can work in case of datetime because upon receiving datetime on application layer i am manipulate it according to my preferred timezone.
Yes, as discussed above, work your logic in UTC (generally) and present localized to the zone expected by the user. When crucial, confirm with the user their desired time zone. And make a habit of always including the zone/offset info when displaying a date or time, to avoid ambiguity and confusion.
But if i only want time stored on database side then for that as well shall i store it as UTC time. If yes then 1 am in JST will result in 4 pm UTC of previous day. Can someone suggest how shall i store time alone in database side.
Do you mean you want to store just the time-of-day without a date and without a time zone? If so, use:
LocalTime in Java.
TIME WITHOUT TIME ZONE in standard SQL.
The SQL standard bizarrely defines a TIME WITH TIME ZONE type, but this makes no sense. Just think about it. And don’t be surprised; this is not the only anti-feature in the SQL standard. Postgres does offer this type, as following the standard is one of the primary goals of Postgres. Likewise, the java.time framework in Java includes a java.time.OffsetTime class to be compatible, but you will never use it.
Postgres has excellent date-time handling, and does offer the TIME WITHOUT TIME ZONE data types.
LocalTime localTime = LocalTime.of( 15 , 30 ) ;
myPreparedStatement.setObject( … , localTime ) ;
Retrieval.
LocalTime localTime = myResultSet.getObject( … , LocalTime.class ) ;
You might want to apply that time-of-day to a date and time zone to determine a moment by instantiating a ZonedDateTime.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate localDate = LocalDate.of( 2019 , Month.JANUARY , 23 ) ;
ZonedDateTime zdt = ZonedDateTime.of( localDate, localTime, z ) ;
See that same moment through the wall-clock time of UTC. Same moment, same point on the timeline, but different wall-clock time.
Instant instant = zdt.toInstant() ;
And adjust to another time zone if desired. Again, same moment, same point on the timeline, but different wall-clock time.
ZonedDateTime zdtMontréal = instant.atZone( ZoneId.of( "America/Montreal" ) ) ;
Perhaps you want to see the time-of-day in Québec for that moment.
LocalTime localTimeMontréal = zdtMontréal.toLocalTime() ;
Here is a table of the various date-time types in Java and in standard SQL.
I am currently writing an application where I want to provide a feature wherein a user can change the current time and timezone of the system.
Using the input timezone value , I run a shell script which links the local time to the passed timezone.
On UI, I am showing a list of timezones by calling - TimeZone.getAvailableIDs();
This retrieves TZ info from the "tzdb.dat" which comes along with the JRE.
But some of the timezones like "IST" are not supported by the native system. And these kind of unsupported Timezones are returned by TimeZone.getAvailableIDs().
So is there any way in Java where we can retrieve native system supported timezones ?
Time zones have a poor history
Time zones are, surprisingly, a real mess. I have been surprised to learn that they have not historically been well-defined and standardized.
In more recent times, a format of Continent/Region seems to have been widely adopted. See examples on this Wikipedia page. And learn about tzdata maintained currently by IANA.
You asked:
is there any way in Java where we can retrieve native system supported timezones ?
The TimeZone class in Java is now obsolete, supplanted by the modern java.time class ZoneId. Get a set of time zone names.
Set< String > zoneIds = ZoneId.getAvailableZoneIds() ;
Time zones change surprisingly often, both in their name and their currently used offset-from-UTC. Be sure to keep the tzdata in your Java implementation up-to-date. Ditto for your host OS. And your database system as well, with some such as Postgres having their own internal copy.
True time zones
You said:
But some of the timezones like "IST"
IST is not a real time zone. It is a pseudo-zone, which could mean Irish Standard Time, India Standard Time, or something else.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
If you meant India time, use:
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
If you meant Ireland time, use:
ZoneId z = ZoneId.of( "Europe/Dublin" ) ;
Do not rely on current default time zone
As commented by Matt Johnson-Pint, you should generally not be altering your host OS’ default time zone. Indeed, servers should usually be set for UTC (an offset of zero hours-minutes-seconds).
In your Java code, always specify your desired/expected time zone. The JVM’s current default time zone can be altered at any moment by any code in any thread of any app within the JVM — so depending on the current default is not reliable.
If you want the current moment as seen in India, specify the appropriate time zone.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ; // Capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone).
I need to set the system date in Centos 6.6 so what i did
cp /usr/share/zoneinfo/Europe/Paris /etc/localtime
And I wrote a small java application to print date and I print the date with the command date and i got this
[root#sandbox ~]# date
Fri May 20 19:13:32 CEST 2016
[root#sandbox ~]# java t
Fri May 20 17:13:35 UTC 2016
I really need to know why i have different time and how to fix it?
And i don't want to make change to the java code i want to fix the system date
We cannot precisely assist you until you post the source code for that little Java app.
Tracking date-time
One general concept you seem to be misunderstanding is that a date-time as tracked by a computer has no time zone. If you are using the old outmoded class java.util.Date, it tracks a number of milliseconds since the epoch reference date of first moment of 1970 in UTC.
Date-time != String
Another concept: A date-time object is not a string. You can generate a String to represent the date-time value in your date-time object, but that string is distinct from the date-time. Remember, internally that date-time is actually a count of milliseconds since 1970 UTC).
Your app is likely calling the toString method on java.util.Date class. That method confusingly applies the JVM’s current default time zone to the stored date-time creating the false illusion that the java.util.Date has that zone assigned.
Default time zone
The default time zone is usually picked up from the host OS when the JVM launches, but not always. Configuration flags for the JVM may indicate another time zone as default. And any code in any thread of any app within the JVM can change the JVM’s default time zone at any moment, during runtime! Because of being undependable, I suggest you avoid using the default, and instead always specify the desired/expected time zone.
Generally best practice is to keep servers on UTC. But I do not want my app to be vulnerable to such externalities as some sysadmin changing the server’s time zone, I always specify the desired/expected time zone in my Java code (shown further down).
No problem
So you have no problem to fix. Paris time (CEST) of 19:13:3x is two hours ahead of UTC, which your Java app is correctly showing as 17:13:3x for UTC time zone. These values make sense. These two date-time strings are two different representations of the very same moment on the timeline.
If you want a Paris time in your Java app, ask for a Paris time (shown further down below). If you want UTC, ask for UTC in your Java app (also shown further down).
As to why your Java app is showing the time in UTC remains a mystery until you show us your source code.
In the mean time, I can show the basics of capturing the current time and adjusting into a desired time zone.
java.time
You are use old date-time classes that have proven to be troublesome, confusing, and flawed. Avoid them. These old classes have been supplanted by the java.time framework built into Java 8 and later. Much of the java.time functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
An Instant is the current moment on the timeline in UTC, with resolution of nanoseconds (finer than the milliseconds in java.util.Date).
Instant instant = Instant.now();
Generally best to much of your business logic, database storage, other storage, and data exchange in UTC. So make frequent use of Instant.
Wall-clock time
Apply a time zone to get the wall-clock time for some locality. Apply a ZoneId to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Generating ISO 8601 strings
The toString methods in java.time by default generate strings in the standard ISO 8601 formats. The ZonedDateTime class’ toString method extends the standard by appending the name of the time zone in square brackets.
String output = instant.toString();
2016-05-21T00:52:53.375Z
String output = zdt.toString();
2016-05-21T02:52:53.375+02:00[Europe/Paris]
You can adjust into yet another time zone. Notice the date being previous to that of Paris, 20 versus 21 of May (still “yesterday” in Montréal).
ZonedDateTime zdt_Montréal = zdt.withZoneSameInstant( ZoneId.of( "America/Montreal" ) ) ;
2016-05-20T20:52:53.375-04:00[America/Montreal]
So we have generated three different textual representations of the very same moment on the timeline (UTC, Paris, & Montréal).
Time zones
Avoid using 3-4 letter zone abbreviations like CEST. These are neither standardized, nor unique(!). Furthermore, they are not true time zones. Always use proper time zone names, in the format of continent/region.
if you don't like or you can't change java code then you have to change server/system/centos timeZone, First you have to see list of possible timezones in
ls /usr/share/zoneinfo
and result
see we have date Sat May 21 02:54:11 IRDT 2016 we are now going to change you need to change (or set) the symbolic link /etc/localtime so make a backup of the existing `localtime file
sudo mv /etc/localtime /etc/localtime.bak
Next, create the link:
sudo ln -s /usr/share/zoneinfo/America/Chicago /etc/localtime
Make sure to replace America/Chicago with the directory (if your zone has one) and filename of the timezone you wish to use for example
sudo ln -s /usr/share/zoneinfo/UTC /etc/localtime
Now you just need to test your change. Run date from the command line, i done it and result are
and for see huge list of timezone click here
Make sure you also check your /etc/timezone file and adjust accordingly
Ok first you should think of using #Basil-Bourque answer to change your code. it's a good practice
and for changing you Centos timezone try to export TZ to Europe/Paris like the following
export TZ="Europe/Paris"
I am trying to convert the ZonedDateTime to a Date. Looks like in the conversion, it looses the time zone and gets my local time.
System.out.println("zoneDate1::::::::::"+ZonedDateTime.now(ZoneId.of("America/Chicago")));
System.out.println("zoneDate1:Date::::::::::"+Date.from(ZonedDateTime.now(ZoneId.of("America/Chicago")).toInstant()));
The above outputs as below:
zoneDate1::::::::::2016-04-15T17:35:06.357-05:00[America/Chicago]
zoneDate1:Date::::::::::Fri Apr 15 18:35:06 EDT 2016
Is this because this is a Date type? How would i go about doing this kind of conversion and conserve the zoned time?
What is the problem? What did you expect? I see no misbehavior.
Your java.time type (ZonedDateTime) is assigned a time zone of America/Chicago.
Your JVM apparently has an assigned time zone related to east coast of North America, the clue being the EDT value seen in string output. The toString method on java.util.Date applies your JVM’s current default time zone when generating its textual representation of the date-time value. Poorly designed, this behavior is trying to be helpful but is actually confusing because you cannot actually get or set this time zone on the java.util.Date object.
At any rate, the east coast of North America (such as America/New_York time zone) is an hour ahead of America/Chicago. So you are seeing 17:xx:xx time for Chicago and 18:xx:xx for Eastern Daylight Saving Time. These values are correct.
You should call java.util.TimeZone.getDefault() when investigating the behavior of the old date-time classes.
java.time
The bigger problem is that you are even using these old date-time classes such as java.util.Date/.Calendar. They are poorly designed, confusing, and troublesome. Avoid these old classes altogether. They have been supplanted in Java 8 and later by the java.time framework.
Also, avoid using 3-4 letter zone abbreviations like EDT. These are neither standardized nor unique. Use proper time zone names in continent/region format.
Instant
To capture the current date-time in java.time, just use Instant. This class captures a moment on the timeline in UTC. Do most of your work in UTC. No need for time zones unless expected by your user when displayed in the user interface.
Instant now = Instant.now();
Database
To send to your database, first make sure you have defined the column in the table as something along the line of the SQL standard TIMESTAMP WITH TIME ZONE. By the way, support for date-time types various among databases with some doing a much better job than others.
Hopefully JDBC drivers will be updated someday to directly handle the java.time types. Until then, we must convert into a java.sql type when transferring data to/from a database. The old java.sql classes have new methods to facilitate these conversions.
java.sql.Timestamp
For a date-time value like Instant, we need the java.sql.Timestamp class and its from( Instant ) method.
java.sql.Timestamp ts = java.sql.Timestamp.from( now );
Avoid working in java.sql.Timestamp as it is part of the old poorly-designed mess that is the early Java date-time classes. Use them only for database transfer, then shift into java.time immediately.
Instant instant = ts.toInstant();
So simple, no time zones or offset-from-UTC involved. The Instant, java.sql.Timestamp, and database storage are all in UTC.
ZonedDateTime
When you do need to shift into some locality’s wall-clock time, apply a time zone.
ZoneId zoneId = ZoneId.of( "America/Chicago" ); // Or "America/New_York" and so on.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Huh? Date doesn't have time zones so, this is probably why it's failing. Maybe this is what you're looking for:
Date.from(java.time.ZonedDateTime.now().toInstant());
If your database allows you to store the timestamp along with the timezone, you should go ahead and save it as a timestamp.
If not, I would recommend that you store the date-time as per your timezone (or GMT). Add an additional column in the table to hold the value of the user's timezone.
When you fetch the value from the database, you can convert it to the user's timezone. Avoid storing just the date.