Java.util.date get the actual date at client timezone - java

Searching up and down, right and left - don't find simple answer to this question:
I have java.util.Date instance, which get its value from mySQL.
Also I have time-zone code of the logged-in user.
I need to get the actual time at user time-zone.
For example:
My server-machine time-zone is GMT+2.
My date value in DB is: 2017-02-09 16:38:58.000
According to my server-machine-time-zone I get it into date instance as: 2017-02-09T16:38:58.000+0200
Now I need to know what to do if:
In case, for sample, my client-time-zone-code is GMT+4, I want to get:
2017-02-09 20:38:58.000
Pure date, that is right to my time zone and not contain "+4" or "GMT" indication.
In short words: convert my java.util.date to pure date that right to specific time-zone.
Sound very simple? after read very much documentaion, I already not sure that this is really simple.

Timestamp (with time zone)
As far as I have understood, the date-time in your database in UTC, but when you retrieve it, you (incorrectly) receive 2017-02-09T16:38:58.000+02:00.
First, if you can, change the datatype of your MySQL database column to timestamp (in some other databases it would be called timestamp with time zone). This will make sure that MySQL knows that the times are in UTC and should enable you to retrieve them as the right point in time rather than the right time of day in the wrong time zone. This in turn will give you the best starting point for converting to the client time zone.
java.time
Second, retrieve your value into an appropriate type from java.time, the modern Java date and time API. Avoid java.util.Date since it is poorly designed and cannot handle different time zones. For example, if your database datatype is datetime:
LocalDateTime dateTime = yourResultSet.getObject("your_col", LocalDateTime.class);
LocalDateTime is a date and time of day without time zone, so you cannot get the wrong time zone. Supply the offset that you know is right:
OffsetDateTime odt = dateTime.atOffset(ZoneOffset.UTC);
Convert to client time zone:
ZoneId clientTimeZone = ZoneId.of("Indian/Reunion");
ZonedDateTime clientDateTime = odt.atZoneSameInstant(clientTimeZone);
System.out.println(clientDateTime);
2017-02-09T20:38:58+04:00[Indian/Reunion]
Do yourself the favour of using a real time zone in the region/city format rather than an offset like +04:00. It’s easier to understand and more future-proof. Indian/Reunion is just an example, of course, use the correct one for your client.
The ZonedDateTime above has both offset and time zone in it. It’s recommended to keep it that way, and I don’t see it doing any harm. The client can always opt not to display it. If you still insist, convert to LocalDateTime again:
LocalDateTime clientDateTimeWithoutOffset = clientDateTime.toLocalDateTime();
System.out.println(clientDateTimeWithoutOffset);
2017-02-09T20:38:58
If the database datatype is timestamp:
OffsetDateTime odt = yourResultSet.getObject("your_col", OffsetDateTime.class);
This saves the first step above. The remainder is the same.
Link
Oracle tutorial: Date Time explaining how to use java.time.

java.util.Date does not store any time zone. It just stores the number of milliseconds since the 'epoch', which is 1 January 1970, 00:00:00 UTC.
Thus, all you have to do is to know the time zone of your server machine, find the period between this time zone and the time zone you want to convert it to and add or subtract the period.
UPDATE:
int clientGMT = 4; //GMT you want to convert to
int serverGMT = 2; //server's GMT
int delta = clientGMT - serverGMT; //delta between the dates
//assume this is the date in GMT + 2 received from the server
Date d1 = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss").parse("12.03.2019 13:00:00");
//... and you want to convert it to GMT + 4 (client side's time zone)
Date resultDate = new Date(d1.getTime() + delta * 3600000);
P.S. Yes, you have to manipulate time zones manually, as I said above, java.util.Date does not store this information (each date is assumed to be in UTC).

Related

Why is the following date conversion in Java 8 not appropriate?

I have seen a lot of debates on the following date conversion:
timeStamp.toLocalDateTime().toLocalDate();
Some people say that it is not appropriate because the timezone has to be specified for proper conversion, otherwise the result may be unexpected. My requirement is that I have an object that contains Timestamp fields and another object that contains LocalDate fields. I have to take the date difference between both so I think that the best common type to use is LocalDate. I don't see why the timezone has to be specified as either timestamp or LocalDate just represent dates. The timezone is already implied. Can someone give an example when this conversion fails?.
It’s more complicated than that. While it’s true that a Timestamp is a point in time, it also tends to have a dual nature where it sometimes pretends to be a date and time of day instead.
BTW, you probably already know, the Timestamp class is poorly designed and long outdated. Best if you can avoid it completely. If you are getting a Timestamp from a legacy API, you are doing the right thing: immediately converting it to a type from java.time, the modern Java date and time API.
Timestamp is a point in time
To convert a point in time (however represented) to a date you need to decide on a time zone. It is never the same date in all time zones. So the choice of time zone will always make a difference. So one correct conversion would be:
ZoneId zone = ZoneId.of("Africa/Cairo");
LocalDate date = timestamp.toInstant().atZone(zone).toLocalDate();
The Timestamp class was designed for use with your SQL database. If your datatype in SQL is timestamp with time zone, then it unambiguously denotes a point in time, and you need to see it as a point in time as just described. Even when to most database engines timestamp with time zone really just means “timestamp in UTC”, it’s still a point in time.
And then again: sometimes to be thought of as date and time of day
From the documentation of Timestamp:
A Timestamp also provides formatting and parsing operations to support
the JDBC escape syntax for timestamp values.
The JDBC escape syntax is defined as
yyyy-mm-dd hh:mm:ss.fffffffff, where fffffffff indicates
nanoseconds.
This doesn’t define any point in time. It’s a mere date and time of day. What the documentation doesn’t even tell you is that the date and time of day is understood in the default time zone of the JVM.
I suppose that the reason for seeing a Timestamp in this way comes from the SQL Timestamp datatype. In most database engines this is a date and time without time zone. It’s not a timestamp, despite the name! It doesn’t define a point in time, which is the purpose of and is in the definition of timestamp.
I have seen a number of cases where the Timestamp prints the same date and time as in the database, but doesn’t represent the point in time implied in the database. For example, there may be a decision that “timestamps” in the database are in UTC, while the JVM uses the time zone of the place where it’s running. It’s a bad practice, but it is not one that will go away within a few years.
This must also have been the reason why Timestamp was fitted with the toLocalDateTime method that you used in the question. It gives you that date and time that were in the database, right? So in this case your conversion in the question ought to be correct, or…?
Where this can fail miserably without us having a chance to notice is, as others have mentioned already, when the default time zone of the JVM is changed. The JVM’s default time zone can be changed at any time from any place in your program or any other program running in the same JVM. When this happens, your Timestamp objects don’t change their point in time, but they do tacitly change their time of day, sometimes also their date. I’ve read horror stories — in Stack Overflow questions and elsewhere — about the wrong results and the confusion coming out of this.
Solution: don’t use Timestamp
Since JDBC 4.2 you can retrieve java.time types out of your SQL database. If your SQL datatype is timestamp with time zone (recommended for timestamps), fetch an OffsetDateTime. Some JDBC drivers also let you fetch an Instant, that’s fine too. In both cases no time zone change will play any trick on you. If the SQL type is timestamp without time zone (discouraged and all too common), fetch a LocalDateTime. Again you can be sure that your object doesn’t change its date and time no matter if the JVM time zone setting changes. Only your LocalDateTime never defined a point in time. Conversion to LocalDate is trivial, as you have already demonstrated in the question.
Links
java.sql.Timestamp documentation
Wikipedia article: Timestamp
Question: Getting the date from a ResultSet for use with java.time classes
Question: Java - Convert java.time.Instant to java.sql.Timestamp without Zone offset
As you can see here(taken from https://stackoverflow.com/a/32443004/1398418):
Timestamp represents a moment in UTC and is the equivalent of the modern Instant.
When you do:
timeStamp.toLocalDateTime().toLocalDate();
the timeStamp is converted from UTC to the system timezone. It's the same as doing:
timeStamp.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
For example:
Timestamp stamp = new Timestamp(TimeUnit.HOURS.toMillis(-1)); // UTC 1969-12-31
System.setProperty("user.timezone", "EET"); // Set system time zone to Eastern European EET - UTC+2
stamp.toLocalDateTime().toLocalDate(); // represents EET 1970-01-01
stamp.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // represents EET 1970-01-01
That result (getting the date in the system time zone) is expected and if that's what you want, doing timeStamp.toLocalDateTime().toLocalDate() is appropriate and correct.
You're saying that you have a LocalDate field in some object and you want to get a period between it and a Timestamp, well that's just not possible without aditional information. LocalDate just represents a date, it has no time zone information, you need to know how it was created and what time zone was used.
If it represent a date in the system time zone then getting the period by using timeStamp.toLocalDateTime().toLocalDate() would be correct, if it represents a date in UTC or any other time zone then you might get a wrong result.
For example if the LocalDate field represents a date in UTC you will need to use:
timeStamp.toInstant().atZone(ZoneId.of("UTC")).toLocalDate();
Example: the 23rd of January becomes the 24th
You asked:
Can someone give an example when this conversion fails?.
Yes, I can.
Start with the 23rd of January.
LocalDate ld = LocalDate.of( 2020 , Month.JANUARY , 23 );
LocalTime lt = LocalTime.of( 23 , 0 );
ZoneId zMontreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , zMontreal );
Instant instant = zdt.toInstant();
zdt.toString() = 2020-01-23T23:00-05:00[America/Montreal]
instant.toString() = 2020-01-24T04:00:00Z
The Instant class represents a moment as seen in UTC. Let's convert to the terribly legacy class java.sql.Timestamp using the new conversion method added to that old class.
// Convert from modern class to troubled legacy class `Timestamp`.
java.sql.Timestamp ts = Timestamp.from( instant );
ts.toString() = 2020-01-23 20:00:00.0
Unfortunately, the Timestamp::toString method dynamically applies the JVM’s current default time zone while generating text.
ZoneOffset defaultOffset = ZoneId.systemDefault().getRules().getOffset( ts.toInstant() );
System.out.println( "JVM’s current default time zone: " + ZoneId.systemDefault() + " had an offset then of: " + defaultOffset );
JVM’s current default time zone: America/Los_Angeles had an offset then of: -08:00
So Timestamp::toString misreports the object’s UTC value after adjusting back eight hours from 4 AM to 8 PM. This anti-feature is one of several severe problems with this poorly designed class. For more discussion of the screwy behavior of Timestamp, see the correct Answer by Ole V.V.
Let's run your code. Imagine at runtime the JVM’s current default time zone is Asia/Tokyo.
TimeZone.setDefault( TimeZone.getTimeZone( "Asia/Tokyo" ) );
LocalDate localDate = ts.toLocalDateTime().toLocalDate();
Test for equality. Oops! We ended up with the 24th rather than the 23rd.
boolean sameDate = ld.isEqual( localDate );
System.out.println( "sameDate = " + sameDate + " | ld: " + ld + " localDate: " + localDate );
sameDate = false | ld: 2020-01-23 localDate: 2020-01-24
See this code run live at IdeOne.com.
So what is wrong with your code?
Never use java.sql.Timestamp. It is one of several terrible date-time classes shipped with the earliest versions of Java. Never use these legacy classes. They have been supplanted entirely by the modern java.time classes defined in JSR 310.
You called toLocalDateTime which strips away vital information. Any time zone or offset-from-UTC is removed, leaving only a date and a time-of-day. So this class cannot be used to represent a moment, is not a point on the timeline. Ex: 2020-12-25 at noon — is that noon in Delhi, noon in Düsseldorf, or noon in Detroit, three different moments several hours apart? A LocalDateTime is inherently ambiguous.
You ignored the crucial issue of time zone in determining a date. For any given moment, the date varies around the globe. At one moment it may be “tomorrow” in Australia while simultaneously “yesterday” in Mexico.
The problem lies in what is being represented by these objects. Your question forgets a crucial aspect, which is: What is the type of timeStamp?
I'm guessing it's a java.sql.Timestamp object.
Timestamp, just like java.util.Date, is old API equivalent to Instant.
It represents an instant in time, in the sense that it is milliseconds since jan 1st 1970 UTC. The system has no idea which timezone that was supposed to be in. You're supposed to know; the error, if an error is going to occur here, already occurred before you get to this code. Here's a trivial explanation of how it COULD go wrong:
you start off with a user entering a date in a date field on a webform; it's 2020-04-01.
Your server, running in Amsterdam, saves it to a DB column that is internally represented as UTC, no zone. This is a mistake (you're not saving an instant in time, you're saving a date, these two are not the same thing). What is actually stored in the DB is the exact moment in time that it is midnight, 2020-04-01 in amsterdam (in UTC, that'd be 22:00 the previous day!).
Later, you query this moment in time back into a java.sql.Timestamp object, and you're doing this when the server's tz is elsewhere (say, London time). You then convert this to a localdatetime, and from there to a localdate, and.... you get 2020-03-31 out.
Whoops.
Dates should remain dates. Never convert LocalX (be it Time, Date, or DateTime) to Instant (or anything that effectively is an instant, including j.s.Timestamp, or j.u.Date - yes, j.u.Date does NOT represent a date, it is very badly named), or vice versa, or pain will ensue. If you must because of backward APIs take extreme care; it's hard to test that 'moving the server's timezone around' breaks stuff!

How to check LocalDateTime instance is local or UTC in java?

I have want to find whether my LocalDateTime instance holds local date&time or UTC date&time (like DateTime.Kind property in C#)
LocalDateTime date1=LocalDateTime.now(); // it is local
LocalDateTime date2=LocalDateTime.now(ZoneId.of("UTC")); // it is UTC
Anything like (date1.getKind() == Kind.UTC || date1.getKind() == Kind.Local) in Java?
The LocalDateTime object itself doesn't store the timezone information - it has only the date and time related fields: day, month and year; hour, minute, seconds and nanoseconds. But the now method uses a timezone or an offset to get the correct values for those fields.
That's because the answer to the questions "What day is today?" and "What time is it?" is not as simple as we might think.
It's common to think that the answer is as simple as taking a look at our calendar/cell phone/whatever and seeing the current date/time. But the technically correct answer is: "It depends".
It depends, basically, on where you are. At this moment, each place in the world has its own local date and time. For example, in July 5th, 2017: while it was 14h (or 2 PM) in São Paulo, it was 6 PM in London and 5 PM in UTC, but in Tokyo it was 2 AM of the next day (July 6th).
Each region in the world has specific rules to determine what's their local time during history, and of course it affects their local date.
And the concept that maps a country/city/region to these rules is a timezone.
That's why the now method needs a timezone. The ZoneId object loads all the timezone data to check what's the current date and time in that zone and adjust the day/month/year/hour/minute/second/nanosecond values accordingly. The version that receives no parameters (LocalDateTime.now()) will use the system's default timezone, so the API always uses some timezone in the end.
The timezone (or the offset, such as ZoneOffset.UTC) is used to get the correct values for day, month, year, hour, minute, second and nanosecond, and then - in the case of LocalDateTime and any other classes that don't keep the zone - discarded.
So, the concept might be a little different from what you're thinking. If I do:
// ZoneOffset.UTC is equivalent to ZoneId.of("UTC")
LocalDateTime date = LocalDateTime.now(ZoneOffset.UTC);
What this code does is: "take the current date and time in UTC, and get just the date and time fields, discarding the timezone/offset information".
When I ran this code, the current date/time in UTC was 2017-09-25T12:15:43.570Z, so the LocalDateTime has the value equivalent to 2017-09-25T12:15:43.570 (without any timezone information, just the date and time fields). If I call now() without arguments, it'll use the JVM default timezone (in my case, it's America/Sao_Paulo), and the value will be 2017-09-25T09:15:43.570.
So, with a LocalDateTime you can get the values, but you can't know from which timezone those values came from, because it doesn't keep this information.
If you want a UTC date, you must use another classes, designed to keep this information:
Instant.now() - this will always get the current UTC instant
OffsetDateTime.now(ZoneOffset.UTC) - with this you can query for date and time fields (such as getDayOfMonth() or getHour())
ZonedDateTime.now(ZoneOffset.UTC) - for UTC, it's the same as OffsetDateTime, but if you use a different timezone, it handles all timezone specific data, such as Daylight Saving Time changes.
To check if such object is in UTC, one way is to use the getZone() method:
ZonedDateTime z = ZonedDateTime.now(ZoneOffset.UTC);
System.out.println(z.getZone().equals(ZoneOffset.UTC)); // true
But if you use equivalents like ZoneId.of("UTC"), the equals method return false. So you could also check if z.getZone().getId() is equals to Z or UTC. With OffsetDateTime, it's similar:
OffsetDateTime odt = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(odt.getOffset().equals(ZoneOffset.UTC)); // true
With Instant you don't need to check, because it's always in UTC.
You can check all the available types in Oracle's date/time tutorial.
Both ZonedDateTime and OffsetDateTime can be converted to a LocalDateTime using the toLocalDateTime() method:
// dt will have the current date and time in UTC
LocalDateTime dt = ZonedDateTime.now(ZoneOffset.UTC).toLocalDateTime();
// or
LocalDateTime dt = OffsetDateTime.now(ZoneOffset.UTC).toLocalDateTime();
With this, the dt variable will have all the date and time fields (day/month/year, hour/minute/second/nanosecond) that corresponds to the current date/time in UTC. But it won't keep any timezone/offset information, so the LocalDateTime object itself can't know from which timezone those values came from.
I realize this question is a bit old, but I am learning Java and found myself trying to do something very similar. After some reading I found I could do what you are asking with this:
public static void main (String args[]) {
LocalDateTime now = LocalDateTime.now(Clock.systemDefaultZone()); // The clock argument is not really needed here.
String pattern = "dd MMM yyyy HH:mm:ss"; // Setup your format for output
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
System.out.println("Local time is: " + dtf.format(now));
LocalDateTime utc = LocalDateTime.now(Clock.systemUTC()); // Define alternate timezone
System.out.println("GMT/UTC is: "+dtf.format(utc));
}

How to keep original timezone with JodaTime

I have date in String format I need to parse. The format is as following with timezone from all over the world :
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
It seems that java.util.Date doesn't accept timezone with : separator. So I'm trying with Jodatime library :
DateTime formattedDate = DateTimeFormat.forPattern(pattern).parseDateTime(stringDate);
LocalDateTime formattedDate2 = DateTimeFormat.forPattern(pattern).parseLocalDateTime(stringDate);
MutableDateTime formattedDate3 = DateTimeFormat.forPattern(pattern).parseMutableDateTime(stringDate);
System.out.println(formattedDate);
System.out.println(formattedDate2);
System.out.println(formattedDate3);
These lines output :
2016-04-29T09:46:49.222+02:00
2016-04-29T12:16:49.222
2016-04-29T09:46:49.222+02:00
As far as I understand the formatter modify output timezone to comply on mine (I'm in Paris, UTC+2), but I want the output keep its original timezone. Is it possible to do it with Jodatime library? Or should I change for another?
Edit :
Actually I need to get a Date object on which the timezone offset would be 270 (the timezone offset of the stringDate : 4 hour and 30 minutes) in place of 120 (my local timezone offset):
System.out.println(formattedDate.toDate().getTimezoneOffset()); // I expect 270 but I get 120
What you missed is DateTimeFormatter#withOffsetParsed:
Returns a new formatter that will create a datetime with a time zone equal to that of the offset of the parsed string.
Otherwise the formatter will parse it into your local time zone (surprising, I know).
#Test
public void preserveTimeZone() {
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
DateTime dt = DateTimeFormat.forPattern(pattern).withOffsetParsed().parseDateTime(stringDate);
System.out.println(dt); // prints "2016-04-29T12:16:49.222+04:30"
}
As for your edit - java.util.Date does not hold time zone information and the deprecated getTimezoneOffset() method only
Returns the offset, measured in minutes, for the local time zone relative to UTC that is appropriate for the time represented by this Date object.
So you'd better use Joda Time or java.time classes to handle time zones properly.
When I run the same code that you have posted, I end up with
2016-04-29T02:46:49.222-05:00
2016-04-29T12:16:49.222
2016-04-29T02:46:49.222-05:00
which if you will notice, has different hour values AND time-zone values. However, if you look at their millis:
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
you'll see the output
1461916009222
1461950209222
1461916009222
So they have the same epoch time, but are printed out differently. This is due to the mechanism of toString() on DateTime objects, and how they are to be interpreted.
DateTime and LocalDateTime(MutableDateTime is just a mutable version of DateTime) deal with the same epoch time in different ways. LocalDateTime will always assume that epoch time is UTC time(per the javadoc for LocalDateTime), while DateTime will assume that epoch is represented in the time zone of the Chronology which it holds(per the javadoc again). If the TimeZone is not specified at construction time, then the Chronology will assume that you want the timezone of your default Locale, which is set by the JVM. In your case, the default Locale is Paris France, while mine is St. Louis USA. Paris currently holds a +2:00 time zone offset, while St. Louis has -5:00, leading to the different time zone representations when we print it.
To get even more annoying, those offsets can change over time. If I come back in 6 months and try to answer this again, my values will show -6:00 (stupid Daylight savings time!)
The important thing to remember is that these two dates have the same epoch time: we are talking about the same instant in time, we are just representing that time differently when we print it out.
If you want to use a different time zone for representing the output of the parse result, then you can set the DateTimeZone during formatting using DateTimeFormat.withZone() or DateTimeFormat.withLocale:
DateTimeFormatter sdf = DateTimeFormat.forPattern(pattern).withZone(DateTimeZone.forOffsetHoursMinutes(4,30));
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
which will print
2016-04-29 12:16:49.222+0430
2016-04-29 12:16:49.222
2016-04-29 12:16:49.222+0430
notice that the LocalDateTime version still prints out without the TimeZone. That's kind of the feature of LocalDateTime: it is represented without having to deal with all this business.
So that is why your printing values look weird. To further your question about getting a java.util.Date object from the parsed DateTime object: toDate will give you a java.util.Date which represents the same epoch time. However, java.util.Date behaves similarly to DateTime, in that unless otherwise stated, it will use the TimeZone of the default Locale. If you know the Locale ahead of time, then you can use the toDate(Locale) method to ensure you use that Locale's TimeZone offset.
It gets a lot harder if you don't know the TimeZone ahead of time; in the past, I've had to hand-parse the TimeZone hour and minute offsets to determine the proper TimeZone to use. In this exact case that's not too difficult, since the last 6 characters are extremely well-formed and regular(unless, of course, they aren't :)).

Is this the correct way to obtain a java.sql.Timestamp at UTC from a Date?

I develop a SonarQube plugin and for one of my needs I need to store the analysis date of a project as an SQL TIMESTAMP (Please note: a TIMESTAMP, and not a TIMESTAMP WITH TIMEZONE).
Here is how I currently do it:
// In the SonarQube Sensor
// .getAnalysisDate() returns a java.util.Date
final Instant instant = module.getAnalysisDate().toInstant();
// Timestamp at UTC from the Instant
final LocalDateTime dt = LocalDateTime.frominstant(instant, ZoneOffset.UTC);
final Timestampt ts = Timestamp.valueOf(dt);
I have a little trouble grasping the concept of an Instant, there is also ZonedDateTime etc...
Anyway, this seems to do what I want, but is it the correct way?
To store a UTC TIMESTAMP in your DB, you need to create a Java Timestamp that represents the date of your report (say 8th November 7pm UTC), but in the local time zone without conversion (say 8th November 7pm CET). So your approach is correct: get the LocalDateTime of the analysis date in UTC (8th November 7pm) and create a Timestamp in your local time zone at that LocalDateTime.
I don't think there is a shorter/better way to do it. If you used a sql TIMESTAMP WITH TIME ZONE field you would not have to do any manipulations and Date.from(Instant) would produce the correct result.
Clarification of the concepts involved, using the time at which you posted your question as an example (Sunday 8th November 2015 at 7pm UTC) and assuming your local time zone is CET (Central European Time = UTC+1):
the Java Timestamp will be the number of milliseconds since the epoch, i.e. it represents the unique instant on the time line at which you posted your question and does not have any time zone information
when storing that Timestamp into a TIMESTAMP (i.e. without time zone) field, the jdbc driver will calculate the date/time corresponding to your Timestamp in the default time zone (unless a Calendar is explicitly provided) - so your DB will show Sunday 8th November at 8pm
a java.time.Instant is similar to a Java Timestamp: it represents a unique point in time, without time zone information
a LocalDateTime is like a sql TIMESTAMP, it says, for example, Sunday 8th November 8pm, but you don't know what point in time that is without additional time zone information
a ZonedDateTime is essentially a LocalDateTime + a time zone. For example Sunday 8th November 8pm [Europe/Paris] - that generally identifies a unique instant but not necessarily (think of when clocks change backward for DST and the same hour is repeated twice).
an OffsetDateTime is essentially a LocalDateTime + an offset vs. UTC. For example Sunday 8th November 8pm +01:00. That identifies a unique instant in time.
The standard approach is generally to store an instant as a sql TIMESTAMP WITH TIME ZONE and use either a Timestamp or an OffsetDateTime on the Java side of things.
If performance matters, I would use the following:
final long timeAtLocal = module.getAnalysisDate(); // or System.currentTimeMillis(); or new Date().getTime(); etc.
final long offset = TimeZone.getDefault().getOffset(timeAtLocal);
final Timestamp timeAtUTC = new Timestamp(timeAtLocal - offset);
Timestamp.from(instant) is all you should need.
Neither java.sql.Timestamp nor java.time.Instant have a timezone so you don't need to convert to UTC.
Alternatively directly from java.util.Date
long millisSinceEpoch = module.getAnalysisDate().getTime();
Timestamp timestamp = new Timestamp(time);

Time Zones in Java / GWT (Client-side)

[Client-side GWT class]
I have a Date Object...
Date dataObject = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
.parse("2009-10-12T00:00:00.000);
This works fine. However when I do a:
dateObject.getTime();
It returns a UNIX Time milliseconds using a GMT with daylight savings, therefore making it a UNIX Time I cannot use. I need it in UTC. How do I do this?
Currently I'm parsing a date and it is giving me back:
'Thu Apr 16 08:46:20 GMT+100 2009' # '1239867980191'
However the date I'm passing in is 1 hour less than this time (7:46 and not 8:46!).
How do I pass in the fact it's UTC? Or if it can't use UTC (which would be ridiculous), how do I use GMT without the daylight savings?
Your last edit makes things clearer.
Basically, you are confused, and you already get what you want.
1239867980191 milliseconds since the Epoch translates to Thursday, April 16th, 2009, at 7:46:20.191 in the GMT time zone. The very same instant translates to the same day, but 8:46:20.191 in the GMT+01 time zone. If your input string specified "7:46:20.191" and you indeed got 1239867980191 from Date.getTime() then congratulations, the parsing code understood your "7:46:20.191" as to be interpreted in the GMT time zone, and did it properly.
If afterwards you get "8:46:20" when printing, this is only because you use the GMT+01 time zone for displaying that instant. Note that the string contains GMT+100 precisely to notify you that it uses that time zone for display purposes. The instant which the Date instance represents is nonetheless exactly the instant you wish it to contain. Remember that a Date instance represents an instant in time, for which no notion of time zone applies: time zones are used to convert instants into calendar elements (days, hours...) and back.
To convert a Date to a displayable string, use DateTimeFormat.format(Date, TimeZone) which lets you specify which time zone you want to use for that string.
Since the Calendar class is not supported in GWT, maybe something hackish like this will work:
final String timezone = "GMT-07:00";
DateTimeFormat dtf = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ");
long unix = dtf.parse("2009-10-12T00:00:00" + timezone).getTime();
This way you can provide the correct timezone info - though, that should be the default behaviour.
It is the other way round. A Date instance holds the time in milliseconds since the Epoch, using the UTC time scale (i.e. leap seconds are ignored). This is what Date.getTime() returns and that's what you want.
The culprit here is the parser, which interprets the date you give as a string in your local time zone. If you want DateTimeFormat to interpret the string as a date-and-time given in the UTC time zone, append an explicit time zone to the parsed string:
DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ")
.parse("2009-10-12T00:00:00.000" + " GMT");
(The above assumes that I understood GWT documentation properly; I have not tried.)
Just to be clear in my notations: for all practical purposes, there is no difference between "GMT" and "UTC", and there is no daylight saving in the GMT time zone. Other time zones are often defined as "GMT plus or minus some offset" and the offset may change between summer and winter. For instance, the time zone in New York is somewhat equivalent to "GMT-04" in summer and "GMT-05" in winter.
I keep seeing formats with ZZZZ being suggested... but why?
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" would match
"2009-10-12T00:00:00.000-0000"
The last part being the offset from UTC; California (to use someone else's example time) would be -0800, -0700 in summer.
As a side note, GMT is also always -0000. That's why Britain's summer time zone is BST (British Summer Time, +0100).
Try the Calendar object.
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Date dataObject = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
.parse("2009-10-12T00:00:00.000);
cal.setTime(dataObject);
cal.getTimeInMillis();
According to the API, getTimeInMillis() returns "the current time as UTC milliseconds from the epoch."
EDIT: as _bravado pointed out, the Calendar API is currently not available for GWT (Issue 603). While this would get the appropriate time in a Java application, it isn't going to work here. There is information in the group about using GMT.
EDIT: Missing a closing bracket on the the Calendar.getInstance() call

Categories