I would like to convert date with its UTC offset to the different timezone.
Suppose that I get 2021-06-14 06:56:00 (this is local date time) with UTC offset +3 hours and I need to convert this local date time to LosAngeles timezone (UTC -8 hours). In order to implement this, I wrote the following snippet:
public static LocalDateTime toPstTimeZone(LocalDateTime localDateTime, int utcOffset) {
final var pstUtcOffset = ZoneOffset.ofHours(-8);
return localDateTime
.atOffset(ZoneOffset.ofHours(utcOffset))
.withOffsetSameInstant(pstUtcOffset)
.toLocalDateTime();
}
need to check if this is valid approach to do.
Your approach is correct. However, I would suggest if possible to work to begin with, with ZonedDateTime or OffsetDateTime. in this case switching between timezones is much easier. For ZonedDateTime switching to different time zone is just one method: public ZonedDateTime withZoneSameInstant(ZoneId zone)
Related
I have LocalDateTime that keeps value in UTC.
I want to convert it to local date and time according to a time zone,
here how I do it:
public LocalDateTime convertUTC2LocalDateTimeZone(LocalDateTime dateTime){
System.out.println("dateTime:" + dateTime);
ZonedDateTime zonedDateTime = dateTime.atZone(ZoneId.of("Canada/Mountain"));
System.out.println("zonedDateTime:" + zonedDateTime);
LocalDateTime ldt = zonedDateTime.toLocalDateTime()
System.out.println("ldt:" + ldt);
return ldt;
}
Output:
dateTime:2018-07-15T10:00:46
zonedDateTime:2018-07-15T10:00:46-06:00[Canada/Mountain]
ldt:2018-07-15T10:00:46
As you can see the ldt value is the same as input, no time conversion occurred.
Any idea why time conversion not occurred?
A LocalDateTime represents a "local time and date". In other words: it's something abstract like "January 1st 2020, 10:00 AM" without any time zone information.
It does not represent anything in UTC. There simply is no time zone information contained in it.
So it doesn't represent a "physical" or exact point in time. To do this you need to convert it to a ZonedDateTime by adding some time zone. The way you do in your code basically says: "Give me a ZonedDateTime object that represents the local time provided by this LocalDateTime in the given time zone".
That means this ZonedDateTime does represent a fixed point in time (i.e. you can calculate the milliseconds since the epoch, basically).
Then you ask "given that ZonedDateTime, what would the local date/time be?", which will just return the value that you initially put in without any modification.
To actually convert from UTC to some other timezone, you need to explicitly create a ZonedDateTime in the UTC timezone first:
create ZonedDateTime representing UTC
calculate ZonedDateTime in the target timezone
get a LocalDateTime from the ZonedDateTime created in #2.
So in code:
ZonedDateTime utcDateTime = dateTime.atZone(ZoneOffset.UTC); // #1
ZonedDateTime mountainDateTime = utcDateTime.withZoneSameInstant(ZoneId.of("Canada/Mountain")); // #2
LocalDateTime localDateTimeAtMountain = mountainDateTime.toLocalDateTime(); // #3
I would want to take the time and time zone from user and create an entry. Used below calendar API to do this, it is working for few time zone and not working few time zones
calendar.setTime(eventFormEntryBean.getStartDate());
TimeZone timeZone = TimeZone.getTimeZone("Europe/Amsterdam");
calendar.setTimeZone(timeZone);
Working timezone(at the end +xx:xx)
Pacific/Palau 2019-11-27T20:51:09.000+09:00
IST - 2019-11-20T22:00:00.000+05:30
Europe/Amsterdam - 2019-11-28T12:49:24.000+01:00
America/Los_Angeles - 2019-11-20T21:32:49.000-08:00
Not working time zone:-
Africa/Dakar - 2019-11-21T05:30:45.000Z
London(Europe/London) - 2019-11-21T12:08:42.000Z
For the above London and Africa/Dakar time zones do not have any indicator to distinguish the time zone, it simply specify “.000Z” at the end. Is there any attribute that we need to set in order to get full time zone?
what does that .000z means?
If you want to be able to write code that reflects the difference between offsets and time zones, leave java.util and switch to java.time (for Java 8+ and with a support library for Java 6 and 7).
Then you can do things like these:
public static void main(String[] args) {
/*
* the base of this example is a date time with an offset of +01:00
* (which is present in several zones, not just in Europe/Amsterdam!)
*/
String datetime = "2019-11-28T12:49:24.000+01:00";
// parse it to an offset-aware object
OffsetDateTime plusOneHourOffsetDateTime = OffsetDateTime.parse(datetime);
// print it to be sure ;-)
System.out.println(plusOneHourOffsetDateTime
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
// convert it to a zone-aware date time object by providing the zone
ZonedDateTime europeAmsterdamZonedDateTime = plusOneHourOffsetDateTime
.atZoneSameInstant(ZoneId.of("Europe/Amsterdam"));
// print it
System.out.println(europeAmsterdamZonedDateTime
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// then take the same instant but use a different time zone
ZonedDateTime utcZonedDateTime = plusOneHourOffsetDateTime
.atZoneSameInstant(ZoneId.of("UTC"));
// print that, it adds a Z (indicating an offset of 00:00) and the time zone
// that was specified
System.out.println(utcZonedDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// take a totally different time zone and do it again
ZonedDateTime pacificPalauZonedDateTime = plusOneHourOffsetDateTime
.atZoneSameInstant(ZoneId.of("Pacific/Palau"));
// print that one, too
System.out.println(pacificPalauZonedDateTime
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
which outputs this
2019-11-28T12:49:24+01:00
2019-11-28T12:49:24+01:00[Europe/Amsterdam]
2019-11-28T11:49:24Z[UTC]
2019-11-28T20:49:24+09:00[Pacific/Palau]
EDIT
The reason for the DateTimeParseException mentioned in your comment is the date-time String, because it doesn't have a zone or an offset, which makes it unparseable by the default DateTimeFormatter used in OffsetDateTime.parse(String datetime).
If you have a String with date and time information but without a zone or an offset, you can parse it to a LocalDateTime first and create a ZonedDateTime from that:
public static void main(String[] args) {
// date time String without zone or offset information
String dateTimeString = "2019-11-30T19:35:06";
// create a LocalDateTime from the String
LocalDateTime ldt = LocalDateTime.parse(dateTimeString);
// then create a ZonedDateTime from the LocalDateTime adding a zone
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()); // system default here
// and print it
System.out.println(zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
You misunderstood. Z means exactly the same as +00:00 and is the conventional and recommended way to write it in the ISO 8601 format that you are producing. So for all of your time zones you are getting the correct UTC offset (except possibly IST; that may stand for Irish summer time or Israel standard time, in which case your offset of +05:30 is wrong; don't rely on ambiguous three letter tome zone abbreviations).
The Calendar and TimeZone classes are both poorly designed and long outdated. I recommend that instead of using those you use java.time, the modern Java date and time API. You need ZoneId and ZonedDateTime. See the answer by deHaar.
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);
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));
}
I'm using MongoDB to store my data. Mongo stores timestamps in UTC as a default.
We process data in different time zones. I'm struggling to convert UTC timestamp to PDT or IST timestamps.
Trying to construct a method to pass timezone(into which my timestamp is to be converted) and timestamp(UTC). Method to return the timestamp of specified time zone.
public Date getDateBasedOnZone(Date date, "America/Los_Angeles") {
return dateInAmerica/Los_Angeles;
}
You could use something like the following to get the time in a particular zone:
date.toInstant().atZone( ZoneId.of( "America/Los_Angeles" ) )
A java.util.Date object does NOT contain timezone information so it's impossible to convert from one timezone to another in a java.util.Date (it doesn't make sense). It's simply a wrapper around long which is milliseconds since EPOCH.
You only start seeing timezone in java.util.Calendar or when a java.util.Date is converted to String.
There's also Joda-Time which has far better date API's than the core Java libraries.
You can use a dateformat with the required timezone and apply it to the date
public Date convertToZone(Date date, String tz) {
DateFormat TFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TFormat.setTimeZone(TimeZone.getTimeZone(tz));
return df.parse(currentTFormat.format(date));
}