Convert UTC to LocalDateTime in Joda? - java

DateTime dt = new DateTime("2014-09-15T21:20:14");
System.out.println(dt);
System.out.println(dt.plusMillis(581042272).toDateTime().toLocalDateTime().toDateTime(DateTimeZone.forID("GMT")));
the time in dt is in UTC, I want to set the time in dt plus milliseconds to GMT? However, the time is still printed as UTC (1 hour behind GMT). How can I set it so it's one hour in front?
2014-09-15T21:20:14.000+01:00
2014-09-22T14:44:16.272Z
I know the time is exactly one hour behind because I made this request at 15:44:16 GMT

Your DateTime is actually not in UTC - it's in the system default time zone. To fix it, you just need to tell it that the value you're passing in is in UTC:
DateTime dt = new DateTime("2014-09-15T21:20:14", DateTimeZone.UTC);
System.out.println(dt);
DateTime other = dt.plusMillis(581042272);
System.out.println(other);
Output:
2014-09-15T21:20:14.000Z
2014-09-22T14:44:16.272Z
Also note that you can't have made the request at 15:44:16 GMT, as that hasn't occurred yet. At the time I'm writing this, it's 16:05 British Summer Time, therefore 15:05 GMT. It's important to understand that the time zone in the UK isn't "GMT" - that's just the part of the time zone when we're not observing daylight savings.
If you want to convert to the UK time zone, you want:
DateTime other = dt.plusMillis(581042272)
.withZone(DateTimeZone.forID("Europe/London"));

For those that have trouble with converting datetime from a server to local datetime:
1.Make sure the server gives you a UTC time, meaning, the format should contain a timezone.
2.Convert with pattern, if the api does not give you an timezone, then you might get an exception because of the last 'Z'.
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
DateTime dt = formatter.parseDateTime(currentPost.postDate);
3.to check the time offset (optional)
DateTimeZone ActualZone = dt.getZone();
4.Convert to local time
TimeZone tz2 = TimeZone.getDefault();
DateTime localdt = new DateTime(dt, DateTimeZone.forID(tz2.getID()));
(if you control the API yourself, and it happens to be an asp.net api, check this, to set the Kind of the datetime, even though you might have saved it as UTC time in the database, you will send the datetime with the default server timezone)

val marketCentreTime = timeInAnotherTimezone.withZone(DateTimeZone.forID("yourCountryName/andyourCityName"));

Related

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

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).

Convert UTC java.sql.Time to java.time.localtime with correct DST

I have a problem to convert a java.sql.Time (UTC) which is fetched from a database to a java.time.LocalTime (GMT+1 DST). It is always missing the DST hour. So like a Time of 03:00 is only converted to a LocalTime of 04:00 instead of 05:00.
//Saved UTC time in DB: 03:00
LocalTime.ofInstant(Instant.ofEpochMilli(sqlTime.getTime()), ZoneId.of("Europe/Berlin"));
=> 04:00 //expected 05:00
I guess the problem is that java.sql.Time saves the time with a default date of 1970-01-01 and in 1970 there was no DST in Germany. But of course the time should be shown for today and not for 1970.
So how can I get the correct time for this example?
Assuming that you are using at least JDBC 4.2, you should be able to retrieve a LocalTime from your result set:
LocalTime timeInUtc = yourResultSet.getObject(yourTimeColumn, LocalTime.class);
Then there’s no need bother with the outdated and poorly designed java.sql.Time class. The time you get will still be in UTC, of course. Here’s how to convert:
LocalTime timeInUtc = LocalTime.of(3, 0);
ZoneId zone = ZoneId.of("Europe/Berlin");
LocalTime timeInGermany = OffsetDateTime.now(ZoneOffset.UTC)
.with(timeInUtc)
.atZoneSameInstant(zone)
.toLocalTime();
System.out.println("Zeit heute in Deutschland: " + timeInGermany);
When I ran the code today I got the output you expected:
Zeit heute in Deutschland: 05:00
Edit: If there’s no way you can avoid getting a java.sql.Time, convert it to LocalTime first. Assuming that the Time is in UTC and we don’t want to rely on a fragile JVM time zone setting for conversion, you are correct that we need the getTime method:
Time sqlTimeInUtc = // Get from database
LocalTime timeInUtc
= LocalTime.MIDNIGHT.plus(sqlTimeInUtc.getTime(), ChronoUnit.MILLIS);
If you could rely on the JVM time zone setting also being UTC, the following would be nicer:
LocalTime timeInUtc = sqlTimeInUtc.toLocalTime();
In both cases the rest is as above.
In all cases there are some corner cases around the question whether you want “today in UTC” or “today in Europe/Berlin time zone” when you say “the time should be shown for today”. There’s also a corner case if the time is between 2 and 3 AM and today is the last Sunday in March, where the clocks are turned forward from 2 to 3 to initiate summer time (DST) in Germany. Please think these corner cases through and decide what you want.
By the way your diagnosis is completely correct: Time.getTime returns the time of day on Jan 1, 1970, so when you feed this into an Instant, you are converting the time of day on this date, that is, without summer time.
As far as I understand it your question is: Given a time in UTC convert it to local time according to the current time offset. This time offset is different depending of whether DST is in effect or not.
A possible approach is to determine the current offset using TimeZone:
TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
int timeZoneOffsetMillis = tz.getOffset(new Date().getTime());
Now timeZoneOffsetMillis contains the number of milliseconds you have to add to your UTC time to get local time.
You can get a LocalTime like this:
LocalTime localTime = LocalTime.ofNanoOfDay((sqlTime.getTime() + timeZoneOffsetMillis) * 1000000L);
If your time is only accurate to seconds instead of nanoseconds anyway you might want to use LocalTime.ofSecondOfDay.

Converting time to UTC using java 8

I have some APIs in Java which accept times as strings.
The UI sends plain string like "10:00:00". I am creating a LocalTime out of that and saving in the db (MySQL).
Later in another API I use a LocalDate object and the above time with Zone UTC to create a ZonedDateTime object that is saved to the db. My problem is that the time is not getting converted to UTC.
ZonedDateTime.of(LocalDate.now(ZoneId.of("UTC")),LocalTime.now(ZoneId.of("UTC")), ZoneId.of("UTC"));
ZonedDateTime.of(LocalDate.now(ZoneId.of("UTC")),dto.getStart(), ZoneId.of("UTC"));
Both of these are different though I am sending for eg 07:00:00 which is my time in India. Please guide me as to how I should convert the time alone to UTC.
EDIT:: I have set jvm Duser.Timezone to UTC.When I do this:
ZonedDateTime.of(LocalDate.now(ZoneId.of("UTC")),dto.getStart(), ZoneId.of("UTC")).getOffset().getTotalSeconds();
It gives 0 as seconds
EDIT::
ZoneId z = ZoneId.of("Asia/Calcutta");
ZoneId z1 = ZoneId.of("UTC");
TimeZone.getTimeZone(z).getRawOffset();
Tried this and it gives the diff in ms. I will try using this provided the UI sends the actual local zone. Will update..
If the input corresponds to your local time, you can create a ZonedDateTime to get the current date in India timezone, set the time and then convert it to UTC:
LocalTime time = LocalTime.parse("10:00:00"); // 10 AM
ZonedDateTime utc = ZonedDateTime
.now(ZoneId.of("Asia/Calcutta")) // current date/time in India
.with(time) // set time to 10 AM
.withZoneSameInstant(ZoneOffset.UTC); // convert to UTC
The value of variable utc will be 2018-01-29T04:30Z (running the code today, January 29th). The time in UTC is 4:30 AM, which is equivalent to 10 AM in India.
I believe that MySQL can't save a ZonedDateTime, so you can first convert it to an Instant by calling utc.toInstant(). Or if the driver you use is an older version and it works only with java.util.Date, you can convert it using Date.from(utc.toInstant()).
Also note that I used the constant ZoneOffset.UTC, which is equivalent to (but better than, IMO) calling ZoneId.of("UTC").
For now We have decided to allow the UI to change the time to whatever is its timezone. JVM timezone for the server is set to UTC...
I would advise against storing a ZonedDateTime in a MySQL database. MySQL timestamps don't store the timezone information so you can't guarantee you'll get back the same time that you save.
Instead I would store a LocalDateTime and standardise on UTC for internal times and then format with a timezone when you need to display etc. To get the current time in UTC you can do something like this.
LocalDateTime now = LocalDateTime.now(Clock.systemUTC());
To build a UTC timestamp for today and a provided time you could do something like this.
LocalTime time = getTime();
LocalDateTime = LocalDateTime.of(LocalDate.now(Clock.systemUTC()), time);
To build a timestamp using the date for the current timezone, a provided time and then convert to UTC for saving to the database.
ZoneId timezone = ZoneId.of("Asia/Calcutta");
LocalTime time = getTime();
ZonedDateTime zdt = ZonedDateTime.of(LocalDate.now(timezone), time, timezone);
LocalDateTime utcDateTime = LocalDateTime.ofInstant(zdt.toInstant(), ZoneOffset.UTC);

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);

Convert Time from one time zone to another using Java 8 Time

I am trying to convert Date with GMT +5:30 to EST with java 8 ZonedDateTime.
String inputDate = "2015/04/30 13:00";
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm", Locale.US);
LocalDateTime local = LocalDateTime.parse(inputDate, sourceFormatter);
// local : 2015-04-30T13:00
//Combining this local date-time with a time-zone to create a ZonedDateTime.
ZonedDateTime zoned = local.atZone(TimeZone.getTimeZone("GMT+5:30").toZoneId());
// zoned : 2015-04-30T13:00+05:30[GMT+05:30]
ZonedDateTime zonedUS = zoned.withZoneSameInstant(TimeZone.getTimeZone("GMT-5:00").toZoneId());
// zonedUS : 2015-04-30T02:30-05:00[GMT-05:00]
I am expecting 3:30 AM EST but what I am getting is 2:30 AM EST as 1 PM IST= 3:30AM EST. What am I missing?
It seems that whatever service you found was being over-helpful in interpreting what you meant and assumed North American Eastern Daylight Time (EDT) when you specified EST (Eastern Standard Time). Most, not all of the places using EST as standard time are using daylight saving time and hence were on EDT or offset UTC-04:00 on the date you use, April 30, 2015.
If it makes sense in your situation, you should always prefer to give time zone in the region/city format, as Asia/Kolkata and America/New_York. If you intended Eastern Time as in New York or Montréal, one may say that your “time zone” of GMT-5:00 was wrong and the cause of your unexpected result.
So your code becomes for example:
String inputDate = "2015/04/30 13:00";
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm", Locale.US);
LocalDateTime local = LocalDateTime.parse(inputDate, sourceFormatter);
// local : 2015-04-30T13:00
//Combining this local date-time with a time-zone to create a ZonedDateTime.
ZonedDateTime zoned = local.atZone(ZoneId.of("Asia/Kolkata"));
// zoned : 2015-04-30T13:00+05:30[Asia/Kolkata]
ZonedDateTime zonedUS = zoned.withZoneSameInstant(ZoneId.of("America/Montreal"));
// zonedUS : 2015-04-30T03:30-04:00[America/Montreal]
I have made one other change: When using the modern classes from java.time, there is no point in also using the outdated TimeZone class, so I have taken that out. The code is slightly simpler, and more importantly, ZoneId.of(String) includes validation of your time zone string so you will discover any spelling error in the time zone name (like when I just happened to type a ( instead of the / in Asia/Kolkata — such happens all the time).
Most of the above has already been said in comments by Jon Skeet and others. I thought it deserved to go into an answer so it’s plain to see that the question has been answered.
Though the question is old, felt like I could add more to the accepted answer.
A ZonedDateTime is different from an OffsetDateTime.
I would prefer to use ZonedDateTime when I'm getting a time for a specific location like "Asia/Kolkata", "Asia/Shanghai", "US/Pacific" (this time zone will change depending on the day of the year because of Daylight savings).
To illustrate with an example,
var pacific = ZonedDateTime.of(2020,11,01,1,59,0,0,ZoneId.of("US/Pacific"))
var afterAnHour = pacific.plusHours(1)
This will give me a time of
2020-November-01 01:59:00.000 AM -07:00[US/Pacific]
And if i add an hour to it, it will give me a time of
2020-November-01 01:59:00.000 AM -08:00[US/Pacific]
You can see that the hour component is same even after adding an hour to the time. This is because the daylight savings time has kicked in and the time zone is shifted from -07:00 to -08:00.
Now if i use an OffsetDateTime look what happens.
var offsetNow = OffsetDateTime.of(2020,11,01,1,59,0,0,ZoneOffset.of("-07:00"))
var offsetAfterAnHour = offsetNow.plusHours(1)
The offsetNow will be,
2020-November-01 01:59:00.000 -07:00
And adding an hour to it will be,
2020-November-01 02:59:00.000 -07:00
you can see that the hour component has become 2 after adding an hour.
The key point is a ZonedDateTime uses ZoneRules to calculate important properties like Daylight savings time so that it can adjust the time zone accordingly.
While the OffsetDateTime will not change the zone offset for anything.

Categories