Converting Time Zones from a String to UTC (JODA TIME) - java

I have a stored UTC timestamp in the database. When I retrieve that UTC timestamp I cast it into a String. I want to take that UTC Timestamp String and convert it to the device's local time using Joda Time. Anyone who could possibly help out with this. It would be very appreciated! Here is what I am doing right now:
String date = ""+ds.child("datecreated").getValue();
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
DateTime dt = formatter.parseDateTime(date);
DateTime dt2 = new DateTime(dt).toDateTime(DateTimeZone.getDefault());
String personalDate = dt2.toString();
dateTV.setText(personalDate);
System.out.println("THIS IS THE FIRST TIME: " + dt + "THIS IS THE SECOND TIME: " + dt2);
The problem is is that it is giving me the exact same time when I convert it to my local time, which it shouldn't be doing since it is being stored in UTC and I am converting to Eastern Standard Time which is my phone's default.

To show that Andreas in the comment has hit the nail right on: I ran the following snippet in America/Coral_Harbour time zone (since I didn’t know your exact time zone, Eastern Standard Time is used in several (though fewer after 8 March when Eastern Daylight Time began)).
String date = "2020-03-12T01:23:45.678+0000";
System.out.println("This is the string: " + date);
DateTime dt = new DateTime(date);
DateTime dt2 = new DateTime(dt).toDateTime(DateTimeZone.getDefault());
System.out.println("This is the first time: " + dt);
System.out.println("This is the second time: " + dt2);
Output is:
This is the string: 2020-03-12T01:23:45.678+0000
This is the first time: 2020-03-11T20:23:45.678-05:00
This is the second time: 2020-03-11T20:23:45.678-05:00
Compare the first two lines and notice that the conversion from UTC to EST has already happened when parsing the string.
As an aside, since your string is in ISO 8601 format, you don’t need to specify any formatter for parsing it. The DateTime(Object) constructor accepts it. But the same conversion happened in your parsing.
What happened in your code?
Repeating the quote from Andreas’ comment:
If the withOffsetParsed() has been called, then the resulting
DateTime will have a fixed offset based on the parsed time zone.
Otherwise the resulting DateTime will have the zone of this formatter,
but the parsed zone may have caused the time to be adjusted.
So your formatter has the default time zone of your device, and therefore also the DateTime object that you get from parsing.
So when creating dt2 you were converting from Eastern Standard Time to Eastern Standard Time and therefore got the same date-time again.
Link: Documentation of DateTimeFormatter.parseDateTime()

The part that you are missing is that, as the JavaDoc states, a DateTime object, internally, is represented by the number of "milliseconds from the Java epoch of 1970-01-01T00:00:00Z."
So, a DateTime does not have a time zone. Two DateTime objects that represent the same instant in time are exactly the same, regardless of the time zone represented in the string that you parsed it from.
A DateTime only "has" a timezone when you format it.
When you format it, you get a string with timezone that you requested, and the time representation adjusted accordingly.
The reason that the two dates (dt and d2) look the same to you is because you did not format them (you used their implicit "toString()" methods), so both got formatted with your local time zone.

Related

Joda Time to Java Time Migration show different result while migration

Given the following code
public static void main(String[] args) {
org.joda.time.format.DateTimeFormatter _timestampFomatNYCJoda = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd HHmmss.SSS").withZone(DateTimeZone.forID("America/New_York"));
DateTimeFormatter _timestampFomatNYC = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS").withZone(ZoneId.of("America/New_York"));
LocalDateTime localDateTime = LocalDateTime.now();
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime();
System.out.println("System Time " + new Date());
System.out.println("Java Version " + localDateTime.format(_timestampFomatNYC));
System.out.println("Joda Version " + _timestampFomatNYCJoda.print(jodaLocalDateTime.toDateTime(DateTimeZone.UTC)));
}
Why does the Java Version and Joda Version dont match ? I am running this on IST clock.
Below is the output
System Time Fri Mar 27 17:01:33 IST 2020
Java Version 20200327 170133.933
Joda Version 20200327 130133.938
I can reproduce your results. I can also explain them. Joda-Time and java.time have been designed to behave differently in this case. Let’s look at them in turn.
Joda-Time
In Joda-Time DateTimeFormatter.withZone() gives you a formatter with an override zone, that is, a zone that will always be used for formatting dates and times. In other words, any date and time will be converted to this zone for printing. The documentation says:
When printing, this zone will be used in preference to the zone from
the datetime that would otherwise be used.
When you do new org.joda.time.LocalDateTime(), you are getting a LocalDateTime representing the current date and time in your default time zone. The Local in some class names means without time zone or offset from UTC. I figure that you must have got a value equal to 2020-03-27T17:01:33.938.
Apparently what happens when you format a LocalDateTime with a formatter with an override zone, is that the formatter assumes that your LocalDateTime is in UTC (which yours isn’t) and converts it from there, in your case to America/New_York time zone. Since summer time (DST) is in effect in New York, the offset is -04:00, so 17:01 becomes 13:01.
This is the wrong result. When the time is 17:01 in your time zone, it is not 17:01 UTC, so the conversion is based on a false premise. It is also not 13:01 in New York, so the converted result is telling a lie.
java.time
With java.time setting an override zone on a formatter works similarly for formatting, but with a difference that matters here: the override zone is only used when printing a date-time object that identifies an instant (a point in time). From the docs:
When formatting, if the temporal object contains an instant, then it
will be converted to a zoned date-time using the override zone.
Whether the temporal is an instant is determined by querying the
INSTANT_SECONDS field. If the input has a chronology then it will be
retained unless overridden. If the input does not have a chronology,
such as Instant, then the ISO chronology will be used.
… In all other cases, the override zone is added to the temporal,
replacing any previous zone, but without changing the date/time.
Again LocalDateTime.now() gives you the current date and time of day (a few milliseconds earlier than the query through Joda-Time), 2020-03-27T17:01:33.933. Local still means without offset or time zone.
Because your LocalDateTIme hasn’t got offset or time zone, it cannot identify an unambigous point in time, an instant. Therefore when formatting it neither the date nor the time of day is changed. And since your format pattern contains no time zone or offset, none is printed. So you just get the date and time in your time zone (not in New York), 20200327 170133.933.
To get the date and time in New York time zone
DateTimeFormatter timestampFormat
= DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS");
ZonedDateTime timeInNy = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(timeInNy.format(timestampFormat));
When I ran this code just now, the output was:
20200327 122359.683
Documentation links
Joda-Time DateTimeFormatter.withZone()
java.time DateTimeFormatter.withZone()

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

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

Convert UTC to LocalDateTime in Joda?

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

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