I work for an organization in two locations. One of them is Chile, where daylight savings time is extremely unpredictable and often varies from year-to-year. We have an application that is dependent on time and up until now, have been using joda time. When the Chilean government decides to change daylight savings, we use joda code to set an offset to the default DateTimeZone:
/**
* Updates the default time zone, i.e. the time zone which is used as local time.
*/
private void updateDefaultTimeZone() {
if (isEmpty(Configuration.Value.TIME_ZONE_OFFSET)) {
// make the site's default time zone the current time zone for joda time,
// this should always be the case for the non-Chile site, and work for the Chile site during most of the year
DateTimeZone.setDefault(siteService.getSiteTimeZone());
} else {
// makes a user defined time zone the current time zone for joda time
// this will be used when Chile is delaying or pulling forward the transition from/to daylight
// saving time and the JVM is therefore not able to calculate the local times appropriately
Integer offset = getInteger(Configuration.Value.TIME_ZONE_OFFSET);
DateTimeZone.setDefault(DateTimeZone.forOffsetHours(offset));
}
}
We're trying to transition our code base from joda time to Java 8 time, but I have really no idea the classes that are involved in doing something similar. I'm guessing perhaps ZoneRules, but I'm not sure. Does anyone have any hints as to how to most cleanly accomplish this?
Additionally, what is the best way of converting units in Java 8 time? Say I have 24 hours and I want to convert it into days. (I ask because in this case, it seems many methods work with milliseconds and I want to work with hours, but think there must be a better built-in way to convert than to do the arithmetic by hand.)
Use java.util.TimeZone.setDefault(TimeZone). Because JSR-310 java.time.* is integrated with the JDK, there is no need for a separate method.
(To convert units, you may find it easiest to use TimeUnit, but perhaps a separate more focussed question would help you there.)
JodaStephen has said it. He’s the lead developer of both Joda-Time and java.time, so definitely the authority here. I add just some supplementary thoughts.
Consider not relying on a default time zone
The default time zone of the JVM can be changed at any time from another part of your program and from other programs running in the same JVM. So relying on it is fragile. java.time (Java 8 time) consistently offers you the possibility of specifying time zone for all time zone sensitive operations. My habit is to use this. So my suggestion is that you store the time zone somewhere in your program where you alone control who’s tampering with it and code your date and time operations to use this stored setting and not the default time zone of the JVM.
If you still rely on the default: The TimeZone class has a bad design flaw. This would look sound to me at first sight:
TimeZone.setDefault(TimeZone.getTimeZone("America/Santiago_de_Chile"));
It isn’t! Look at the output from
System.out.println(TimeZone.getDefault().getID());
GMT
Extremely confusing IMHO. The correct time zone ID is America/Santiago, not America/Santiago_de_Chile. So we should have expected an IllegalArgumentException or similar here. Instead TimeZone just tacitly gives us a completely wrong time zone, GMT.
Instead use:
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("America/Santiago_de_Chile")));
Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: America/Santiago_de_Chile
Time unit conversions
Like JodaStephen I routinely use TimeUnit for time unit conversions, but I try to remember to think twice before doing so.
System.out.println(TimeUnit.HOURS.toDays(24));
System.out.println(TimeUnit.DAYS.toHours(400_000_000_000_000_000L));
1
9223372036854775807
It looks right, doesn’t it? It isn’t either! For two reasons:
A day is not always 24 hours. When Chile goes from standard time to summer time (DST) or vice versa, a day is 23 or 25 hours.
The conversion doesn’t report long overflow. The latter conversion overflows. TimeUnit tacitly gives us the closest value that can fit in a long, in this case the value of Long.MAX_VALUE.
When you know what you are doing and you know your conversions don’t overflow, you may use it. I also do most of my int math without checking for overflow. java.time offers a nice converstion that does check for overflow:
System.out.println(Duration.ofDays(400_000_000_000_000_000L).toHours());
Exception in thread "main" java.lang.ArithmeticException: long overflow
I even find this code a bit more readable than the TimeUnit code.
Related
I need to store datetime records with IANA database current version used (2022g for example). How could I get it with Go?
I tried to search this on standard "time" package, but it seems that there isn't any functionality for this. I am expecting that there is some function that can return IANA tzdb version as a string.
UPDATE 1 according to comments below I need to clarify the problem:
The main problem is I want to store some FUTURE events. The event object has several fields:
Local dateTime
Timezone
UTC datetime
To keep my data up to date with IANA database (timezone, daylight saving time may change) I need to store current version of tzdb version. That will help me to write correct data migration of my events when new version of tzdb was released. So I need to add one more field with version of current tzdb that had been used to populate the time.
And I am trying to figure out how can I get the current version of my tzdb that Go application is using right now to store that version.
Also I am opened to alternative solutions of storing time records with extra accuracy of long-lived future events.
Update 2: This events are bounded to exact location.
The discussion thread in the comment is pretty long, but I'll attempt to answer and address some of the concerns. (I won't address the question in the title, as I believe that is not straightforward in Go.)
Indeed, future scheduling of events should be in terms of the time zone where the event takes place - which is usually not UTC.
Time zone identifiers will never be removed or renamed (with rare exception anyway). Once introduced, the identifier will either maintained as a Zone or as a Link in the TZDB indefinitely. Thus, you don't need to check that the time zone still exists. (Windows time zone IDs are also like this.)
DST is only one aspect of picking the correct offset. The standard time may have changed as well. However, all of that is encapsulated in the tzdb itself. You shouldn't need to concern yourself about which version of the tzdb was in effect when you created the event.
The general approach to this issue in most cases is:
Store the scheduled local date, time, and time zone ID of the event (local with regard to the time zone of the event).
Example: 2030-12-31T00:00:00[America/New_York]
At the time you create the event, also calculate a UTC value (or equivalent DateTimeOffset value) and store that in a separate field - so you know exactly when to fire the event:
Example: 2030-12-31T05:00:00Z (or 2030-12-31T00:00:00-05:00)
Periodically check that your UTC equivalent is correct. This can be in a daily maintenance task, or on application startup, or just before the event (perhaps also an hour before the event), or all of these.
The offset will only be different than projected if the time zone data changed on the device to give it a new offset. For example, let's hypothetically say the lawmakers in the USA succeed at making DST permanent sometime before this event takes place. Then the equivalent UTC time for the same event would now be 2030-12-31T04:00:00Z (or 2030-12-31T00:00:00-04:00).
In such cases, update the UTC time of the event if it has changed, but the original local time of the event usually should not be modified. Human beings tend to schedule things in terms of local time, not in terms of their UTC equivalents.
The only advantage knowing the TZDB version would give you, is you could do that last step less often - only when knowing the data has changed. I see that as an optimization though - it's not usually required.
Without such legal changes to time zone definitions, the mere start/stop of DST as scheduled is not a reason to worry about this. That is already accounted for by using the TZDB in the first place.
If the event is recurring (say a 10:00 AM daily meeting), each occurrence might have a different offset, but the local time will be consistent and the TZDB doesn't need to be updated to calculate it.
In some cases, the bot may have knowledge of the user's timezone. Is there a way to pass this knowledge to the bot framework so that it can handle certain date forms correctly - e.g. "tomorrow" & "yesterday"? Even things like "next friday" are sometimes sensitive to the timezone.
Currently, it appears that these are handled assuming the user's timezone offset is zero.
It seems that the chronic parser is unreachable so you can't really change anything to that. I see 3 solutions.
Solution 1: Create your own RecognizeDateTime class to work with utc time or however you want.
Solutions 2: When you get the date (in the validation method for example) add the utc offset to the time. This will preserve the right date, for utc times and local times.
dateTime += TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
solution 3: When the datetime is created by using a string like "yesterday", the day is correct but the time defaults to 00:00 or 12AM. To make this less sensitive you can just add 12 hours to the UTC time. This will preserve the day in almost all timezones. (All timezones above 12+ will have a problem. Luckily there aren't many people there.) (This is the solution I'm currently using for one of my solutions)
Perhaps this question should be broken up into two posts, but I currently have an API for a few business customers. I am currently using ISO 8601 timestamps with a UTC time zone to represent times. However, I don't like the idea of these timestamps being attached to any timezone because the times should be the same no matter what timezone you are in. 5PM UTC should be 5PM CST, etc...
I know that you can leave the Z off of an ISO timestamp, and it will be interpreted as whatever local time you are in. Is this ok practice? And if so, how do I do this in Ruby? I read the doc for the Time class and didn't see anything about this.
EDIT: Let me re-word this just a little bit, or atleast clarify something. The reason why I'm seeking timestamps that aren't attached to a timezone is exactly because I know that my client servers and API server will hardly ever match up. If a client is submitting an event with a time, that time needs to be ambiguously equal to the ambiguous locale specific to the event that the user is working on.
That's a mouthful...assume that I'm working on an event scheduler. Each event belongs to a storefront or location of a company. When times are being shown for a location, it is assumed that the times shown are in the timezone of the location, and for clarity's sake should never be shown at a time formatted to a user's local timezone. If I'm looking at the scheduler on the East Coast, but looking at events for locations on the West Coast, the times I should see should be local to the locations on the West Coast, not adjusted for my timezone.
I know a solution could be to simply store times with timezone information for the location its associated to. But the use case that a user would want to convert a time to their timezone is VERY rare, and I'd rather make implementing my API easier...this was actually my original implementation but implementing the API in many different environments and across multiple programming languages, it became clear that it is a hurdle to show times local to that timestamp's timezone for a lot of languages. If a user wanted to convert times to their local timezone I could easily store global timezone information for the location object itself.
I don't know what you mean by "the times should be the same no matter what timezone you are in. 5PM UTC should be 5PM CST, etc..". 5PM UTC clearly isn't 5PM CST!
Anyway, I don't think that what you are proposing is an ok practice. Suppose you leave off the Z and have a timestamp be interpreted as whatever local time you are in. Since this is a network API, the client and server might not be in the same timezone. When the client submits a "local" time, what does it mean? The local time on the client (if so, how does the server know what that is?)? The local time on the server? It's ambiguous. This is the crux of the reason why just about the only reasonable thing to do is to use UTC throughout.
What you can do is attach a timezone to a timestamp if it might be relevant. For example, "you should observe one minute of silence at 2012-11-10T22:00:00Z in honour of the soldiers who died in WW1" sounds weird because Rememberance Day isn't on November 10! "you should observe one minute of silence at 2012-11-11T11:00:00+13:00" sounds a lot better once you put that New Zealand time zone in there... In this case you can keep and timestamp (in either local or UTC) together with the timezone offset (e.g. store both of them together in your database).
It does, however, depend on what your times represent. For example, in "at equinoxes, sunset happens at 18:00" it makes sense to use an abstract time that isn't qualified with a timezone (it's true in every timezone, and/or you're talking about solar time). But attaching a date to this abstract time makes little sense, so I don't think you would be talking about ISO8601 in this case.
I've an application that depends heavily on Java. We do extensive logging, database insertion, etc. After the day light timing switch we noticed that all Java time is about an hour behind. We've Jre version of 1.6_18. I thought this issue was resolved in earlier versions of Java. Do suggest as to what can be done, if there are any patches for this.
Timezone information is modified periodically. Java 6 update 18 is likely to have out of date DST settings for your location.
Either upgrade to the latest (update 25) or run the TZupdater tool.
EDIT: I have just discovered that Oracle provides an RSS feed for timezone updates. If your application absolutely must have the most recent TZ data keep an eye on this.
tl;dr
Your database of time zone ‘tz’ info is likely out-of-date.
Use the back-port of java.time classes.
See Tutorial.
Includes up-to-date ‘tz’ database.
Avoid troublesome old legacy date-time classes from earliest Java.
Use UTC for most of your work.
Use UTC
Think of UTC as the “one true time”. Any date-time for a particular time zone is a derivation of UTC. When at work doing programming or sys-admin work, forget about your own local time zone. I strongly suggest adding a clock to both your physical and digital desktops displaying UTC. In a pinch, use a web site such as Time.is.
Most of your business logic, logging, data storage, data exchange, and database persistence should all be in UTC. Adjust into a time zone only for presentation.
Avoid old date-time classes
Avoid the troublesome old legacy date-time classes bundled with the earliest versions of Java. Now supplanted by the java.time classes.
Instant
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds.
Instant now = Instant.now(); // Current moment in UTC.
Specify time zone
The Java Virtual Machine has a current default time zone. This may be picked up at launch from the host OS. Or it may be set at launch by configuration settings. Or it may be changed at any moment by any code in any thread of any app within the JVM -- affecting all other code during execution!
Given that the JVM’s current default time zone varies, and is outside your control as a programmer, never depend on it. Always specify your desired/expected time zone.
Generally best to keep server host OS set to a time zone of UTC, but again, never depend on that in your programming.
ZonedDateTime
To adjust an Instant into a time zone, generate a ZonedDateTime object.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
Call toString to generate a String in standard ISO 8601 format. For other formats, search Stack Overflow for the class DateTimeFormatter.
Update your tz time zone database
Time zone definitions and their anomalies such as Daylight Saving Time (DST) change frequently, surprisingly often.
Oracle release regular updates of Java that include fresh copies of the tz database. If you cannot install those Java updates, or if a nearly last-minute change to zones was made by some careless politicians, you can manually update your Java using the Timezone Updater Tool by Oracle.
Locale
Know that Locale has nothing to do with time zone. A Locale defines (a) a human language to use for translation, and (b) cultural norms deciding issues such as abbreviation, punctuation, and order of parts. So you only need a Locale when generating Strings, when using the DateTimeFormatter class.
Locale l = Locale.CANADA_FRENCH;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
So a time zone defines the meaning of a date-time value, but a Locale defines its presentation.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I wonder if, when you call something like that
time();
it returns the timestamp relatively to your time zone setting or to UTC ?
For example, when syncing two agents, that must be important to take care of it, doesn't it ?
You are absolutely right that the time needs to be adjusted for users at different locations.
First, you need to be able to know
what timezone each agent is in, which you cannot really guess, but ask the client to include the info in the request.
Then, depending on the language you
are using, you can either call some
function with the timezone offset as
the parameter to get the adjusted time, or calculate the
time yourself by adding
60*60*1000*offset milliseconds to the
UTC time.
In python e.g. time.time
[r]eturns the time as a floating point number expressed in seconds since the epoch, in UTC.