I am getting timestamp with timezone value from postgres and in java i am storing the result in Timestamp variable. But the date time changes somehow. I am not getting the original value.
Table
CREATE TABLE log_fail
(
user_name character varying(99) NOT NULL,
date_time timestamp with time zone,
CONSTRAINT pk_log_failed_login PRIMARY KEY (user_name)
)
table data
username = superman
date_time = 2016-12-12 10:06:39.582-08
sql
String uname = "superman";
String sql1 = "select date_time from log_failed_login where user_name = '" + uname + "'";
rs1 = conn.createStatement().executeQuery(sql1);
if (rs1.next())
{
Timestamp attempt_datetime = rs1.getTimestamp("date_time");
}
Result i am getting
attempt_datetime = 2016-12-12 23:36:39.582
The problem is that the time zone on the machine where your Java code runs is Asia/Calcutta, and the timestamp is converted to that when it is converted to a string.
You can change your time zone in Java like this:
java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("Europe/Vienna"));
Three offsets
You are not showing us all of your code, so we cannot answer with certainty. But with some deduction I have a theory, a theory of three offsets.
Start with a value eight hours behind UTC → 2016-12-12 10:06:39.582-08
Adjust into UTC → 2016-12-12 18:06:39.582Z
Adjust into offset of +05:30 → 2016-12-12 23:36:39.582+05:30
Awkwardly reported sans offset → 2016-12-12 23:36:39.582
Thanks to the Answer of Laurenz Albe for solving the core issue; I just added a wordy narration here.
A couple of terms:
An offset-from-UTC is a number of hours and minutes and seconds from UTC, for example +05:00.
A time zone is an offset plus a set of rules for handling anomalies such as Daylight Saving Time (DST). Properly named as continent/region such as America/Montreal.
Your input 2016-12-12 10:06:39.582-08 represents a moment eight hours behind UTC, used in much of the west coast of North America.
You submitted that value to Postgres for storage. You do not say precisely, but I assume the column in the table is of type TIMESTAMP WITH TIME ZONE.
In Postgres, this type means: “with respect for any accompanying offset/zone info, adjust the incoming value into UTC”. Postgres then discards the accompanying offset/zone info. I repeat, Postgres does not store or remember the offset/zone, not in any of the date-time types. The difference between the WITH type and TIMESTAMP WITHOUT TIME ZONE is that in WITHOUT type any accompanying offset/zone info is ignored entirely, with no adjustments being made.
So when you submitted 2016-12-12 10:06:39.582-08 to Postgres, that value was automatically adjusted to be 2016-12-12 18:06:39.582Z. The Z here is short for Zulu and means UTC. Also, Postgres is not storing these strings literally, instead using its own internal binary storage format. These strings are for us humans to read in this discussion.
Anyways, back to that adjustment to UTC. Note how the time changed from 10 hours to 18 hours, because adjusting from eight hours behind UTC to UTC means adding eight hours.
So, good, the job of storing this value is done. We recorded the exact same moment in time, with 2016-12-12 10:06:39.582-08 and 2016-12-12 18:06:39.582Z both being the very same moment but seen through the lens of two different wall-clock time values. Everything so far is proper and sensible.
Later you retrieved the 2016-12-12 18:06:39.582Z value from the Postgres database. Apparently you extracted the data through JDBC into a java.sql.Timestamp object. Therein lies the problem. This class is one of the old date-time classes bundled with the earliest versions of Java. While laudable as an industry-leading attempt at date-time handling, these classes haven proven to be confusing, troublesome, and flawed. Avoid them whenever possible. They are now legacy, supplanted by the java.time classes.
Amongst the many problems of these legacy date-time classes is the implementation of their toString methods. Thee toString methods are overly-eager to please in implicitly applying the JVM’s current time zone. These has caused no end of confusion to so many Java programmers. Add this Question as another example of this confusion.
First, let's demonstrate this implicit application of time zone. First get the current moment in UTC, as an Instant.
Instant instant = Instant.now ();
Check to see our JVM’s current default time zone and its offset.
ZoneId z = ZoneId.systemDefault (); // Get current default time zone of this JVM.
String offset = z.getRules ().getOffset ( instant ).toString (); // Extract the offset-from-UTC from our default time zone.
Lastly, make a java.sql.Timestamp to see the behavior of its toString method.
java.sql.Timestamp ts = new java.sql.Timestamp ( instant.toEpochMilli () );
Dump to console.
System.out.println ( "instant.toString(): " + instant );
System.out.println ( "z.toString(): " + z );
System.out.println ( "offset.toString(): " + offset );
System.out.println ( "ts.toString(): " + ts );
instant.toString(): 2016-12-13T23:06:22.635Z
z.toString(): America/Los_Angeles
offset.toString(): -08:00
ts.toString(): 2016-12-13 15:06:22.635
From this output we can see that the Timestamp is indeed applying my own current default of -08:00 offset to an internal value in UTC. Even worse, the output of this method omits any indication of its offset, leaving us to assume it is UTC when in fact is not. Even more frustrating: this ‘feature’ is entirely undocumented!
Now that we have seen this anti-feature’s behavior in action, we can return to the example data in the Question. Apparently, the resulting data is 2016-12-12 23:36:39.582, lacking any indication of offset as we just noted above. But look at the time-of-day, 23:36:39.582 versus what we might have expected as UTC, 18:06:39.582. The unexpected value is five and a half hours ahead of our expected UTC value, or +05:30. An offset of +05:30 happens to be used in India and Sri Lanka, in zones such as Asia/Kolkata. So we can deduce that the author of this Question was running her code on a JVM with a current default time zone of something like Asia/Colombo or Asia/Kolkata having an offset of +05:30.
Solution
Avoid the legacy date-time classes.
Use java.time classes instead. The java.time classes all have sane simple toString methods that sensibly use the standard ISO 8601 text formats without any unexpected bonus behaviors.
Your JDBC 4.2 compliant driver may be able to directly address java.time types by calling PreparedStatement::setObject and ResultSet::getObject.
myPreparedStatement.setObject( … , instant ) ;
… and …
Instant instant = myResultSet.getObject( … ) ;
If not, fall back to using the java.sql types, but as briefly as possible. Use new conversion methods added to the old classes.
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
… and …
Instant instant = myResultSet.getTimestamp( … ).toInstant() ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
https://www.postgresql.org/docs/current/static/datatype-datetime.html
For timestamp with time zone, the internally stored value is always in
UTC (Universal Coordinated Time, traditionally known as Greenwich Mean
Time, GMT). An input value that has an explicit time zone specified is
converted to UTC using the appropriate offset for that time zone. If
no time zone is stated in the input string, then it is assumed to be
in the time zone indicated by the system's TimeZone parameter, and is
converted to UTC using the offset for the timezone zone.
When a timestamp with time zone value is output, it is always
converted from UTC to the current timezone zone, and displayed as
local time in that zone
check what is the timezone of your client:
select setting from pg_settings where name = 'TimeZone';
This will give your an idea, why time differs for different clients
Related
I have a date column in postgres db whose value is 2018-11-20 22:07:20. The datatype is timestamz. I want to get the above value in java code and convert that to seconds with respect to the current time. Suppose the current date is 2018-11-21 22:07:20 then the final answer should be 86400 seconds. Can anyone help me with this?
tl;dr
Duration // Represent a span-of-time unattached to the timeline.
.between( // Calculate elapsed time between two moments.
OffsetDateTime.now( ZoneOffset.UTC ) , // Capture the current moment as seen in UTC.
myResultSet.getObject( … , OffsetDateTime.class ) // Retrieve the moment stored in your database as a `OffsetDateTime` object, *not* as a mere string.
) // Return a `Duration` object.
.toSeconds() // View that duration as a total number of whole seconds. Obviously, any fractional second is ignored.
Details
The Answer by Michael is close but not quite correct.
It fails to account for anomalies in your local time zone, such as Daylight Saving Time (DST): LocalDateTime is the wrong class there, as it cannot, by definition, represent a moment.
It also fails to address the bigger problem that dumb strings are being used to exchange date-time values with the database rather than using smart objects.
date column in postgres db whose value is 2018-11-20 22:07:20
No, that is not the value of the column. That is a textual representation of the value. What is the distinction? Well, unfortunately, many tools used to access your data take the liberty of altering the data being retrieved by applying a time zone adjustment.
Even worse, your example text lacks an indicator of time zone or offset-from-UTC. This contradicts your next statement.
The datatype is timestamz.
I think you misspelled timestampz (missing the p). Even so, this seems to be incorrect, as no such type is listed among the Postgres date/time types. Some systems use that word as an abbreviation, but I recommend always using the longer SQL-standard name for clarity.
You likely meant the type TIMESTAMP WITH TIME ZONE which Postgres, like some other databases, stores as a value in UTC. Any indicator of time zone or offset-from-UTC present within incoming data is used to adjust to UTC, then the indicator is discarded. So values going into, and out of, a TIMESTAMP WITH TIME ZONE column in Postgres is always in UTC. Beware, as mentioned above, some tools interfere with the data retrieval by injecting a time zone adjustment, a well-intentioned though very confusing anti-feature.
Smart objects, not dumb strings
As of JDBC 4.2, we can exchange java.time objects with the database via setObject and getObject methods. Use the object rather than mere strings to exchange date-time values.
OffsetDateTime
Retrieve your value from a column of type TIMESTAMP WITH TIME ZONE as an OffsetDateTime value with its offset set to UTC.
OffsetDateTime odtThen = myResultSet.getObject( … , OffsetDateTime.class ) ;
For comparison, get the current moment in UTC. Specify the offset using the constant ZoneOffset.UTC.
OffsetDateTime odtNow = OffsetDateTime.now( ZoneOffset.UTC ) ;
To generate text representing that duration in standard ISO 8601 format, call OffsetDateTime::toString.
Duration
Capture elapsed time as a Duration object.
Duration d = Duration.between( odtNow , odtThen ) ;
To generate text representing that duration in standard ISO 8601 format, call Duration::toString.
String output = d.toString() ; // PnYnMnDTnHnMnS
To see that entire duration as one big count of whole seconds, call Duration::toSeconds.
long secondsElapsed = d.toSeconds() ;
ZonedDateTime
By the way, if you wish to view either the that odtThen or odtNow value through the lens of the wall-clock time used by the people of a particular region (a time zone), apply a ZoneId to get a ZonedDateTime.
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(!).
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = odtThen.atZoneSameInstant( z ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Parse into a LocalDateTime, then get the Duration between that and the current time, and convert it to seconds.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("your pattern");
LocalDateTime dateTime = LocalDateTime.parse("2018-11-20 22:07:20", formatter);
return Duration.between(dateTime, LocalDateTime.now()).getSeconds();
You can work out the pattern yourself.
I have my app hosted in a London Server. I am in Minasota, USA. So the timezone is different hours.
How can I obtain the current date / time with my time zone. The tricky part is i don't want to fetch current date and time based on server date and time. Is there a way i can fetch the real current date and time based on time zone.
The below code returns information but if the server date is invalid then the response will be invalid date too. But i want the REAL current date and time.
My input will be time zone. Any help is appreciated.
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Use Minasota's time zone to format the date in
df.setTimeZone(TimeZone.getTimeZone("America/Minasota"));
System.out.println("Date and time in Minasota: " + df.format(date));
It's important to know that the use of the three letter time-zone abbreviations is deprecated in java 8. So, don't expect them to be supported forever. Here are two equivalent examples of using the java 8 time api to achieve this:
System.out.println(Instant.now().atZone(ZoneId.of("CST",ZoneId.SHORT_IDS)));
System.out.println(Instant.now().atZone(ZoneId.of("America/Chicago")));
This question is possibly a duplicate of this
tl;dr
Is there a way i can fetch the real current date and time based on time zone.
Yes. Use modern java.time rather than terrible legacy classes.
ZonedDateTime.now( // Represent a moment as seen through the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "America/Chicago" ) // Specify time zone by proper `Continent/Region` name, *never* 2-4 letter pseudo-zones such as “CST”.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text in standard ISO 8601 format wisely extended to append the name of the time zone in square brackets.
2018-11-07T14:38:24.723394-06:00[America/Chicago]
Details
I have my app hosted in a London Server. I am in Minasota, USA.
This should be irrelevant to your app.
Server should default to UTC time zone (generally).
Client should be asked to confirm their desired/expected time zone (when critical).
Always specify explicitly your desired/expected time zone by passing optional argument rather than relying implicitly on the JVM’s current default time which can change at any moment during runtime(!).
How can I obtain the current date / time with my time zone.
Firstly, most of your business logic, storage, and exchange of date-time values should be done in UTC.
Instant instant = Instant.now() ; // Current moment in UTC.
You can see that same moment through the lens of a wall-clock time used by the people of a particular region, a time zone.
The time zone for Minnesota is America/Chicago.
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 CST or EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Chicago" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
As a shortcut, you can skip the Instant.
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
But i want the REAL current date and time
➥ Here is the core concept in mastering date-and-time work: The Instant (UTC) and the ZonedDateTime (some time zone) seen above represent the very same moment. Those two objects represent the same point on the timeline. They are both the “REAL date and time”. They use two different wall-clock times to show the same moment.
If a person in Iceland were on the phone with someone in Minneapolis, and they both look up their respective clocks & calendars on the wall to speak aloud the current date and time, which one of them is correct? Both are correct, two ways to express the same simultaneous moment.
Indeed, you would do well to think of UTC as The One True Time™, with all zoned times as mere variations. Focusing on your own parochial time zone, and then translating back-and-forth, will drive you batty. Focus on UTC, adjust into a time zone only when expected by the user or necessitated by business logic.
This has all been covered many times already on Stack Overflow. So search for more info and examples. And learn to search Stack Overflow before posting.
To generate strings, either call toString for text in standard ISO 8601 format, or use DateTimeFormatter.ofLocalized… to automatically localize, or use DateTimeFormatter.ofPattern to specify a custom formatting pattern. This has been covered many many times on Stack Overflow, so search for more info.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I am dealing with Time and Date in Java.
I am having date as : 2018-08-22T22:00:00-0500
My Time Zone offset here is -0500
How can I get the list of available Time Zone IDs?
My main objective here is to set a date to a particular Time Zone. However I do not know the time zone as it is embedded in this date format.
Update :
My question is different from Java TimeZone offset
as according to the accepted answer to that question, I need to have time zone info : "Europe/Oslo" . However I only have offset. See the accepted answer below which solves my problem.
tl;dr
eachZoneId.getRules().getOffset(
OffsetDateTime.parse(
"2018-08-22T22:00:00-0500" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssX" )
).toInstant()
).equals( myTargetZoneOffset )
Offset versus Zone
I do not know the time zone as it is embedded in this date format.
No, your input string of 2018-08-22T22:00:00-0500 has no time zone. It has only a mere offset-from-UTC.
An offset is simply a number of hours, minutes, and seconds of displacement ahead of, or behind, UTC. Yours shows an offset five hours behind UTC. In contrast, a time zone is a history of past, present, and future changes to the offset used by the people of a certain region.
OffsetDateTime
In java.time, we represent a moment with an offset as a OffsetDateTime class.
Your input string is in standard ISO 8601 format. So we should be able to parse directly without specifying a formatting pattern, as the java.time classes use ISO 8601 formats by default when parsing/generating strings. However your input lacks a colon in the offset between hours and minutes. While allowed by the standard, the OffsetDateTime class a small bug in Java 8 & 9 that fails by default to parse such values. As a workaround, specify a DateTimeFormatter.
String input = "2018-08-22T22:00:00-0000";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssX" ); // Specify formatting pattern to match input string.
OffsetDateTime odt = OffsetDateTime.parse( input , f ); // Parse from dumb string to smart `OffsetDateTime` object.
odt.toString(): 2018-08-22T22:00-05:00
Time zone names
How can I get the list of available Time Zone IDs?
Not sure what you mean by “Time Zone IDs”. I am guessing that you are asking for a list of all the time zones using that particular offset-from-UTC at that particular moment.
A proper time zone name has 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(!).
We represent the time zone using the ZoneId. The TimeZone class is now legacy, and should be avoided.
To get our list of ZoneId objects with that offset in use on that date, we need to first extract the offset (ZoneOffset) from our OffsetDateTime.
ZoneOffset offset = odt.getOffset() ;
offset.toString(): -05:00
Next phase is to interrogate all known time zones, asking each for the offset in effect at the moment of our OffsetDateTime. The argument for that moment must be in UTC, a Instant object. So we must extract an Instant from our OffsetDateTime. Still the same moment, the same point on the timeline, but seen through the lens of a different wall-clock time.
Instant instant = odt.toInstant() ; // Extract a UTC value (`Instant`) from our `OffsetDateTime` object.
instant.toString(): 2018-08-23T03:00:00Z
The Z on the end is short for Zulu and means UTC.
Make an empty list to collect the desired zones.
List< ZoneId > hits = new ArrayList<>() ; // Make an empty list of `ZoneId` objects found to have our desired offset-from-UTC.
Now get all known zones. A method exists giving a set of all zone names, but not the zone objects. So for each iteration we must instantiate the ZoneId. Then we ask the zone for its rules, the list of changes in effect over time for that region. To the rules we pass our moment (Instant), and get back the ZoneOffset in effect at that time. If this offset matches our target offset, we add the zone to our list.
Be aware that many of the zones may be essentially duplicates or deprecated. The list of zones has had a fractured history, with many changes, and some are mere aliases to others.
Set < String > names = ZoneId.getAvailableZoneIds(); // Get a collection of all known time zones’ names.
for ( String name : names ) // Loop each name.
{
ZoneId z = ZoneId.of( name ); // Instantiate a `ZoneId` for that zone name.
ZoneRules rules = z.getRules(); // Get the history of past, present, and future changes in offset used by the people of this particular region (time zone).
ZoneOffset o = rules.getOffset( instant ); // Get the offset-from-UTC in effect at this moment for the people of this region.
if( o.equals( offset )) { // Compare this particular offset to see if it is the same number of hours, minutes, and seconds as our target offset.
hits.add( z ); // If we have a hit, add to our collection of `ZoneId` objects.
}
}
Dump our hits collection to the console.
[America/Panama, America/Chicago, America/Eirunepe, Etc/GMT+5, Pacific/Easter, Mexico/General, America/Porto_Acre, America/Guayaquil, America/Rankin_Inlet, US/Central, America/Rainy_River, America/Indiana/Knox, America/North_Dakota/Beulah, America/Monterrey, America/Jamaica, America/Atikokan, America/Coral_Harbour, America/North_Dakota/Center, America/Cayman, America/Indiana/Tell_City, Chile/EasterIsland, America/Mexico_City, America/Matamoros, CST6CDT, America/Knox_IN, America/Bogota, America/Menominee, America/Resolute, SystemV/EST5, Canada/Central, Brazil/Acre, America/Cancun, America/Lima, America/Bahia_Banderas, US/Indiana-Starke, America/Rio_Branco, SystemV/CST6CDT, Jamaica, America/Merida, America/North_Dakota/New_Salem, America/Winnipeg]
Be aware that this list of zones is valid only for our chosen particular moment. In earlier times, or later times, some of these zones may be using some other offset-from-UTC. Conversely, at other moments some zones not on this list may be using our desired offset.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You can convert your date to a Calendar. Calendar.getTimeZone() would return the TimeZone match the Calendar. And then TimeZone.getAvailableIDs() would give you the available IDs.
This question is related to this but this specific question focuses on the why. So no, this isn't a duplicate.
Quoting the answer:
The problem is that Java Date objects don't store a time zone. The
value is always in UTC, and is parsed and formatted in a given time
zone, usually the JVM's default time zone.
Oracle DATE columns are also stored without time zone, but should
represent the date as seen by the user. In 99.99% of cases, that means
the date in the JVM's default time zone.
So, the JDBC driver takes the Timestamp / Date value, which is in UTC,
converts it to the default time zone, and saves that to the database.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
What is it trying to solve by adjusting the value before saving it to the database?
The answers to these questions are the whys.
I couldn't see the benefit of the design and I can only see are the problems associated with it. Case in point is when saving is done in a specific timezone and retrieval is done in another timezone. The amount of questions being thrown at this specific topic just proves my point.
So ultimate question is, why it was designed that way? What are the reasons?
Date-time handling is a surprisingly complicated topic. Our intuitive understanding of time works against us as programmers, making this topic difficult to master. Furthermore, poor date-time handling in old databases and old classes make the job even more confusing.
java.time
Firstly, avoid the wretched old date-time classes bundled with the earliest versions of Java. Never use java.util.Date, java.util.Calendar, java.sql.Timestamp, nor other related classes. Use only the java.time classes. If you must interface with old code not yet updated to java.time, call on new conversions methods added to the old classes.
Date was replaced by Instant.
The problem is that Java Date objects don't store a time zone.
Not true. An Instant (and a Date) is always in UTC. Both the modern and legacy class represent a a count of fractional seconds since the first moment of 1970 in UTC, 1970-01-01T00:00:00Z. Always in UTC, easy-peasy.
Oracle DATE columns are also stored without time zone,
True.
The Oracle DATE data type represents only a date with time zone but lacks any concept of time zone or offset-from-UTC. This is apparently a legacy type, created before the SQL standard defined some basic date-time types. In the standard, the TIMESTAMP WITHOUT TIME ZONE might map close to Oracle DATE.
but should represent the date as seen by the user. In 99.99% of cases, that means the date in the JVM's default time zone.
I have no idea what the author meant by that. I think that is their clumsy way of saying that any type similar to the SQL-standard TIMESTAMP WITHOUT TIME ZONE simply takes any given date or date-with-time-of-day as-is, with no attempt at adjusting between zones or offsets. So if you pass January 21, 2018 at noon, it stores a value equivalent to this string 2018-01-23T12:00 without any regard to whether that was noon in Montréal Québec or noon in Kolkata India (two different moments, hours apart).
So, the JDBC driver takes the Timestamp / Date value, which is in UTC, converts it to the default time zone, and saves that to the database.
While the JDBC driver is unspecified here, I doubt this is its behavior. Such behavior would be a contradiction of the behavior of the Oracle DATE type which is no such zone adjustment. The Oracle DATE type (as I read the documentation; I'm not an Oracle user) is agnostic or unaware of zones/offsets.
In Java, the class mapping to SQL-standard TIMESTAMP WITHOUT TIME ZONE and Oracle DATE is LocalDateTime. You should use these zone-less types only in these three situations:
The zone or offset is unknown.This is bad. This is faulty data. Analogous to having a price/cost without knowing the currency. You should be rejecting such data, not storing it.
The intention is “everywhere”, as in, every time zone.For example, a corporate policy that states “All our factories will break for lunch at 12:30" means the factory in Delhi will break hours before the factory in Düsseldorf which breaks hours before the factory in Detroit.
A specific moment in the future is intended, but we are afraid of politicians redefining the time zone.Governments change the rules of their time zones with surprising frequency and with surprisingly little warning or even no warning at all. So if you want to book an appointment at 3 PM on a certain date, and you really mean 3 PM regardless of any crazy decision a government might make in the interim, then store a LocalDateTime. To print a report or display a calendar, dynamically apply a time zone (ZoneId) to generate a specific moment (ZonedDateTime or Instant). This must be done on-the-fly rather than storing the value.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
The JDBC driver should not be doing any adjustments to UTC for a type of Oracle Date or SQL-standard TIMESTAMP WITHOUT TIME ZONE.
If two users at 2018-06-06T21:53Z, one in Québec and one in India, both simultaneously save the current moment of their own parochial wall-clock time into a column of type SQL-standard TIMESTAMP WITHOUT TIME ZONE or Oracle DATE, then we should see two rows with values:
2018-06-06T17:53 (notice the date is “yesterday”)
2018-06-07T03:23 (notice the date is “tomorrow”)
The values are different because America/Montreal is four hours behind UTC while Asia/Kolkata is five and a half hours ahead of UTC, and no adjustment for time zones was made. To repeat myself yet again, the stored values here represent only a date and a time-of-day, but without any context of time zone or offset-from-UTC, they do not represent a moment.
The confusion may be coming from the fact that some databases such a Postgres do adjust incoming values into UTC for values heading to a column of a different type, the TIMESTAMP WITH TIME ZONE type (note the WITH versus WITHOUT). Postgres and something other databases use any passed zone/offset info to adjust into UTC value, then discard the zone/offset info. So the type name is something of a misnomer, and you can think of it as TIMESTAMP WITH RESPECT FOR TIME ZONE.
If those same two users seen above at 2018-06-06T21:53Z were saving the current moment into a SQL-standard column of type TIMESTAMP WITH TIME ZONE, then the two rows would appear as:
2018-06-06T21:53Z
2018-06-06T21:53Z
The Z at the end is pronounced Zulu and means UTC.
What is it trying to solve by adjusting the value before saving it to the database?
Generally, the best practice in date-time handling is to work in UTC rather than in other zones/offsets.
When working as a programmer or sysadmin, forget about your own parochial time zone. Translating back-and-forth from your own zone to UTC or other zones will drive you batty. Think of UTC as the One True Time; all other zones are but mere variations.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Store.
String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ; // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.
myPreparedStatement.setObject( 1 , instant ) ; // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.
Retrieve.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Present that moment in a particular time zone.
ZoneId z = ZoneId.of( "Asia/Kolkat" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I just want to share my experience about Java TimeZone. Here was the problem:
The inDaylightTime(Date date) function of timezone always returns 0, regardless of date. Consistently getDSTSavings() also returns 0.
here is the snippet of code to create timezone:
Timezone timezone = TimeZone.getTimeZone("GMT+1:00");
DST in a timezone object created with an id like "UTC+1:00" (or "GMT+1:00") will be different with a timezone object created with corresponding string "Europe/Berlin", so if DST is important to your application, always use full string id's instead of corresponding time offset.
So changing timezone definition to:
Timezone timezone = TimeZone.getTimeZone("Europe/Berlin");
will solve the problem.
No, TimeZone API figures out day light savings. You are using a custom time zone ID.
From the Documentation of TimeZone API
No daylight saving time transition schedule can be specified with a custom time zone ID
So, you need to specify the time zone ID available to get day light savings
Typically, you get a TimeZone using getDefault which creates a TimeZone based on the time zone where the program is running. For example, for a program running in Japan, getDefault creates a TimeZone object based on Japanese Standard Time.You can also get a TimeZone using getTimeZone along with a time zone ID. For instance, the time zone ID for the U.S. Pacific Time zone is "America/Los_Angeles". So, you can get a U.S. Pacific Time TimeZone object with:
Use the time zone ID, this will take care of day light savings in that particular zone
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
tl;dr
Ask if DST is currently in effect.
ZoneId
.of( "Europe/Madrid" )
.getRules()
.isDaylightSavings( Instant.now() )
Ask the offset (hours-minutes-seconds) ahead or behind UTC currently in effect.
ZoneId
.of( "Africa/Tunis" )
.getRules()
.getOffset( Instant.now() )
DST comes and goes
To ask "Is Daylight Saving Time in effect?", you must specify a moment. The very definition of Daylight Saving Time (DST) is that it comes and goes, twice a year.
Use Instant to specify a moment.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
java.time
You are using terrible date-time classes that are now legacy, supplanted years ago by the modern java.time classes defined in JSR 310.
Use ZoneId rather than TimeZone.
Offset versus time zone
TimeZone.getTimeZone("GMT+1:00");
The string GMT+1:00 does not represent a time zone, it represents an offset. There are many time zones that may all coincidentally be using an offset right now of one hour ahead of UTC, such as Africa/Casablanca, Africa/Brazzaville, Africa/Tunis, Europe/Andorra, Europe/Warsaw, and many more.
Understand that an offset is merely a number of hours-minutes-seconds ahead or behind the prime meridian. An offset looks like +05:30 or -05:00.
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. The rules of a time zone are set capriciously by politicians, and change with surprising frequency.
A proper time zone name is composed as Continent/Region such as America/Montreal or America/New_York. See this list of zones at Wikipedia (may not be up-to-date).
ZoneId z = ZoneId.of( "Europe/Gibraltar" ) ;
Asking "Is DST in effect?"
It seems you want to know if DST is currently in effect for a particular time zone. Get the ZoneRules for a particular ZoneId. Then interrogate for a specific moment.
ZoneId z = ZoneId.of( "Europe/Gibraltar" ) ;
ZoneRules rules = z.getRules() ;
boolean dstInEffect = rules.isDaylightSavings( Instant.now() ) ;
And you can ask for the amount of the offset-from-UTC currently in effect. A ZoneOffset object represents that number of hours-minutes-seconds ahead or behind the prime meridian.
ZoneOffset offset = rules.getOffset( Instant.now() ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….