Is it able to get same date&time in other time zone?
I mean, currently, I'm getting a zulu datetime from database. This is:
2019-04-02T00:00:00Z
I'd need to keep date&time part in other timezone (ZoneId.systemDefault()). I'd like to get:
2019-04-02T00:00:00+02:00[Europe/Madrid]
Is it able to get it?
Sure, this is possible, and pretty easy to do.
final String dateStr = "2019-04-02T00:00:00Z";
final ZonedDateTime date = ZonedDateTime.parse(dateStr);
final ZonedDateTime zonedDateTime = date.withZoneSameLocal(ZoneId.systemDefault());
Output:
date: 2019-04-02T00:00Z
zonedDateTime: 2019-04-02T00:00+02:00[Europe/Rome]
withZoneSameLocal is doing the magic
Returns a copy of this date-time with a different time-zone, retaining
the local date-time if possible.
Related
I'm receiving a query parameter date, as yyyy-MM-DD (2022-03-08).
I want to conver this to java.util.Calendar / java.util.GregorianCalendar formmat.
So my idea is converto:
String -> ZonedDateTime -> Calendar.
What I did:
ZonedDateTime parsedDate = ZonedDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//date = 2022-03-08
even with the correct format, I'm getting:
Text '2022-03-08' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2022-03-08 of type java.time.format.Parsed
I found out that this error occurs because my string does not have a TimeZone.
One suggestion was to use LocalDate
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date = LocalDate.parse(fecha, formatter);
but I can't use localDate as an argument for ZonedDateTime.parse().
What else could I try?
I want to conver this to java.util.Calendar / java.util.GregorianCalendar formmat.
That seems silly; Calendar/GregorianCalendar is obsolete, and the API is horrendous. Why use a broken screwdriver when there's a shiny new one right there in the toolbox? Don't do this.
So my idea is converto: String -> ZonedDateTime -> Calendar.
That seems silly. The string does not contain a ZonedDateTime. It doesn't even contain a LocalDateTime. It is clearly a LocalDate. So, convert it to a localdate, and you go from there.
The power of the java.time package is that each different concept in time has a matching type in the j.t package that is properly named. For example, java.util.Date is a lie: It is a timestamp, and it has nothing whatsoever to do with dates; asking a Date object for 'what year is it', is broken (try it, you get a warning).
Calendar, similarly, is an utter falsehood. It does not represent a calendar at all; it, too, represents a timestamp.
LocalDate on the other hand is perfect truth: It represents a date (not a time), and it does not include timezone or other localizing information: It makes sense only as 'locally'.
Each stop should just make sense, on its own:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd");
LocalDate date = LocalDate.parse("2022-10-01", formatter);
So far, so good. I'd just stop there - why lie? Why return a Calendar which is both API wise a lie (that class does not represent calendars), and even if someone knows exactly what Calendar is, it's still a lie: A calendar implies it has exact time and a timezone. You do not have a time, and also don't have a timezone. Why return something that suggests stuff that isn't there?
But, if you MUST, then explicitly add a timezone and a time, and THEN go for it:
ZonedDateTime zdt = someLocalDate.atStartOfDay().atZone(ZoneId.of("Europe/Amsterdam"));
GregorianCalendar gc = GregorianCalendar.from(zdt);
This code is clear and legible: It makes crystal clear that the code picks a time, and picks a zone.
But, again, now you ended up with a horrible, horrible object you should not be using, for anything.
There are other ways of getting a ZonedDateTime than just its static parse() method. Here's how to turn a LocalDateTime into a ZonedDateTime:
ZonedDateTime zoned = dateTime.atZone(ZoneId.of( "America/New_York"));
or if you have a LocalDate:
ZonedDateTime zoned =
date.atStartOfDay( ZoneId.of( "America/New_York" ));
I would not use java.util.Calendar or Date. They're junk. I'd either stick with LocalDate or use ZonedDateTime depending on if you need to keep track of time zones or not. This should get you where you want to go either way, I guess, as it sounds like you know what you want to do once you have a ZonedDateTime.
UPDATE: I looked up how to convert a ZoneDateTime to a Calendar:
Calendar calendar = GregorianCalendar.from(zoned);
just in case you hadn't gotten that far and really want to go that way.
On the server code I would like to get the "Day" but not of server date/time but of a specific timezone, GMT+8 specifically.
I have this code:
DateFormat formatter = new SimpleDateFormat("EEEE", Locale.ENGLISH);
String day = formatter.format(new Date()).toUpperCase();
availabilities.add(Availability.builder()
.day(day)
.from(LocalTime.now())
.to(LocalTime.now())
.build());
How do I get the "day" for the specific timezone and also have to build a LocalTime.now() which will return a LocalTime object but not the current time of the said timezone.
For instance as of this writing GMT+8 now is ~6:25 am so that would be the one that LocalTime.now() returns instead of the cloud server which is in the different timezone.
SDF and new Date() are old API and you don't want these. For example, Date is a lie; it does not represent a date whatsoever, it actually represents an instant in time. This is dumb - that's why there is a new API.
EDIT: Made it simpler by invoking the now method of ZonedDateTime.
private static final ZoneId TARGET_ZONE = ZoneId.of("Singapore");
ZonedDateTime atTarget = ZonedDateTime.now(TARGET_ZONE);
DayOfWeek whatYouWant = atTarget.getDayOfWeek();
NB: You can go with +8 explicitly, then you're looking for an OffsetDateTime, and atOffset(ZoneOffset.ofHours(8)), but that's... weird. Who could possibly want 'UTC+8'? Nobody, except airplanes and military operations in a certain zone, and surely that's not your target audience.
An external API returns an object with a date.
According to their API specification, all dates are always reported in GMT.
However, the generated client classes (which I can't edit) doesn't set the timezone correctly. Instead, it uses the local timezone without converting the date to that timezone.
So, long story short, I have an object with a date that I know to be GMT but it says CET. How can I adjust for this mistake withouth changing my local timezone on the computer or doing something like this:
LocalDateTime.ofInstant(someObject.getDate().toInstant().plus(1, ChronoUnit.HOURS),
ZoneId.of("CET"));
Thank you.
tl;dr ⇒ use ZonedDateTime for conversion
public static void main(String[] args) {
// use your date here, this is just "now"
Date date = new Date();
// parse it to an object that is aware of the (currently wrong) time zone
ZonedDateTime wrongZoneZdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
// print it to see the result
System.out.println(wrongZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// extract the information that should stay (only date and time, NOT zone or offset)
LocalDateTime ldt = wrongZoneZdt.toLocalDateTime();
// print it, too
System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// then take the object without zone information and simply add a zone
ZonedDateTime correctZoneZdt = ldt.atZone(ZoneId.of("GMT"));
// print the result
System.out.println(correctZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
Output:
2020-01-24T09:21:37.167+01:00[CET]
2020-01-24T09:21:37.167
2020-01-24T09:21:37.167Z[GMT]
Explanation:
The reason why your approach did not just correct the zone but also adjusted the time accordingly (which is good when desired) is your use of a LocalDateTime created from an Instant. An Instant represents a moment in time which could have different representations in different zones but it stays the same moment. If you create a LocalDateTime from it and put another zone, the date and time are getting converted to the target zone's. This is not just replacing the zone while keeping the date and time as they are.
If you use a LocalDateTime from a ZonedDateTime, you extract the date and time representation ignoring the zone, which enables you to add a different zone afterwards and keep the date and time as it was.
Edit: If the code is running in the same JVM as the faulty code, you can use ZoneId.systemDefault() to get the same time zone as the faulty code is using. And depending on taste you may use ZoneOffset.UTC instead of ZoneId.of("GMT").
I am afraid you will not get around some calculations here. I'd strongly suggest to follow an approach based on java.time classes, but alternatively you might use the java.util.Calendar class and myCalendar.get(Calendar.ZONE_OFFSET) for those calculations:
https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#ZONE_OFFSET
I wanted to get the LocalDateTime in GMT so wrapped it with ZonedDateTime.
But gmtZoneTime is returned in the following format: 2019-10-29T00:00Z[GMT] While I need it to be: 2019-10-29T00:00:00.000+0000
How should I properly convert localDateTime into the GMT ZonedDateTime?
val currentDate:LocalDate = java.time.LocalDate.now
val localDateTime: LocalDateTime = currentDate.atStartOfDay
val gmtZoneTime: ZonedDateTime = localDateTime.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("GMT"))
You need to format the ZonedDateTime.
First approach would be to use predefined formatter like: java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME, however for GMT it shows 'Z' instead of '+0000' (default behaviour, other offsets are displayed like '+0100' etc.)
So the second one would be to create your own formatter like:
java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")
and then use it to format ZonedDateTime like gmtZoneTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"))
so you get a result like:
2019-10-28T23:00:00+0000
First your code is incorrect. When I ran it in my time zone (Europe/Copenhagen) just now, I got
2019-10-29T23:00Z[GMT]
I don’t think you intended 23:00 in GMT.
Second you may think of GMT or UTC as an offset (of zero from UTC), so it is more correct to use an OffsetDateTIme than a ZonedDateTime for the time. This also eliminates your unwanted suffix. In Java (it’s all I can write):
LocalDate currentDate = LocalDate.now(ZoneOffset.UTC);
OffsetDateTime gmtZoneTime = currentDate.atStartOfDay(ZoneOffset.UTC)
.toOffsetDateTime();
System.out.println(gmtZoneTime);
Output when running just now:
2019-10-30T00:00Z
Edit: You can safely regard UTC and GMT as synonymous since java.time does that (even though strictly speaking they may differ by up to a second).
I assumed you also wanted the date in UTC, so passed this as argument to LocalDate.now(). If you want the date in some other time zone, pass that time zone to LocalDate.now() so that it is clear from the code what you get.
If you want that specific format in your question, pezetem is correct in the other answer that you need to format into a string:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxx");
String formattedGmtTime = gmtZoneTime.format(formatter);
System.out.println(formattedGmtTime);
2019-10-30T00:00:00.000+0000
It seems wordy to me, though. I’d at least leave out the milliseconds since we know they are 0, probably the seconds too. Said without knowing your exact business case.
Link: Difference between UTC and GMT
I have time 12:00:00 in format HH:mm:ss.
I know that this time comes from server witch is setup with +3 offset.
If i use SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");, it parses time with regard to device, which can be in a different timezone.
Is there another way to parse it with regard to +3 offset except adding it to the original string?
First, should your server rather send the time in UTC? If clients are everywhere, this would seem more time zone neutral and standardized. However, the way to handle it in code wouldn’t be much different. In any case the server offset form UTC could be constant:
private static final ZoneOffset serverOffset = ZoneOffset.ofHours(3);
In real code you will probably want to make it configurable somehow, though. To parse:
OffsetTime serverTime = LocalTime.parse("12:00:00").atOffset(serverOffset);
System.out.println(serverTime);
This prints
12:00+03:00
Since your time format agrees with LocalTime’s default (ISO 8601), we need no explicit formatter. If a representation of the time with offset is all you need, we’re done. If you need to convert to the user’s local time, to do that reliably you need to decide both a time zone and a date:
LocalTime clientTime = serverTime.atDate(LocalDate.of(2018, Month.JANUARY, 25))
.atZoneSameInstant(ZoneId.of("Indian/Maldives"))
.toLocalTime();
System.out.println(clientTime);
With the chosen day and zone we get
14:00
Please substitute your desired time zone and date.
Just hypothetically, if you knew the user’s offset from UTC, you could use just that:
LocalTime clientTime = serverTime.withOffsetSameInstant(ZoneOffset.of("-08:45"))
.toLocalTime();
The example yields 00:15. However, no one knows when the politicians introduce summer time (DST) or other anomalies in the user’s time zone, so I discourage relying on an offset alone.
And yes, I too am using java.time. SimpleDateFormat is not only long outdated, it is also notoriously troublesome, so java.time is what I warmly recommend.
Set the timezone on your SimpleDateFormat object:
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT+03:00"));
I recommend you use the Java 8 date and time API (package java.time) instead of the old API, of which SimpleDateFormat is a part.
Using the Java 8 DateTime API:
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("HH:mm:ss");
LocalTime clientLocalTime = LocalTime
.parse("12:00:00", formatter)
// Create an OffsetTime object set to the server's +3 offset zone
.atOffset(ZoneOffset.ofHours(3))
// Convert the time from the server timezone to the client's local timezone.
// This expects the time value to be from the same day,
// otherwise the local timezone offset may be incorrect.
.withOffsetSameInstant(ZoneId.systemDefault().getRules().getOffset(Instant.now()))
// Drop the timezone info - not necessary
.toLocalTime();