I've noticed that on some devices the NSTimeZone's name method for a particular timezone can return different values. When testing the Brisbane time zone, my device returns #"Australia/Brisbane" whereas another user's device returns "Etc/GMT-10". Both iPhone's are running 3.1.2.
The Date and Time Programming Guide for Cocoa states that:
timeZoneWithName: The name passed to this method may be in any of the
formats understood by the system, for
example EST, Etc/GMT-2,
America/Argentina/Buenos_Aires,
Europe/Monaco, or US/Pacific, as shown
in the following code fragment.
I'd just like to know what could determine which value is used? The device? The language?
I've discovered what the reason for this was.
When manually setting the timezone from Apple's build-in list, the correct and exact timezone name is returned. However, some mobile carriers provide the current time zone offset over the air, and if this is the case, Apple provide an "Automatic" setting which allows the iPhone to change time zone automatically when they move across into another time zone.
The problem is that while the current GMT offset is provided, there's no way to tell what latitude the user is at. Obviously, there may be for example, several cities in Australia with the a time zone of GMT -10. Therefore, no specific time zone name is available, only that the phone is currently GMT -10.
I've never scene what you describe. In my experience it returns the name you created it with.
In any case, if you're using the name for UI display, you should call [NSTimeZone localizedName:locale:] to force the name style you want.
Related
At 6:00am Sydney time, Rory Allan clicks the foofoo button in their browser.
This inserts into the foofoo table with an 8:00pm UTC timestamp in the database of my server.
A lot of people are excited about this foofoo button, and they wanted to see what time of the day Sydney time Rory clicked it.
As I stare at my screen in California and view the status of the click, I want to see that he clicked it at 6:00am, not 1:00pm, which is what I will see if I pull the UTC time from the server and let my browser convert it.
You see, a lot of different people click foofoo, such as Phillip Herman in Germany, or Ivan Efimiov in Russia. And all we care about is the relative time of the day they clicked it, in the location they clicked it, regardless of the viewing location.
I don't know the best way to do this. Do I take the local timestamp and convert it to a string, storing it in addition to the real UTC timestamp? Or is this a common problem with a common resolution that I haven't found? I'm guessing / hoping the latter.
This isn't Language specific. These dates have a long journey:
Unix timestamp > Python date > JSON > Node > Mongo > Node > Browser
Ok, clearly 8:00pm UTC is not enough information. But what do you really want to know?
Is 06:00 enough information?
Look for time-only data types, preferably those that reflect time-of-day, rather than elapsed-time. These are often called LocalTime, TimeOfDay, etc.
Or, use strings in HH:MM format (on a 24-hour clock)
Or record the total number of minutes as an integer (60 * HH) + MM.
Or if you need a higher precision, then use seconds, or milliseconds, or microseconds, etc...
Is 06:00 Australia/Sydney enough information?
Store the time as mentioned earlier and store the time zone name in a separate string.
But be careful, because without a date you don't know if UTC+10 or UTC+11 was in effect.
Is 06:00+10:00 enough information?
You could look for a time with time zone data type, such as the one that exists in PostgreSQL, though even the PostgreSQL docs strongly discourage using this type.
Instead, store the time and offset as separate components.
Be careful, because you'd not necessarily know if this data was from Sydney. There are other time zones with UTC+10 at parts of the year too.
Assuming you have the date, such as today, then:
Is 2017-05-31T06:00 enough information?
Store the date and time together in a component that is sometimes called DateTime or LocalDateTime in various languages - but be careful that it is not bound to any specific time zone. Use DateTimeKind.Unspecified in .NET, or "naive" DateTime's in Python, etc.
For MongoDB and others that don't have such a type, use a string in ISO8601 format.
Be careful, because this time could come from anywhere in the world. Nothing here relates it to a specific point in time.
Is 2017-05-31T06:00+10:00 enough information?
Some languages and databases have a DateTimeOffset or OffsetDateTime type for this purpose.
If not, you can store a "Unix Timestamp" and also store the offset from UTC separately.
Or you can just store a string in ISO8601 format with the offset included - just be very careful with regard to comparisons / sorting.
Is 2017-05-31T06:00 Australia/Sydney enough information?
Store separately the date+time component from the time zone.
Consider that a time may be ambiguous during a DST fall-back transition.
Is 2017-05-31T06:00+10:00 Australia/Sydney enough information?
Here we have everything we might possibly need. Use a ZonedDateTime in Java/Joda-Time/Noda-Time, or an "aware" DateTime in Python (pytz, dateutils, etc.), or similar types when they exist in your platform.
Watch out for timestamp with time zone, as you might expect it to store the time zone and it typically does not (despite the name).
If not available, then consider storing a Unix timestamp and separate the time zone name as a string.
As you can tell - there are a LOT of options, and it really depends on exact use case and features available in each language/platform. You'll find more details if you search/ask for each one separately.
tl;dr
Exchange date-time values in UTC as strings in standard ISO 8601 format.
Work in UTC
General rule in date-time handling is to think in UTC, work in UTC, log in UTC, share in UTC, and store in UTC. But present in zoned time for the user.
By think in UTC, I mean every programmer needs to learn to stop their parochial thinking about their own particular home time zone. Translating back-and-forth to your own zone to UTC to other zones will drive a person nuts. Think of UTC as The One True Time®. All other zones and offsets are mere variations.
This strategy is much like internationalization. The programmer uses key strings in her own human language to look up string values from the localization tool to present a value (piece of text) in the human language preferred by her user. In date-time handling, the programmer works in UTC but applies a zone preferred by her user for presenting text in the user-interface.
Moment in UTC
The basic Java class for this Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
So when Rory in Sydney clicks his button, we record an Instant object.
Instant instant = Instant.now() ;
instant.toString(): 2017-06-01T09:24:54.435Z
Zoned
To present that moment to Rory in his own time zone, we apply a ZoneId to get a ZonedDateTime object.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = instant.atZone( z );
zdt.toString(): 2017-06-01T19:24:54.435+10:00[Australia/Sydney]
Now we have two objects, instant & zdt, that both refer to the same simultaneous moment on the timeline. The only difference is wall-clock time.
If the user in California wants to see the moment of Rory's button click according to Sydney time, then we already have that solution seen above. If not, if the California user wants to see the moment of Rory's button click in her own California clock, then read on.
We can adjust into yet another region’s wall-clock time by applying another time zone.
ZoneId zAmericaLosAngeles = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdtAmericaLosAngeles = instant.atZone( zAmericaLosAngeles );
zdtAmericaLosAngeles.toString(): 2017-06-01T02:24:54.435-07:00[America/Los_Angeles]
Now we have three objects that all represent the same simultaneous moment: instant, zdt, and zdtAmericaLosAngeles. One moment, three wall-clock times.
See this above code run live at IdeOne.com.
Time-of-day
If you literally meant you want the time-of-day only, without the date, you can extract a LocalTime object from those objects above.
But think twice about doing this, as presenting a time-only without date and zone can lead to ambiguity and misunderstanding.
LocalTime lt = zdt.toLocalTime();
all we care about is the relative time of the day they clicked it, in the location they clicked it, regardless of the viewing location
If you are really really really sure that is what you want, then combine my advice above. (But I doubt this is a wise way to go.)
LocalTime lt = LocalTime.now( ZoneId.of( "Australia/Sydney" ) ) ; // Current time-of-day in Sydney.
Library for date-time
We have been using the modern java.time classes in examples above. They are exceptional – I mean than literally. They are virtually unique. Virtually all other platforms have terrible support for date-time work. The predecessor to java.time was the Joda-Time project which was ported to .Net platform as Noda Time. Other than java.time & Noda Time, I know of no other decent library on other platforms.
ISO 8601
The ISO 8601 standard defines many sensible practical formats for textual representation of date-time values.
The java.time classes use the standard formats by default when generating & parsing strings. You have been viewing those ISO 8601 formats in examples above. Except for ZonedDateTime which wisely extends the standard by appending the name of the time zone in square brackets.
The T in the middle separates the date portion from the time-of-day portion.
For UTC, the Z on the end is short for Zulu and means UTC.
For offset-from-UTC, you see a plus/minus number of hours and minutes ahead of or behind UTC. A time zone is history of past, present, and future offsets in use by a particular region.
Database
How databases handle date-times varies widely, though poor support is most common. A database driver, such as JDBC drivers, add another layer of behavior. So no way to succinctly address that here. And this topic is already asked and answered in many other pages in Stack Overflow.
If your database lacks serious date-time support, you may be better off storing the ISO 8601 strings with the Z on the end. These values when sorted alphabetical are also in chronological order.
I use ZingChart to show data as a chart. In the char, I show the data following a time which I get from the server. ZingChart is set as default to follow client time.
I found we can set time zone in a chart only as integer value. However, some time zone are UTC+10:30, UTC+04:30...
So how can we set time zone to ZingChart showing correct time? And if that day has Daylight Saving Time, how should I correct it.
JSON scripts
Unfortunately it appears that ZingChart only supports whole-hour time zone offsets. Not only does this not account for time zones with fixed fractional-hour offsets, but it also doesn't properly account for time zones that use daylight saving time.
The example in the documentation says:
... For example, to set the timezone to Pacific Time, you would add: "timezone":-8.
This is incorrect, as Pacific time is only at UTC-8 during standard time. When it's in daylight time, it uses UTC-7.
This is a common mistake. See "Time Zone != Offset" in the timezone tag wiki. My recommendation to the ZingChart developers would be:
Anywhere you support timezone:-8 you should also support fractional hour offsets such as timezone:5.5 or timezone:8.75.
You should also support named time zone identifiers such as "America/Los_Angeles". To make them work, you'll need to provide a function that the developer can hook into. Don't try to implement the function directly, as there are several libraries already available for this. For example, a developer might combine ZingChart with moment-timezone by writing something like:
zingchart.fnTZOffset = function(timestamp, timeZone) {
return moment(timestamp).tz(timeZone).utcOffset() / 60;
}
ZingChart would invoke this function when timezone was a string and would apply the resulting offset to the specific data point.
Without support from ZingChart, there's not much you can do to properly support time zones.
One other solution to the Daylight Savings time issue some ZingChart users have mentioned in the past is MomentJS. http://momentjs.com
After allowing the user to select their timezone many applications ask if the DST adjustment should be made. Given resources like the tz database which contain past and present information on DST observances for each timezone, why do applications ask?
They shouldn't. Usually those that do are not using the tz database and have made invalid assumptions about how time zones work.
It is usually paired with a time zone selection dropdown that only lists numeric offsets, like this:
One should instead consider asking for time zone like this:
By asking for countries first, one can reduce the choice of time zones from the tz database to just a handful for the country. And since many countries only have a single time zone, sometimes the user will just need to select their country.
BTW - Both of the above graphics are from the Pluralsight course, Date and Time Fundamentals, of which I am the author. I cover this issue, and many other similar common mistakes.
You can also read more in the timezone tag wiki, in the section titled "TimeZone != Offset".
There is one common exception to this rule - Microsoft Windows. If the chosen time zone has DST, then Microsoft allows a user the option to disable it:
This is sometimes needed because there are places in the world that are not represented fully by the options Windows presents. Microsoft doesn't use the TZ database for this, but has their own time zones that they maintain.
For example, if you live in Atikokan, Ontario, Canda, the only valid selection in Windows is Eastern Time with DST disabled. Compare that with the TZ database, which has defined a zone specifically as "America/Atikokan".
This can create a problem for .NET developers, as TimeZoneInfo.Local.Id will return "Eastern Standard Time" regardless of whether the DST flag is turned on or off in the control panel. However, if it's disabled, then all of the adjustment rules will have been stripped away. In other words, TimeZoneInfo.Local != TimeZoneInfo.FindBySystemId(TimeZoneInfo.Local.Id). If the application just stores the ID, then it has no way to retrieve the time zone for somewhere like Atikokan.
I've read many a post here re: GWT date handling.
One in particular that struck a cord with me was this one
Sending a date and timezone from GAE server to GWT client
Anyhow, there's a need on a project I'm working on to be able to display days, hours, minute intervals as labels in a grid. My team has adopted an approach where all date/time instances are passed the client from the server in ISO8601 String format. The server time zone is to be respected by the client. The biz use case is that all date/time instances are in "market time", so that any browser that visits the app will see and work with dates in the "market time" timezone which happens to be GMT-05:00 (if Daylight Savings in effect) or GMT-06:00 (if Standard Time in effect).
I have posted some source on Github, here:
https://github.com/fastnsilver/gwt-datehandling-example
Particularly...
https://github.com/fastnsilver/gwt-datehandling-example/blob/master/src/main/java/me/fns/gwt/datehandling/client/util/CSTimeUtil.java
and the GWTTestCase
https://github.com/fastnsilver/gwt-datehandling-example/blob/master/src/test/java/me/fns/gwt/datehandling/client/util/CSTimeUtilTestGwt.java
in the hopes that someone can stare at the utility (and test) we're employing for date handling and help us see what we're not seeing.
EDIT
The basic problem is that CSTimeUtil#hoursInDay(Date) is not being calculated correctly in Production mode for "transition days" This method is used by other methods (like CSTimeUtil#dateToHour(Date) and CSTimeUtil#labelsForDay(Date)).
I have deployed our application with the current implementation of CSTimeUtil and it appears to work, but not quite. I'm really confused by alternate test results when e.g., mvn gwt:test is run in GWT Mode or Production Mode on Windows where the OS timezone is set to various timezones other than U.S. GMT-05:00 or GMT-06:00.
Based on some hints from Andrei and some serious blood, sweat and tears, I figured this out on my own. I have updated the code in Github, so if you're curious please go have a look there.
The basics:
Make sure all Strings are ISO8601 (no millis) compliant when sent from server to client and vice versa
Use DateTimeFormat.getFormat("yyyy-MM-ddTHH:mm:ss.SZZZZ") to format and parse dates
Retreive GMT-prefixed time zone info from java.util.Date in "Market time" using DateTimeFormat(Date, TimeZone), where TimeZone param is set as TimeZone.createTimeZone(TZ_CONSTANTS_INSTANCE.americaChicago()) and time zone String retrieved by TimeZone.getISOTimeZoneString(Date)
Generating days, see generateDay(Date, int) or hours generateHour(Date, int), from a source date had to take into consideration that an increment or decrement coudl trigger a change in time zone offset if occurring on a "transition day".
If you time zone is fixed, why would you use a string to represent date/time? You can send a standard Java Date object to the client. If you want, you can even store all dates and times as Longs and pass Longs only. You also send the GWT's TimeZone Json string for your time zone (once per session). You can find it in the GWT - there is a file with strings for all time zones.
On a client you use DateTimeFormat with many predefined formats to display whatever you need: full date, month and date, date and time, etc. Just remember to create TimeZone object from this Json string and use it in DateTimeFormat.getFormat(...).format(Date, TimeZone).
With this approach you don't have to worry about DST changes (they are encoded in that Json string) and locales. You only pass simple Date or Long objects.
I have a few places in the code where I need to use the TimeZone. I can get the timezone name using DateTime::TimeZone. Is it reasonable to put the timezone name in a constant? Or should it be in a variable?
If the value can change, use a variable, of it is guaranteed to stay the same, use a constant.
For a timezone, the chance it will change is not big, but it is also not 0. The user can change the timezone and it would be nice if the program behaves accordingly.
I vote for variable. You could end up with different users in different timezones working with your application, even if you doubt that could ever happen. It happened to me a couple of years ago.
Look at where the timezone is coming from. Is it the timezone of a city? Make it constant. Is it the timezone of the user? Make it variable - we users travel all the time, and we reset the timezone on our laptops so that Outlook scheduling will work correctly at the new location.