Handling Daylight Saving Time with OffsetDateTime in Java when time needs to remain the same in local time regardless of DST - java

I'm using Spring Boot (Java 8) to develop the backend for a restaurant app which exposes some REST APIs. Everything related to dates and times is handled with OffsetDateTime objects and through the app I use, by default, the offset of "Europe/Rome". Those objects are also persisted in the DB. Everything is working fine, except... daylight saving time kicked in and now all the business hours of the restaurant are off by one hour.
This obviously happens because pre-DST the offset of "Europe/Rome" is +01:00, while post-DST is +02:00. So, to give you an example, the opening hour of the restaurant (saved inside the DB pre-DST) is 08:30:00+01:00, but it becomes 09:30:00+02:00 when accessed post-DST from the API (because it's automatically converted with the default zone of the system). I need the business hours of the restaurant to remain the same in local time, regardless of DST.
How can I handle this situation and offer consistent data to my users?

Offset versus time zone
"Europe/Rome" is the name of a time zone, not an offset.
An offset-from-UTC is simply a number of hours-minutes-seconds ahead of, or behind, the baseline of UTC. As you move eastward, local times are more and more hours ahead of UTC, while moving westward to the Americas the local times are more and more hours behind UTC.
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
You said:
Everything related to dates and times is handled with OffsetDateTime objects
No, you should not use that single class in all situations. OffsetDateTime class is for representing a moment where you know the offset but not the time zone. ZonedDateTime should be used when you know the time zone. And for moments specifically in UTC (an offset of zero), generally best to use Instant.
The OffsetDateTime is rarely the right class to choose. Time zone is always preferable to a mere offset. So usually you will want either ZonedDateTime or Instant. Generally programmers and sysadmins should be thinking, working, logging, and debugging in UTC, so for that use Instant. For presentation to users in their expected time zone, or where business rules require, use ZonedDateTime. The one big exception is JDBC work for database access — the JDBC 4.2 specification inexplicably requires compliant drivers to support OffsetDateTime but not Instant nor ZonedDateTime. Fortunately conversion between the classes is quite simple and easy.
Moment
You need to understand the difference between a moment and not-a-moment. A moment is a specific point on the timeline. Forget about time zones and offsets, if someone in Casablanca Morocco calls someone in Tokyo Japan, they are sharing the same moment. To track a moment, use one of the three classes shown above in blue blocks: Instant, OffsetDateTime, ZonedDateTime.
Instant phoneCallStartedUtc = Instant.now() ; // A moment as seen in UTC.
ZonedDateTime phoneCallStartedCasablanca = phoneCallStartedUtc.atZone( ZoneId.of( "Africa/Casablanca" ) ) ;
ZonedDateTime phoneCallStartedTokyo = phoneCallStartedUtc.atZone( ZoneId.of( "Asia/Tokyo" ) ) ;
See this code run live at IdeOne.com.
phoneCallStartedUtc.toString(): 2021-03-28T20:33:58.695669Z
phoneCallStartedCasablanca.toString(): 2021-03-28T21:33:58.695669+01:00[Africa/Casablanca]
phoneCallStartedTokyo.toString(): 2021-03-29T05:33:58.695669+09:00[Asia/Tokyo]
All three of those objects, phoneCallStartedUtc, phoneCallStartedCasablanca, and phoneCallStartedTokyo, represent the very same simultaneous moment. Their only difference is being viewed through different wall-clock time (what people see when they glance up at a clock on their wall).
Not a moment
Future appointments, such as when a restaurant will open, or when a patient will next see their dentist, cannot be tracked as a moment.
We may know the date and time-of-day when we intend for the restaurant to open, but we cannot know the moment. That is because the clock time used in a time zone is defined by politicians.
Politicians around the world have shown a penchant for changing the clock definition rules in the time zone(s) of their jurisdiction. They move the clock around as a political statement to distinguish themselves from neighboring jurisdictions, or the opposite, to align with neighbors. Politicians change their zone rules to manipulate financial markets. They move the clock during times of war and occupation for symbolic as well as logistical reasons. Politicians move the clock when they join in fads, such as Daylight Saving Time (DST). Later, they move their clocks again when they join a different fad, such as staying on DST year-round.
The lesson to learn here is that time zone rules change, they change with surprising frequency, and they change with little or even no forewarning. So, for example, 3 PM on next Tuesday may not actually occur at the moment you expect right now on Wednesday. Next 3 PM might be an hour earlier than expect now, a half-hour later than you expect now, no one knows.
To track future appointments that are tracked by the clock such as your restaurant opening, use LocalTime for the time of day, and LocalDate for the date. Where appropriate, you may combine those two into LocalDateTime object. Separately track the the intended time zone. Then at runtime when you need to calculate the scheduling of events, apply the time zone to the LocalDateTime to get a ZonedDateTime. The ZonedDateTime will determine a moment, but you cannot store that moment as politicians may move the clock on you, ripping the rug from beneath you.
// Store these three values.
ZoneId zoneIdRome = ZoneId.of( "Europe/Rome" ) ;
LocalTime restaurantOpening = LocalTime.of( 9 , 30 ) ; // 09:30 AM.
LocalDate nextTuesday = LocalDate.now( zoneIdRome ).with( TemporalAdjusters.next( DayOfWeek.TUESDAY ) ) ;
// Do NOT store this next value. Use this next value only on-the-fly at runtime.
ZonedDateTime momentWhenRestaurantIsExpectedToOpenNextTuesday =
ZonedDateTime.of( nextTuesday , restaurantOpening , zoneIdRome ) ;
See that code run live at IdeOne.com.
restaurantOpening.toString(): 09:30
nextTuesday.toString(): 2021-03-30
momentWhenRestaurantIsExpectedToOpenNextTuesday.toString(): 2021-03-30T09:30+02:00[Europe/Rome]
If you want to track when the restaurant actually did open, then you would be tracking moments. Then you would be using one of three classes discussed above, likely Instant.
Instant momentWhenRestaurantActuallyOpened = Instant.now() ; // 09:41 AM, eleven minutes late because the manager could not find the door keys that had dropped to the floor.
By the way, some future appointments are not driven by the clock. For example, rocket launches are scheduled according to nature, the expected weather conditions. Politicians may alter the clock, change the time zone rules, but that does not change the moment when the rocket will launch. Such a change in time zone by the politicians will change how the media report on the planned launch, but the launch itself will not happen earlier or later. So for rocket launches and such we would use one of the three moment-oriented classes discussed above, almost certainly Instant class to focus on UTC only and avoid all time zones entirely.
These concepts and classes have all been addressed many many times already on Stack Overflow. Search to learn more.

Related

Should I use Instant or DateTime or LocalDateTime in Java entities?

In my Java (with Spring Boot and Spring Data JPA) applications, I generally use Instant. On the other hand, I would like to use the most proper data type for time values.
Could you please clarify me about these issues? What data type should I prefer for keeping date and time when:
1. To keep time precisely as timestamp (I am not sure if Instant is the best option)?
2. For normal cases when I just need date and time (as far as I know, the old library was obsolete, but not sure which library should I use).
I also consider the TimeZone, but not sure if using LocalDateTime with UTC solves my problem.
Any help would be appreciated.
Let's assume we need to cover the full span of date and time concerns. If there is a certain concern you don't have, that either collapses various types into 'well then they are interchangible' or simply means you don't need to use a certain part of the API. The point is, you need to understand what these types represent, and once you know that, you know which one to apply. Because even if various different java.time types all technically do what you want, code is more flexible and a lot simpler to read if the types you use represent the things you want them to. For the same reason String[] student = new String[] {"Joe McPringle", "56"}; is perhaps mechanically a way to represent a student's name and age, but things are just a lot simpler if you write a class Student { String name; int age; } and use that instead.
Local alarm clock
Imagine you want to wake up at 07:00 in the morning. Not because you have an appointment, you just like to be a fairly early riser.
So you set your alarm for 07:00 in the morning, go to sleep, and your alarm promptly goes off at 7. So far, so good. However, you then hop in a plane and fly from Amsterdam to New York. (it is 6 hours earlier in new york). You then go to sleep again. Should the alarm go off at 01:00 at night, or at 07:00 in the morning?
Both answers are correct. The question is, how do you 'store' that alarm, and to answer that question, you need to figure out what the alarm is attempting to represent.
If the intent is '07:00, whereever I might be at the time the alarm is supposed to go off', the correct data storage mechanism is java.time.LocalDateTime, which stores time in human terms (years, months, days, hours, minutes, and seconds) and not in computery terms (we'll get there later), and does not include a time zone at all. If the alarm is supposed to go off every day, then you don't want that either, as LDT stores date and time, hence the name, you'd use LocalTime instead.
That's because you wanted to store the concept of 'the alarm should go off at 7 o'clock' and nothing more than that. You had no intention of saying: "The alarm should go off when people in Amsterdam would agree it is currently 07:00", nor did you have the intent of saying: "When the universe arrives at this exact moment in time, sound the alarm". You had the intent of saying: "When it is 07:00 where-ever you are now, sound the alarm", so store that, which is a LocalTime.
The same principle applies to LocalDate: It stores a year/month/day tuple with no notion of where.
This does draw some perhaps wonky conclusions: Given a LocalDateTime object, it is not possible to ask how long it'll take until that LDT arrives. It is also not possible for any given moment in time to be compared to an LDT, because these things are apples and oranges. The notion 'Feb 18th, 2023, 7 in the morning on the dot' isn't a singular time. After all, in New York that 'moment' occurs a full 6 hours earlier than it would in Amsterdam. You can only compare 2 LocalDateTimes.
Instead, you would have to first 'place' your LDT somewhere, by converting it to one of the other types (ZonedDateTime or even Instant) by asking the java.time API: Okay, I want this particular LDT in a certain time zone.
Hence, if you are writing your alarm app, you would have to take the stored alarm (a LocalTime object), convert it to an Instant (which is what the nature of 'what time is it now, i.e. System.currentTimeMillis()' works on), by saying: That LocalTime, on the current day in the current local timezone, as an instant, and THEN comparing those two results.
Human appointments
Imagine that, just before jetting off to New York, you made an appointment at your local (in Amsterdam) barber. Their agenda was kinda busy so the appointment was set for June 20th, 2025, at 11:00.
If you stay in New York for a few years, the correct time for your calendar to remind you that you have an appointment with your barber's in an hour is certainly not at 10:00 on june 20th 2025 in New York. You'd have missed the appointment by then. Instead, your phone should chirp at you that you have an hour left to get to your barber's (a bit tricky, from New York, sure) at 04:00 in the middle of the night.
It sure sounds like we can say that the barber's appointment is a specific instant in time. However, this is not correct. The EU has already adopted legislation, agreed upon by all member states, that all EU countries shall abolish daylight savings time. However, this law does not provide a deadline, and crucially, does not provide a time zone that each EU member state needs to pick. The Netherlands is therefore going to change time zones at some point. They will likely choose to stick either to permanent summer time (in which case they'd be at UTC+2 permanently, vs. their current situation where they are at UTC+2 in summer and UTC+1 in winter, with, notably, different dates when the switch happens vs. New York!), or stay on winter time, i.e. UTC+1 forever.
Let's say they choose to stick to winter time forever.
The day the gavel slams down in the dutch parliament building enshrining into law that the dutch will no longer advance the clocks in march is the day your appointment shifts by one hour. After all, your barber is not going to go into their appointment book and shift all appointments by an hour. No, your appointment will remain on June 20th, 2025, at 11:00. If you have a running clock ticking down the seconds until your barber appointment, when that gavel comes down it should jump by 3600 seconds.
This belies the point: That barber appointment truly is not a singular moment in time. It's a human/political agreement that your appointment is when Amsterdam universally agrees it is currently June 20th, 2025, 11:00 – and who knows when that moment will actually occur; it depends on political choices.
So, you cannot 'solve' this by storing an instant in time, and it shows how the concept 'instant in time' and 'year/month/day hour:minute:second in a certain timezone' are not quite interchangible.
The correct data type for this concept is a ZonedDateTime. This represents a date time in human terms: year/month/day hour:second:minute, and the timezone. It doesn't shortcut by storing a moment in time in epochmillis or some such. If the gavel comes down and your JDK updates its timezone definitions, asking "how many seconds until my appointment" will correctly shift by 3600 seconds, which is what you want.
Because this is for appointments and it doesn't make sense to store just the time of an appointment but not the date, there is no such thing as a ZonedDate or a ZonedTime. Unlike the first thing which comes in 3 flavours (LocalDateTime, LocalDate, and LocalTime), there's only ZonedDateTime.
The universe/log time
Imagine you are writing a computer system that logs that an event occurred.
That event, naturally, has a timestamp associated with it. Turns out that due to severe political upheaval, the laws of the land decide that retrospectively the country has been in a different timezone than what you thought when the event occurred. Applying the same logic as the barber's case (where the actual moment in time jumps by 3600 seconds when the gavel comes down) is incorrect. The timestamp represents a moment in time when a thing happened, not an appointment in a ledger. It should not jump by 3600.
Timezone really has no purpose here. The point of storing 'timestamp' for a log event is so you know when it happened, it doesn't matter where it happened (or if it does, that is fundamentally a separate notion).
The correct data type for this is java.time.Instant. An instant doesn't even know about time zones at all, and isn't a human concept. This is 'computery time' - stored as millis since an agreed upon epoch (midnight, UTC, 1970, new years), no timezone information is necessary or sane here. Naturally there is no time-only or date-only variant, this thing doesn't even really know what 'date' is - some fancypants human concept that computery time is not concerned with in the slightest.
Conversions
You can trivially go from a ZonedDateTime to an Instant. There's a no-args method that does it. But note:
Create a ZonedDateTime.
Store it someplace.
Convert it to an Instant, store that too.
Update your JDK and get new time zone info
Load the ZDT.
Convert it to an Instant a second time.
Compare the 2 ZDTs and the 2 instants.
Results in different results: The 2 instants would not be the same, but the ZDTs are the same. The ZDT represents the appointment line in the barber's book (which never changed - 2025 june 20th, 11:00), the instant represents the moment in time that you are supposed to show up which did change.
If you store your barber's appointment as a java.time.Instant object, you will be an hour late to your barber's appointment. That's why it's important to store things as what they are. A barber's appointment is a ZonedDateTime. storing it as anything else would be wrong.
Conversions are rarely truly simple. There is no one way to convert one thing to another - you need to think of what these things represent, what the conversion implies, and then follow suit.
Example: You are writing a logging system. The backend parts store log events into a database of some sort, and the frontend parts read this database and show the log events to an admin user for review. Because the admin user is a human being, they want to see it in terms they understand, say, the time and date according to UTC (it's a programmer, they tend to like that sort of thing).
The logging system's storage should be storing the Instant concept: Epoch millis, and without timezone because that is irrelevant.
The frontend should read these as Instant (it is always a bad idea to do silent conversions!) - then consider how to render this to the user, figure out that the user wants these as local-to-UTC, and thus you would then on the fly, for each event to be printed to screen, convert the Instant to a ZonedDateTime in the zone the user wants, and from there to a LocalDateTime which you then render (because the user probably does not want to see UTC on every line, their screen estate is limited).
It would be incorrect to store the timestamps as UTC ZonedDateTimes, and even more wrong to store them as LocalDateTimes derived by asking for the current LocalDT in UTC as the event happens and then storing that. Mechanically all these things would work but the data types are all wrong. And that will complicate matters. Imagine the user actually wants to see the log event in Europe/Amsterdam time.
A note about timezones
The world is more complicated than a handful of timezones. For example, almost all of mainland europe is currently 'CET' (Central European Time), but some think that refers to european winter time (UTC+1), some thing that refers to the current state in central europe: UTC+1 in winter, UTC+2 in summer. (There's also CEST, Central European Summer Time, which means UTC+2 and isn't ambiguous). When EU countries start applying the new law to get rid of daylight savings, its likely e.g. The Netherlands on the west edge of the CET zone picks a different time than Poland on the eastern edge. Hence, 'all of central europe' is far too broad. 3-letter acronyms also are by no means unique. Various countries use 'EST' to mean 'eastern standard time', it's not just the eastern USA for example.
Hence, the only proper way to represent timezone names is using strings like Europe/Amsterdam or Asia/Singapore. If you need to render these as 09:00 PST for residents of the west coast of the USA, that's a rendering issue, so, write a rendering method that turns America/Los_Angeles into PST, which is an issue of localization, and has nothing to do with time.
The Answer by rzwitserloot is correct and wise. In addition, here is a summary of the various types. For more info, see my Answer on a similar Question.
To keep time precisely as timestamp (I am not sure if Instant is the best option)?
If you want to track a moment, a specific point on the timeline:
InstantA moment as seen with an offset-from-UTC of zero hours-minutes-seconds. This class is the basic building-block of the java.time framework.
OffsetDateTimeA moment as seen with a particular offset, some number of hours-minutes-seconds ahead of, or behind, the temporal meridian of UTC.
ZonedDateTimeA moment as seen with a particular time zone. A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region, as decided by their politicians.
If you want to track just the date and time-of-day, without the context of an offset or time zone, use LocalDateTime. This class does not represent a moment, is not a point on the timeline.
For normal cases when I just need date and time
If you are absolutely sure that you want only a date with time-of-day, but do not need the context of an offset or time zone, use LocalDateTime.
using LocalDateTime with UTC
That is a contradiction, and makes no sense. A LocalDateTime class has no concept of UTC, nor any concept of offset-from-UTC or time zone.
Spring Data JPA
The JDBC 4.2+ specification maps SQL standard data types to Java classes.
TIMESTAMP WITH TIME ZONE columns map to OffsetDateTime in Java.
TIMESTAMP WITHOUT TIME ZONE columns map to LocalDateTime in Java.
DATE columns map to LocalDate.
TIME WITHOUT TIME ZONE columns map to LocalTime.
The SQL standard also mentions TIME WITH TIME ZONE, but this type is meaningless (just think about it!). The SQL committee has never explained what they had in mind, as far as I know. If you must use this type, Java defines the ZoneOffset class to match.
Note that JDBC does not map any SQL types to Instant nor ZonedDateTime. You can easily convert to/from the mapped type OffsetDateTime.
Instant instant = myOffsetDateTime.toInstant() ;
OffsetDateTime myOffsetDateTime = instant.atOffset( ZoneOffset.UTC ) ;
… and:
ZonedDateTime zdt = myOffsetDateTime.atZoneSameInstant( myZoneId ) ;
OffsetDateTime odt = zdt.toOffsetDateTime() ; // The offset in use at that moment in that zone.
OffsetDateTime odt = zdt.toInstant().atOffset( ZoneOffset.UTC ) ; // Offset of zero hours-minutes-seconds from UTC.
I also consider the TimeZone
The TimeZone class is part of the terrible legacy date-time classes that were years ago supplanted by the modern java.time classes. Replaced by ZoneId and ZoneOffset.
You should take a look at the Java Date and Time API introduced with Java 8. Each class like Instant, LocalDateTime, ZonedDateTime etc. has a documentation as JavaDoc. If you have problems understanding the documentation, please provide a more specific question.

Find User time zone in java from locale string [duplicate]

I have this weird problem, when I create a calendar with a locale, the TimeZone just reverts to the local one
public void start(Locale locale){
String s = locale.getDisplayName();
System.err.println(s);
Calendar c = new GregorianCalendar(locale);
System.err.println(c.getTimeZone());
}
And this is the output:
español (Argentina)
sun.util.calendar.ZoneInfo[id="Europe/Bucharest", //etc more useless date here....
How can i get the proper time from a specific locale ?
The short answer: you can't.
The long answer: There is no such thing as "proper time zone for a locale". That's just because there are a few countries that have more than one time zone (for example United States). Time zone is a different concept.
Anyway, you are looking to solve your problem. I am guessing that you are writing a web application and you see that the time zone is reverting to the server default. That's a typical situation. Both Locale.getDefault() and TimeZone.getDefault() will return server-related information. The JVM has no way of knowing the "proper" time zone. So what can you do about it?
You can add time zone information to user profile (if you have one), or create a time zone combo box (so that the user can switch at runtime). Then you can assign an appropriate object to DateFormat instance and it will convert time zone automatically.
You can read the current time zone offset from the client via JavaScript Date Object's getTimezoneOffset() function and somehow (AJAX) send it to the server. The problem with this method is that there are several time zones with that offset, and the selected time zone might be inappropriate for other dates. Of course you can guess the time zone by polling the data around time change date, but this is probably not what you want to do.
You can send the unformatted time to the client (for example written as ISO 8601 date-time format or as a Unix time of the epoch in relation to UTC) and have Globalize or Dojo format date and time for you.
Out of these three possible choices, I always opt for number 1. By putting time zone information to user profile, you know for sure what his/her preferred time zone is, regardless of their current web browser, etc. Please keep in mind that some users might want to use your application while visiting other countries...
Locale and time zone are orthogonal issues
Locale
A Locale represents the pair of:
A human language, such as French, English, or Chinese.
A set of cultural norms, for deciding issues such as formatting questions like capitalization, abbreviation, order of elements such as day-month-year in a date, and using COMMA versus FULL STOP as a decimal separator.
Locale is not geographical. While a Locale may use a country or region as a way of representing the cultural norms, that does not literally mean the user is in a particular geographic area. For example, an engineer from Québec at a conference in Tokyo Japan will be using a locale of Locale.CANADA_FRENCH but her time zone may be Asia/Tokyo for the duration of her trip.
ZoneId
A time zone, ZoneId, represents a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region. An offset is merely a number of hours-minutes-seconds ahead or behind the prime meridian used by UTC. Politicians around the world have shown a penchant for frequently redefining their time zones and changing the offset to be used by their jurisdiction(s). Indeed, those regions that adopted the silliness of Daylight Saving Time (DST) change their offset twice a year, about ever six months.
➥ So, no, you cannot get a current time nor a time zone from just a given locale.
when I create a calendar with a locale, the TimeZone just reverts to the local one
You are using terrible date-time classes that are now legacy, supplanted by the modern java.time classes defined by JSR 310.
To get the current moment in UTC (an offset of zero hours-minutes-seconds), use Instant. An Instant is always in UTC by definition.
Instant instant = Instant.now() ; // Capture the current moment in UTC.
If you want the current moment in a particular time zone, specify the ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ; // Capture the current moment as seen in the wall-clock time used by the people of a particular region.
As for knowing the time zone to apply, as I said above you must either:
Use the default (ZoneId.systemDefault), or…
Confirm with the user.
You can get a list of known time zones by calling ZoneId.getAvailableZoneIds.
Calendar c = new GregorianCalendar( locale ) ;
You may confused by this constructor of GregorianCalendar taking a Locale, and understandably so.
This constructor is one of the many poor and incorrect design decisions found in the legacy date-time classes. To be blunt, these legacy classes are quite terrible, designed by people who did not understand the complexities and subtleties of date-time handling. Sun, Oracle, and the JCP community decided to supplant them with java.time for good reason.
Locale intersects with time zone when it comes to generating text representing the value of a date-time object for display to the user. A Locale specifies the human language to use in translating name of month, name of day of week, and so on. And Locale specifies the cultural norms for deciding issues such as abbreviating the month or day-of-week (Is the name capitalized? Is a FULL STOP appended?), ordering day, month, and year, and other such aspects.

Jodatime time difference between 2 timezones

What api do I have to use to get the time difference between 2 timezones without inputing any datetime.
What I am doing right now, is make a temporary date (midnight) from one of the timezone then convert to utz and then convert again to the other timezone and then compute the duration of the two. It is working but is there a simpler api to get the time difference by just giving the name of the zones?
Too long for comment, so I put it here.
server is on diff. timezone, data/informations come from clients from different timezones and save in the db in utc. Problem arise when the client request a transaction history in a certain date. It seems it is not a simple conversion of the requested date (client timezone) to utc, I need to add the time difference of the server timezone and the client timezone to the converted utc time to get the correct date. (so i do it like I said above). Now I found out that the time difference should be added or subtracted, depending on whos timezone is ahead. Well anyway thanks for everybody's inputs. Need the project to run asap so they decided to use just one timezone for the meantime. Implemented or not I will seek a solution to this for future projects. :)
I'm not sure if I understood your question correctly. Are you trying to get offsets of different timezone without using any additional API?
Below code will do that using plain Java:
String[] ids = TimeZone.getAvailableIDs();
HashMap<String, Integer> map = new HashMap<String, Integer>();
for (String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
map.put(id, tz.getOffset(new Date().getTime()) / 1000 / 60); //in minutes
}
System.out.println(map.toString());
private final static int tzDiff = TimeZone.getDefault().getRawOffset() - TimeZone.getTimeZone("America/New_York").getRawOffset();
Joda-Time in maintenance mode
Since you asked your Question, the creator of Joda-Time went on to lead JSR 310 to bring a new set of date-time classes to Java. Those classes arrived in Java 8.
Since then, the Joda-Time project was put into maintenance mode, with the project recommending migration to java.time.
Comparing time zones requires a moment
get the time difference between 2 timezones without inputing any datetime.
That makes no sense. You must specify a moment, a point on the time line, to compare offsets in effect in two different time zones.
Understand that an offset is merely a number of hours-minutes-seconds ahead or behind the prime meridian of time-keeping, UTC.
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset in use by the people of a particular region as decided by their politicians.
By definition, the offset in use varies over time for any particular time zone. So it makes no sense to attempt a comparison of time zones without a specific point in time.
What I am doing right now, is make a temporary date (midnight) from one of the timezone then convert to utz and then convert again to the other timezone and then compute the duration of the two.
I am not sure of your goal, but here is code for doing something similar.
Specify your pair of time zones in which you are interested.
ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;
ZoneId zEdmonton = ZoneId.of( "America/Edmonton" ) ;
Specify a date.
LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;
Determine when the day of that date begins in Tokyo time zone. Do not assume the day starts at 00:00. Some dates in some zones may start at a different time. Let java.time determine the first moment of the day.
ZonedDateTime firstMomentOfDayTokyo = ld.atStartOfDay( zTokyo ) ;
Adjust to see that some moment as it looks in the wall-clock time of Edmonton Alberta Canada.
ZonedDateTime zdtEdmonton = firstMomentOfDayTokyo.atZoneSameInstant( zEdmonton ) ;
See that same moment again with an offset of zero by extracting an Instant object.
Instant instant = firstMomentOfDayTokyo.toInstant() ;
Here is the crucial point to understand: firstMomentOfDayTokyo, zdtEdmonton, and instant all represent the very same moment, the simultaneous same point on the time line. Imagine three people in a conference call originating from Japan, Canada, and Iceland (where their time zone is always zero offset). If they all simultaneously looked up at the clock hanging on their respective wall, they would all see different local time-of-day yet be experiencing the same simultaneous moment.
Now that we have an Instant object in hand, we can proceed to solving your challenge: Getting the offset in use for each of those two time zones.
Fetch the time zone rules for each.
ZoneRules rulesTokyo = zTokyo.getRules() ;
ZoneRules rulesEdmonton = zEdmonton.getRules() ;
Get the offset in effect in each zone at that moment. Notice the use of ZoneOffset class here rather than ZoneId.
ZoneOffset offsetTokyo = rulesTokyo.getOffset( instant ) ;
ZoneOffset offsetEdmonton = rulesEdmonton.getOffset( instant ) ;
Calculate the difference between each zone.
Duration d = Duration.ofSeconds( offsetTokyo.getTotalSeconds() - offsetEdmonton.getTotalSeconds() ) ;
But this offset comparisons between time zones is not the best solution to your underlying problem. Read on for a better solution.
Querying database for a day
You said:
server is on diff. timezone
You should always do your Java programming is such a way as to not be affected by the current default time zone of your servers. Always specify the otherwise optional time zone (or offset) arguments to the various date-time related Java methods.
save in the db in utc
Good. It is generally best to store moments after adjusting to UTC. Some relational databases such as Postgres make such an adjustment when receiving inputs to a column of a type akin to the SQL standard type TIMESTAMP WITH TIME ZONE.
Retrieve such a value in JDBC 4.2 and later using java.time classes.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Write such a value to the database.
myPreparedStatement.setObject( … , odt ) ;
You said:
problem arise when the client request a transaction history in a certain date. It seems it is not a simple conversion of the requested date (client timezone) to utc, I need to add the time difference of the server timezone and the client timezone to the converted utc time to get the correct date.
No, not the best approach. You are thinking in terms of local time zones.
When you go to work as a programmer, DBA, or system-admin, you should forget about your local time zone. Learn to think in UTC, with an offset of zero. Logging, data storage, data exchange, and most of your business logic should all be done in UTC. Keep a second clock on your desk set to UTC; I’m serious, your life will be easier.
As an example, let's say your user wants to query for all records with a timestamp the occurred at some point during one full day as seen in Tokyo Japan.
To query for a day's worth of records, do much the same as we saw above. There we got the first moment of the day in Tokyo.
ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;
LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;
ZonedDateTime firstMomentOfDayTokyo = ld.atStartOfDay( zTokyo ) ;
That is the beginning of the span of time (a day) over which we want to query.
Generally best to define a span of time using Half-Open approach. In this approach the beginning is inclusive while the ending is exclusive. So a day starts at first moment of a date and runs up to, but does not include, the first moment of the following date.
ZonedDateTime firstMomentOfFollowingDayTokyo = ld.plusDays( 1 ).atStartOfDay( zTokyo ) ;
The OffsetDateTime is the class that maps to the TIMESTAMP WITH TIME ZONE type defined in standard SQL. So extract a OffsetDateTime from each.
OffsetDateTime start = firstMomentOfDayTokyo.toOffsetDateTime() ;
OffsetDateTime end = firstMomentOfFollowingDayTokyo.toOffsetDateTime() ;
Write your SQL like this:
SELECT *
FROM event_
WHERE when_ >= ?
AND when_ < ?
;
Do not use the SQL command BETWEEN for this work. That command is Fully-Closed rather than Half-Open.
In JDBC:
myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , end ) ;
Keep in mind that a full day in a particular time zone is not necessarily 24-hours long. A day may be 23, 23.5, 25, or some other number of hours long because of anomalies in political time such as Daylight Saving Time (DST).

How to get the current Time and TimeZone from Locale?

I have this weird problem, when I create a calendar with a locale, the TimeZone just reverts to the local one
public void start(Locale locale){
String s = locale.getDisplayName();
System.err.println(s);
Calendar c = new GregorianCalendar(locale);
System.err.println(c.getTimeZone());
}
And this is the output:
español (Argentina)
sun.util.calendar.ZoneInfo[id="Europe/Bucharest", //etc more useless date here....
How can i get the proper time from a specific locale ?
The short answer: you can't.
The long answer: There is no such thing as "proper time zone for a locale". That's just because there are a few countries that have more than one time zone (for example United States). Time zone is a different concept.
Anyway, you are looking to solve your problem. I am guessing that you are writing a web application and you see that the time zone is reverting to the server default. That's a typical situation. Both Locale.getDefault() and TimeZone.getDefault() will return server-related information. The JVM has no way of knowing the "proper" time zone. So what can you do about it?
You can add time zone information to user profile (if you have one), or create a time zone combo box (so that the user can switch at runtime). Then you can assign an appropriate object to DateFormat instance and it will convert time zone automatically.
You can read the current time zone offset from the client via JavaScript Date Object's getTimezoneOffset() function and somehow (AJAX) send it to the server. The problem with this method is that there are several time zones with that offset, and the selected time zone might be inappropriate for other dates. Of course you can guess the time zone by polling the data around time change date, but this is probably not what you want to do.
You can send the unformatted time to the client (for example written as ISO 8601 date-time format or as a Unix time of the epoch in relation to UTC) and have Globalize or Dojo format date and time for you.
Out of these three possible choices, I always opt for number 1. By putting time zone information to user profile, you know for sure what his/her preferred time zone is, regardless of their current web browser, etc. Please keep in mind that some users might want to use your application while visiting other countries...
Locale and time zone are orthogonal issues
Locale
A Locale represents the pair of:
A human language, such as French, English, or Chinese.
A set of cultural norms, for deciding issues such as formatting questions like capitalization, abbreviation, order of elements such as day-month-year in a date, and using COMMA versus FULL STOP as a decimal separator.
Locale is not geographical. While a Locale may use a country or region as a way of representing the cultural norms, that does not literally mean the user is in a particular geographic area. For example, an engineer from Québec at a conference in Tokyo Japan will be using a locale of Locale.CANADA_FRENCH but her time zone may be Asia/Tokyo for the duration of her trip.
ZoneId
A time zone, ZoneId, represents a history of the past, present, and future changes to the offset-from-UTC used by the people of a particular region. An offset is merely a number of hours-minutes-seconds ahead or behind the prime meridian used by UTC. Politicians around the world have shown a penchant for frequently redefining their time zones and changing the offset to be used by their jurisdiction(s). Indeed, those regions that adopted the silliness of Daylight Saving Time (DST) change their offset twice a year, about ever six months.
➥ So, no, you cannot get a current time nor a time zone from just a given locale.
when I create a calendar with a locale, the TimeZone just reverts to the local one
You are using terrible date-time classes that are now legacy, supplanted by the modern java.time classes defined by JSR 310.
To get the current moment in UTC (an offset of zero hours-minutes-seconds), use Instant. An Instant is always in UTC by definition.
Instant instant = Instant.now() ; // Capture the current moment in UTC.
If you want the current moment in a particular time zone, specify the ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ; // Capture the current moment as seen in the wall-clock time used by the people of a particular region.
As for knowing the time zone to apply, as I said above you must either:
Use the default (ZoneId.systemDefault), or…
Confirm with the user.
You can get a list of known time zones by calling ZoneId.getAvailableZoneIds.
Calendar c = new GregorianCalendar( locale ) ;
You may confused by this constructor of GregorianCalendar taking a Locale, and understandably so.
This constructor is one of the many poor and incorrect design decisions found in the legacy date-time classes. To be blunt, these legacy classes are quite terrible, designed by people who did not understand the complexities and subtleties of date-time handling. Sun, Oracle, and the JCP community decided to supplant them with java.time for good reason.
Locale intersects with time zone when it comes to generating text representing the value of a date-time object for display to the user. A Locale specifies the human language to use in translating name of month, name of day of week, and so on. And Locale specifies the cultural norms for deciding issues such as abbreviating the month or day-of-week (Is the name capitalized? Is a FULL STOP appended?), ordering day, month, and year, and other such aspects.

JAVA TimeZone issue EDT Vs EST

I a newbie to java and hence haven't been able figure this out since quite some time.
I am using Windows XP and the machine is set to TimeZone: Eastern Time (US & Canada).
I have a Java application, which takes the current system time and timezone info and writes a string like: 20101012 15:56:00 EST, to a file.
The last piece of Date above, i.e.: the timezone, changes from EST to EDT as i change my system date.
Being precise: From November(eg: Nov2009) to March (Mar 2010), it is EST, otherwise EDT.
EST is what I want ALWAYS and not EDT.
Is there any particular class / function, by which I can always read it as EST?
Awaiting for response.
Thanks for your replies.
Well, I forgot to mention a few things.
I want my machine to be set to: Eastern Time (US & Canada) in the windows time zone settings.
In simple terms, What i want to do is: get my machine time, and write it to a text file
I am aware of the daylight saving which happens from March to Nov.
But the problem is, when I write my machine time to the file, it is written as 2010 01 12 15:56:00 EST if daylight saving (DST) is not present and as 20101012 15:56:00 EDT, if DST is present.
My concern is, whether it is DST or not, I want to write EST always.
I don't think you should do what you are suggesting.
You are saying that regardless of what your system timezone is currently (Eastern Time is NOT your time zone, but UTC+5 or +4 is), you want it to display EST. This is just plainly wrong. Suppose it's in the summer, and your computer thinks it's 2010/6/15 15:00 locally. You print the current time and you get:
The time I print this out at: 2010 06 15 15:00:00 EDT
For whatever reason, you think that the EDT is unpleasing, and so you change it to EST:
I print this out at: 2010 06 15 15:00:00 EST
however, if you then send that snippet to someone else within the next hour, they'll be forced to think you traveled from the future! Since 15:00:00 EST is 16:00:00 EDT.
I would create a custom zone:
TimeZone alwaysEst = TimeZone.getTimeZone("EST+5");
That will report as EST and will always be 5 hours ahead of UTC. In particular, do not choose an existing timezone or you will eventually get burned when a zone update changes the definition.
Do be aware that by forcing EST the dates you log will only match the time displayed by the system for 5 months out of the year. The other 7 months you'll be an hour off. It may simplify parsing the file, but it will confuse users.
Date-time work is tricky
I a newbie to java and hence haven't been able figure this out since quite some time.
Date-time handling is surprisingly tricky for even experienced programmers. The concepts are rather slippery. All kinds of practices have developed by various people in various industries, thereby complicating matters.
Fortunately, Java now has built-in the industry-leading framework for date-time work: java.time defined in JSR 310.
Explicitly specify desired/expected time zone
I am using Windows XP and the machine is set to TimeZone:
The host OS should not affect your date-time handling work in Java.
Never rely on the host machine's current default time zone. That default can change at any time, so it is outside your control as a programmer. Instead, specify your desired/expected time zone within your Java code.
ZoneId z = ZoneId.of( "America/New_York" ) ;
EDT / Eastern Time is not a time zone
… Eastern Time (US & Canada).
There is not really such a thing as "Eastern Time". This imprecise term is a grab-bag collective noun used to describe the current offset-from-UTC used by various time zones. When doing programming, forget all about "Eastern Time", "EST", and other pseudo-zones.
Offset versus zone
Understand that an offset is merely a number of hours-minutes-seconds ahead or behind the prime meridian. An offset looks like +05:30 or -05:00.
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. The rules of a time zone are set capriciously by politicians, and change with surprising frequency.
A proper time zone name is composed as Continent/Region such as America/Montreal or America/New_York. See this list of zones at Wikipedia (may not be up-to-date).
ISO 8601
writes a string like: 20101012 15:56:00 EST, to a file.
We have a formal international standard for the formats of date-time values being serialized as text: ISO 8601. Do not invent your own! The standard formats are wisely designed to be practical, easy to parse by machine, and easy to read by humans across cultures.
For example, nearly 4 PM on the twelveth of October in 2010 in Québec would be:
2010-10-12T15:56-04:00
The java.time classes use the ISO 8601 formats by default when parsing/generating text. So no need to specify a formatting pattern. Use ZonedDateTime to represent a moment as seen through a particular time zone.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.of( 2010 , 10 , 12 , 15 , 56 , 0 , 0 , z ) ;
The ZonedDateTime::toString method wisely extends the standard format to append the name of the zone in square brackets.
zdt.toString(): 2010-10-12T15:56-04:00[America/Montreal]
Parsing such a string.
ZonedDateTime zdt2 = ZonedDateTime.parse( "2010-10-12T15:56-04:00[America/Montreal]" ) ;
boolean sameZdts = zdt.equals ( zdt2 ) ;
sameZdts: true
See that code run live at IdeOne.com.
Daylight Saving Time (DST)
EST is what I want ALWAYS and not EDT.
Your goal does not make sense.
Those pseudo-zones (EST & EDT) are meant to indicate when Daylight Saving Time (DST) is in effect and when standard time is in effect. So wanting to always be using standard time in a region (a time zone) that observes DST is a contradiction, and not possible.
So if you are trying to represent a moment as seen through the wall-clock time used by the people of a particular region (a time zone), you should specify the date, the time-of-day, and the time zone via ZoneId, and let java.time handle the issue of whether DST is in effect or not.
LocalDate ld = LocalDate.of( 2010 , Month.OCTOBER , 12 ) ;
LocalTime lt = LocalTime.of( 15 , 56 , 0 ) ;
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
zdt.toString(): 2010-10-12T15:56-04:00[America/New_York]
To see that same moment in UTC, extract an Instant. Same moment, same point on the timeline, different wall-clock-time.
Instant instant = zdt.toInstant() ;
instant.toString(): 2010-10-12T19:56:00Z
See that code above run live at IdeOne.com.
Booking future appointment
am dealing with a server to whom I need to tell: "Do a task at 20101015 15:30:30 xxx" xxx being the timezone. The server understands only EST and not EDT whatever month it is. Hence, EDT is unpleasing.
Regarding the EST versus EDT, we already covered your concern as senseless/irrelevant. These flags for standard time versus Daylight Saving Time automatically switch over when a particular time zone cuts over to/from DST.
As for booking an appointment in the future, you cannot know if Daylight Saving Time (DST) is in effect or not. You cannot know if some other wacky adjustment has been made to the rules of your time zone.
As mentioned above, time zone definitions are controlled by capricious politicians. Around the world, they have shown a surprising penchant for frequent changes. The US has changed the dates of their DST cutovers multiple times over the decades. Russia has adopted and dropped DST multiple times in recent years. The latest fad is to stay on DST year-round, done recently by Turkey and Morocco, and the United States is on the verge of going this way as well.
So if you want an appointment or a task to be done at a certain time-of-day, you have represent that as a LocalDateTime which represents a date and time-of-day without the context of an offset/zone, plus represent separately the intended time zone (ZoneId).
LocalDate ld = LocalDate.of( 2020 , Month.MARCH , 15 ) ;
LocalTime lt = LocalTime.of( 15 , 0 ) ;
LocalDateTime ldt = LocalDateTime.of( ld , lt ) ;
ZoneId z = ZoneId.of( "America/Thunder_Bay" ) ;
That code above describes when we intend something to occur, but cannot determine a moment. We do not know when 3 PM will happen on March 15, because the politicians controlling the America/Thunder_Bay time zone may redefine the time zone at any moment. So all we can do is occasionally take a stab at it, dynamically. When you need to build a calendar, or schedule something to occur, you must apply the time zone to the LocalDateTime (not a moment) to produce a ZonedDateTime (a moment).
ZonedDateTime zdt = ZonedDateTime.of( ldt , z ) ;
Instant instant = zdt.toInstant() ; // Adjust from zone to UTC (an offset of zero). Same moment, same point on the timeline.
Executors
To schedule your task to run, learn about the Executors framework built into Java. Specifically, the ScheduledExecutorService class.
Firs calculate your elapsed time to wait until running the task.
Duration duration = Duration.between( Instant.now() , instant );
Define the task to be run as a Runnable. Here we use modern lambda syntax, but that is beside the point.
Runnable runnable = ( ) -> {
System.out.println( "Running our task now: " + Instant.now() );
};
Schedule your task to run. Optionally capture the returned ScheduledFuture if you want to track completion.
final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture future = scheduler.schedule( runnable , duration.toMinutes() , TimeUnit.MINUTES );
Important: Be sure to gracefully shutdown your ScheduledExecutorService. Otherwise, the thread pool may continue running, even after you end your program.
It should be noted that during the Summer months of the Eastern timezone, pretty much every major center uses Daylight Savings Time, so you are correctly being shown the time that people in that timezone care about.
If you want it to be a specific timezone, rather than the default/system timezone, then you can force the TimeZone to EST via something like this:
TimeZone est = TimeZone.getTimeZone("EST");
Although as Michael mentioned above, the documentation recommends using a local full name such as "America/New York" rather than the general timezone abbreviation.
If you want to claim that it really is EST even though you know it to be EDT, then I suppose you could do the following: use a SimpleDateFormat instance with a custom pattern that doesn't include the timezone information, then tack "EST" onto the end of the String you write out.
Your question is still not clear.
I didn't undestand if you simply want to force 'EST' chars, even if your machine is set to automatically chage DST, or what you want is to get the actual time on EST.
So you have two options:
Your machine time is set to 2:15pm and DST in on effect, and you want to write 2:15pm EST (which is not the correct actual time) you should use SimpleDateFormat. This way, you will be lying about the time. But, anyway, you know what best fits for you.
Your machine time is set to 2:15pm and DST in on effect, and you want to write 1:15pm EST, you should use: TimeZone.setDefault(TimeZone.getTimeZone("EST"))
The solution depends on why you're writing this time out and what you want to do with it when you read it back in (what it's going to be used to do, not "what you want"). Always writing it out as EST is a misrepresentation of fact. When EDT is in effect it is not, in fact, EST. If you're trying to track some "absolute" point in time I recommend working in GMT instead of any timezone.
the machine is set to TimeZone:
Eastern Time (US & Canada).
That's not a real time zone (neither are EDT and EST). A time zone (as understood by Java) is something like "America/Phoenix", which is an ID for an administrative timezone that has both a base offset and (optionally) switches to daylight savings time at specific dates. Both of these can change, and it's necessary to take such changes into account when interpreting historical dates.
Thus, if you don't want to have DST switches, choose a timezone that does not observe DST. It is possible that there is no such timezone and trying to act like there is would introduce impossible dates.

Categories