Hi I have a table from which I get a date like "2017-03-24 06:01:33" which is in UTC. In my java program I have to receive it as a string. How to convert it to a local date string using client's offset hour and minute?
I would do:
OffsetDateTime odt = LocalDateTime.parse(dateFromDb, DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"))
.atOffset(ZoneOffset.UTC);
String localDateString = odt.atZoneSameInstant(ZoneOffset.ofHoursMinutes(5, 30))
.toLocalDateTime()
.toString();
I am assuming you have got the client’s time zone as an offset in hours and minutes. Then you can pass those into from ZoneOffset.ofHoursMinutes(). Beware that if the client time zone uses summer time (daylight savings time) some of the year (as is the case in greater parts of the USA), you need to be sure you get the offset for the time of year where the date falls. Other ways of specifying the client time zone would be ZoneId.systemDefault() or ZoneId.of("Asia/Kolkata") — such would know the summer time rules and hence may be safer.
The code prints:
2017-03-24T11:31:33
You notice it’s five and a half hours ahead of the input string as expected.
If you only require the date, not the time, use toLocalDate() instead of toLocalDateTime().
All of the above requires Java 8. Or the backport of the Java 8 date and time classes to Java 6 and 7, see ThreeTen Backport. If you cannot use Java 8 and do not want to depend on the backport, find your inspiration in this question: Not able to parse UTC date time to EST local time.
Use this
simpleDateFormat.setTimeZone("Your-Client's-Timezone");
Related
I am migrating an application from jdk 8 to 11 and I can see ZonedDateTime change is behavior about daylight saving time.
JDK8
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
output:
2037-05-10T19:15+02:00[Europe/Paris]
JDK11/12
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
2037-05-10T20:15+02:00[Europe/Paris]
Can someone explain to me why did they change this behavior ?
Best regards,
It’s a known bug in Java 8: JDK-8066982
I believe that what you are experiencing in Java 8 is really this bug: ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition. The bug title doesn’t tell the whole story. The real issue is that in Java 8 DateTimeFormatter.ISO_ZONED_DATE_TIME (which is implicitly used by the one-arg ZonedDateTime.parse that you use) ignores the offset if a time zone ID is included in the parsed string. This in combination with a time zone database that disagrees with your string about the offset used in Paris in October 2037 causes a moment in time to be parsed that conflicts with the offset in the string.
The bug is fixed in Java 9. So in Java 9, 10 and 11, since the same disagreement about offset is still there, the moment parsed is based on the offset of the string. It is then converted to the time zone from the string using the rules from the time zone database. This causes the offset to be changed from +01:00 to +02:00 and the hour of day correspondingly from 19:15 to 20:15. I agree with Java 9+ that this is the correct behaviour.
Don’t use ZonedDateTime for far-future dates
Your problem is also partly caused by using ZonedDateTime for a future date. This is only recommended for the very near future where we assume that no zone rules are changed. For a date and time in 2037, you should either use an Instant if you know the moment in time, or a LocalDateTime if you know just the date and time of day. Only when the time draws near and you trust that your Java installation has got the last time zone updates, convert to a ZonedDateTime.
As has been discussed in the comments, we probably don’t know the correct UTC offset for Paris in October 2037 yet. It seems that EU is likely to abandon summer time (DST) from 2021, and as far as I know, the French politicians have not yet decided what the time will be in France after that.
What if we wanted the time of day from the string?
To get the time from the string (19:15), parse into a LocalDateTime:
String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
LocalDateTime dateTime
= LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("Date and time from string: " + dateTime);
Output is (run on Java 11):
Date and time from string: 2037-05-10T19:15
In case you wanted the full Java 8 behaviour on a later Java version — as I mentioned, it’s not recommended, you shouldn’t use ZonedDateTime here:
TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
LocalDateTime dateTime = LocalDateTime.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime java8Zdt = dateTime.atZone(zone);
System.out.println("Time from string in zone from string: " + java8Zdt);
Time from string in zone from string: 2037-05-10T19:15+02:00[Europe/Paris]
I am migrating an application from jdk 8 to 11 and I can see ZonedDateTime change is behavior about daylight saving time.
JDK8
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
output:
2037-05-10T19:15+02:00[Europe/Paris]
JDK11/12
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
2037-05-10T20:15+02:00[Europe/Paris]
Can someone explain to me why did they change this behavior ?
Best regards,
It’s a known bug in Java 8: JDK-8066982
I believe that what you are experiencing in Java 8 is really this bug: ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition. The bug title doesn’t tell the whole story. The real issue is that in Java 8 DateTimeFormatter.ISO_ZONED_DATE_TIME (which is implicitly used by the one-arg ZonedDateTime.parse that you use) ignores the offset if a time zone ID is included in the parsed string. This in combination with a time zone database that disagrees with your string about the offset used in Paris in October 2037 causes a moment in time to be parsed that conflicts with the offset in the string.
The bug is fixed in Java 9. So in Java 9, 10 and 11, since the same disagreement about offset is still there, the moment parsed is based on the offset of the string. It is then converted to the time zone from the string using the rules from the time zone database. This causes the offset to be changed from +01:00 to +02:00 and the hour of day correspondingly from 19:15 to 20:15. I agree with Java 9+ that this is the correct behaviour.
Don’t use ZonedDateTime for far-future dates
Your problem is also partly caused by using ZonedDateTime for a future date. This is only recommended for the very near future where we assume that no zone rules are changed. For a date and time in 2037, you should either use an Instant if you know the moment in time, or a LocalDateTime if you know just the date and time of day. Only when the time draws near and you trust that your Java installation has got the last time zone updates, convert to a ZonedDateTime.
As has been discussed in the comments, we probably don’t know the correct UTC offset for Paris in October 2037 yet. It seems that EU is likely to abandon summer time (DST) from 2021, and as far as I know, the French politicians have not yet decided what the time will be in France after that.
What if we wanted the time of day from the string?
To get the time from the string (19:15), parse into a LocalDateTime:
String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
LocalDateTime dateTime
= LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("Date and time from string: " + dateTime);
Output is (run on Java 11):
Date and time from string: 2037-05-10T19:15
In case you wanted the full Java 8 behaviour on a later Java version — as I mentioned, it’s not recommended, you shouldn’t use ZonedDateTime here:
TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
LocalDateTime dateTime = LocalDateTime.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime java8Zdt = dateTime.atZone(zone);
System.out.println("Time from string in zone from string: " + java8Zdt);
Time from string in zone from string: 2037-05-10T19:15+02:00[Europe/Paris]
public static void main(String[] args) {
Timestamp ts = new Timestamp(116, 02, 12, 20, 45, 0, 0);
Date d = new Date();
d.setTime(ts.getTime());
System.out.println(d);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
//System.out.println(ts.getTime());
System.out.println(simpleDateFormat.format(ts));
System.out.println(Timestamp.valueOf(simpleDateFormat.format(ts)));
}
In above code last two lines print different values. Current time zone is CST, I wanted to convert it into UTC. When I convert it Last two lines print different values by one hour i.e., last but one print 13 mar 2:45 am and last print 13 Mar 3:45 am. Why they are different and How can I correct it.
Java 8
Instant inst = LocalDateTime.of(2016, Month.MARCH, 12, 20, 45)
.atZone(ZoneId.of("America/Chicago"))
.toInstant();
System.out.println(inst);
This prints
2016-03-13T02:45:00Z
Today you should not (normally) have a need for a Timestamp object. The java.sql.Timestamp class is long outdated. Once we used it for transferring timestamp values with nanosecond precision to and from SQL databases. Today we use the Instant class for this instead. Instant is one of the classes of java.time, the modern Java date and time API (sometimes we use LocalDateTime from the same API, it depends on your exact requirements and the datatype of your database column).
Neither a Timestamp nor an Instant have a time zone in them. Unlike Timestamp the Instant always prints in UTC (denoted by the Z at the end of the above output). As you can see, the above snippet has correctly converted your time of 20:45 CST to 02:45 the next day UTC.
If you do need a timestamp, typically for a legacy API that you cannot change or don’t want to change just now, conversion is easy:
Timestamp ts = Timestamp.from(inst);
System.out.println(ts);
2016-03-12 20:45:00.0
Timestamp.toString uses the JVM’s time zone setting for generating the string, so you recognize the time we started out from. So the Timestamp contains the correct point in time. There is no need to convert it in any way. If it gets inserted incorrectly into your database, the problem is with your JDBC driver, your database or somewhere else, and you should prefer to correct it there if you can.
Java 6 and 7
Code very similar to the above will work in Java 7 if you add ThreeTen Backport to your project. This is the backport of the java.time classes to Java 6 and 7, and I include a link at the bottom (it’s ThreeTen for JSR-310, where the modern API was first described).
Instant inst = LocalDateTime.of(2016, Month.MARCH, 12, 20, 45)
.atZone(ZoneId.of("America/Chicago"))
.toInstant();
Timestamp ts = DateTimeUtils.toSqlTimestamp(inst);
You notice that the only difference from Java 8 is the way we convert the Instant to a Timestamp. The result is the same, of course.
I you don’t want a dependency on ThreeTen Backport, there are of course still ways to obtain a Timestamp. I wouldn’t use the deprecated constructor, as you do in your code, even though it works as long as no one tampers with your JVM’s time zone setting. If you know you want a Timestamp equal to 02:45 UTC, one option is
Timestamp ts = Timestamp.valueOf("2016-03-12 20:45:00");
It still depends on your JVM’s time zone setting, though.
What went wrong in your code?
As mentioned a Timestamp hasn’t got a time zone in it, so converting a Timestamp to UTC does not make sense.
What happens in your code:
The deprecated Timestamp constructor uses your JVM’s time zone setting (America/Chicago, I presume) for constructing a Timestamp corresponding 12 March 2016 at 8.45 PM in your time zone (the same point in time as 13 March 2:45 AM UTC).
Your SimpleDateFormat correctly formats this into 2016-03-13 02:45:00 (UTC).
Timestamp.valueOf() too uses America/Chicago time zone. However, on the night between 12 and 13 March summer time (daylight saving time) begins in this time zone. At 2 AM the clock is moved forward to 3. So there is no 2:45 this night. Timestamp picks 3:45 instead.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.timeto Java 6 and 7.
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
I have a String containing a time in the format: 08:00:00
This time is from US Eastern time and I want to convert it to London's timezone and end up with a String of that time.
I have converted the String to time using
Time.valueOf(t);
However after this I cannot get the timezone to change.
you can displace the time using withZoneSameInstant
LocalTime myLocalTime = LocalTime.parse("08:00:00", DateTimeFormatter.ofPattern("HH:mm:ss"));
LocalTime londonTime = LocalDateTime.of(LocalDate.now(), myLocalTime).atZone(ZoneId.of("America/New_York"))
.withZoneSameInstant(ZoneId.of("Europe/London")).toLocalTime();
System.out.println(myLocalTime);
System.out.println(londonTime);
There are lots of details regarding this question.
The Time class sets the date (day, month and year) to January 1st, 1970. But to convert from EST to London local time, you must consider Daylight Saving Time rules.
The difference in hours is not always the same; it can change depending on the date - considering this year (2017): from January 1st to March 11th, the difference will be 5 hours, then from March 12th to March 25th the difference is 4 hours, then it's back to 5 hours, then in October 29th it's 4 hours and in November 5th is 5 hours again, until the end of the year.
That's because of DST starting and ending in both timezones and at different dates. And each year, these dates change as well, so you need to know the date you're working with, to make the correct conversion.
Another thing is that Java 8 new API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or EST) because they are ambiguous and not standard.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
In the example below I'm using America/New_York - one of the many timezones that uses EST (there are more than 30 timezones that uses or had used it). You can call ZoneId.getAvailableZoneIds() to check all the timezones and choose one that suits best for your case.
The code is very similar to #ΦXocę 웃 Пepeúpa ツ answer, well, because it's straightforward and there's not much to change. I just wanted to add the insights above.
// timezones for US and UK
ZoneId us = ZoneId.of("America/New_York");
ZoneId uk = ZoneId.of("Europe/London");
// parse the time string
LocalTime localTimeUS = LocalTime.parse("08:00:00");
// the reference date (now is the current date)
LocalDate now = LocalDate.now(); // or LocalDate.of(2017, 5, 20) or any date you want
// the date and time in US timezone
ZonedDateTime usDateTime = ZonedDateTime.of(now, localTimeUS, us);
// converting to UK timezone
ZonedDateTime ukDateTime = usDateTime.withZoneSameInstant(uk);
// get UK local time
LocalTime localTimeUK = ukDateTime.toLocalTime();
System.out.println(localTimeUK);
The output will be 13:00 (the result of localTimeUK.toString()) because toString() omits the seconds if the value is zero.
If you want to always output the seconds, you can use a DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
String time = fmt.format(localTimeUK);
In this case, the string time will be 13:00:00.
LocalDate.now() returns the current date using your system's default timezone. If you want the current date in a specific zone, you could've called LocalDate.now(us) (or anyzone you want, or even explicit use the default: LocalDate.now(ZoneId.systemDefault()))
I might have little confusion about Date in android/java. What I know is when calling new Date() it creates a Date instance with current UTC date and time, Right ? Because Date in java don't have any time zone thing, So if I call
new Date().getTime() I will get a long value(time stamp) as UTC, not as local time, right ?
And to show date, we use 'DateFormat' and it has time zone info. So when I call DateFormat.getDateTimeInstance().format(new Date()) I will get a string with local time.
But how do I get long value(time stamp) of local time ?? I found this answer but is that the only way ? or something more simple ?
Thank you :)
So if I call new Date().getTime() I will get a long value(time stamp) as UTC, not as local time, right?
Well, it will give you the number of milliseconds since Jan 1st 1970 00:00:00 UTC, yes. It's not "in" UTC particularly; it's just a number of milliseconds since an arbitrary epoch.
But how do I get long value(time stamp) of local time?
You don't, basically. That turns out not to be a particularly useful concept. If you think about it, a timestamp is just an instant in time - it's independent of time zones. You can express the Unix epoch in any time zone; it just happens to normally be expressed in terms of UTC.
If you need the local date/time for a particular timestamp, you just need to remember the timestamp itself and the relevant time zone. If you give us more information about what you're trying to achieve, we may be able to help more.
See Date.getTime javadocs: Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object. There is no local timestamp. There can be only different textual date / time representations of it.
You can try these to get local date.
String s=new SimpleDateFormat("yyyy-MM-dd",Locale.ENGLISH).format(new Date());
or
Calendar c=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
But how do I get long value(time stamp) of local time ??
A local date-time in a timezone is just a representation of the universal instant in that timezone. The new java.util.Date() gives us that universal instant i.e. it simply represents an instant on the timeline — a wrapper around the number of milliseconds since the UNIX epoch (January 1, 1970, 00:00:00 GMT). Since it does not hold any timezone information, its toString function applies the JVM's timezone to return a String in the format, EEE MMM dd HH:mm:ss zzz yyyy, derived from this milliseconds value. In other words, the same milliseconds will be represented as different date-times in different timezones. The vice versa: at any given moment, date-times in different timezones will give us the same number of epoch milliseconds.
java.time
The object corresponding to new java.util.Date() in java.time, the modern Date-Time API is Instant.now().
A demo of java.time, the modern Date-Time API:
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
// Let's assume 2021-06-26T23:35:50 is the local date-time in India
LocalDateTime ldtIndia = LocalDateTime.parse("2021-06-26T23:35:50");
Instant instant = ldtIndia.atZone(ZoneId.of("Asia/Kolkata")).toInstant();
long millis = instant.toEpochMilli();
System.out.println(millis);
}
}
Output:
1624730750000
ONLINE DEMO
You can convert Instant and java.util.Date to each other using
java.util.Date#from(Instant) and java.util.Date#toInstant().
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.