I need to do some date + time stuff that is not covered well by NSDate on the iPhone. I wonder if there is a library that has more sophisticated functionality regarding dates and time on a international level.
What I want to do is advanced date + time mathemathics. I need to:
convert between different calendar types (Gregorian <> Jewish <> Muslimish <> others)
convert times between different time zones (i.e. I have a swedish time and want to know what time is it in chicago)
add microseconds, seconds, minutes, hours, days, weeks, months, years to a date and find out what exact date+time it is then
find out how many microseconds, seconds, minutes, hours, days, weeks, months and years are between two given dates
draw calendars, so I need data about how many days a specific month has in a specific year
use case, for example: I fly from paris to chicago, and at 10:00 'clock in paris I only know the flight will take 10 hours. So I need to convert time zones and all this stuff to calculate what time I'll arrive in chicago. And then all this ugly summer and winter time stuff where they add or reduce one hour at some point in the year, and I think every country does that to their own liking, some may even fake the time way more than that adding a whole day to a year just to have more time. Don't know. Maybe there's a lib that protects me from all this complexity.
Basically what I want to do is a kind of world calendar but with some special features.
It's in the Date and time programming guide.
Convert from one calender to another
Convert between time zones
Calendrical calculations
etc...
NSCalendar + NSDateComponents?
However, like Peter Hosey said, it's hard to know without knowing what it is you want to do.
For people coming here looking for a lib, try Erica Saduns NSDate-Extensions. It doesn't cover everything you might want, but at least you won't have to do the basic stuff yourself.
Related
For example, Foo Holiday is the 3rd Tuesday in March. Bar Holiday is the 7th minute of the 7th hour of the 7th day of the 7th Month.
I have looked at standards like ISO8601 but it seems those are more concerned with explicit points in time.
As AlexApps99 described in their answer,
there is no standard that will cover all holidays.
For example, the start of Ramadan often depends on physically sighting the new moon, so it's impossible to predict the exact date, as cloudy weather might prevent anybody from spotting it in your country for a few days.
However, for many repeating events the follow simpler rules (e.g. Foo & Bar Holiday in your question), there is
iCalendar's RRULE (Recurrence Rule),
which is a standard for creating repeating patterns for recurring events.
Many calendar apps will have a GUI for creating RRULE.
RRULE is documented in
RFC 5545 Section 3.8.5.3.
For example, an event for U.S. Presidential Election day would be:
Every 4 years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day):
DTSTART;TZID=America/New_York:19961105T090000
RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
Example taken from RFC 5545 Section 3.8.5.3 Page 129
Foo Holiday (3rd Tuesday in March) could be described with:
RRULE;FREQ=YEARLY;BYMONTH=3;BYDAY=TU;BYMONTHDAY=15,16,17,18,19,20,21
Bar Holiday could be described with (7th minute of the 7th hour of the 7th day of the 7th Month) could be described with:
RRULE;FREQ=YEARLY;BYMONTH=7;BYMONTHDAY==7;BYHOUR=7;BYMINUTE=7
Unfortunately, the iCalendar RRULE spec can't can't handle more complicated rules.
Easter can't be done, (although https://github.com/sappjw/calendars contains Easter RRULEs accurate for
1900-2099, and the Python dateutil library's RRULE has an unofficial extension called BYEASTER, but even then, it only supports Western Easter).
Non-Gregorian calendars are also not supported, however, there is a
draft spec RFC7529 that adds
RRULE support for additional calendars, which would allow you to describe
Chinese New Year using the following syntax:
DTSTART;VALUE=DATE:20130210
RRULE:RSCALE=CHINESE;FREQ=YEARLY
SUMMARY:Chinese New Year
These define a recurring event for the Chinese New Year, with the
first instance being the one in Gregorian year 2013.
Example taken from RFC7529#Section 4.3.1
There's no standard way to represent holidays, because holidays are pretty hard to keep track of. Each country has their own set of holidays, and there are different definitions for what is considered a holiday (public holidays, school holidays, religious holidays, international hotdog day etc).
On top of that, the days that holidays lie on is pretty hard to predict. Many holidays celebrating events on the non-Gregorian calendar, such as Easter, will be on a different date each year, and others based on external events can be unpredictable into the future.
Many holidays are not just "days", but a range multiple days, or specific hours of a day, and you can't forget that these intervals have timezones attached too (and will be different in different countries)
With all this complexity, you'll need to create your own way to represent these holidays, probably ignoring some of the complexity you don't need to handle.
If you're sharing this data, a standard format such as iCalendar would be ideal, and you could store custom properties of the holidays in extension fields (starting with X-).
If you're just storing this data internally, you're better off making your own structure to store whatever you need, perhaps using ISO8601 to store the intervals.
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 want to schedule an online multiplayer game tournament, one per unique country, with a convenient start time for as many registered players as possible.
A global start time based on UTC is inconvenient for a number of countries. For instance, 17:00 UTC might be fine for those in the US but not in Australia.
Thus, I'd like to let each country have its own start time, say 17:00, but based somehow on the country's local time. The issue is that many countries have multiple time zones.
Would you recommend I take the average of the time zones per country? For instance, in the US, the start time would be 17:00 for those in the middle of the country, and +/- 2 hours for those on each coast.
I could further try to bucket users by timezone and have separate tournaments per time zone instead of country but that adds more complication to the design and prolongs the tournament to multiple mini-tournaments. That is, if you win your tournament in timezone A for your country, you still need to defeat the winners of the timezones B, C, ... for your country to be declared the country/national winner.
I'm not concerned about users understanding all of this (of course). I'll just show them a notification when it's time to play. I'm focused more on picking a convenient time to keep engagement up.
Has anybody dealt with this issue previously? How did you "solve" it?
Thanks for your thoughts.
Is there an "average" time zone per country? No. Many countries have multiple time zones. Sometimes a given country's time zones are not even near each other, due to the time zone applying to an island or overseas territory.
But could you create one for the purposes of your game? Yes. Of course, it won't necessarily reflect reality of the world. Some users would be behind the time in that zone, and some users would be ahead.
You might consider just assigning a separate start/end time for different groups of tournaments. For example, you could say that the US tournament starts at 8AM Central Time. It wouldn't matter whether or not users in other US time zones had the same local time or not - they would just start earlier or later in their local time accordingly.
China has 1 timezone. So it's probably just US, Canada, Russia and Australia that seriously use multiple timezones - might be worth just hardcoding those (for example to their capitals in most cases).
But there are few countries that less seriously use multiple timezones: Gran Canaria is 1 hour behind the rest of Spain. Guadeloupe is 6 hours or so behind the rest of France (= GMT+1). You 'll want to ignore that for France, because the average (= GMT-2) isn't what you want.
The whole concept of Dates and Times is incredibly flaky and constantly being updated so from the beginning it's an incredibly difficult task. From leap years to leap seconds there are so many variables to take into account.
John Skeet's library looks good but has taken him 3 years to get it this far and it's still far from perfect.
Could someone give me an indication on how the Go programming language handled DateTime's differently or similarly compared to other languages/libraries? This is purely for curiosity's sake.
This question is more about the similarities and differences between current available libraries - preferably in english, not pages and pages of documentation.
See the Go time package documentation and source code. A Time represents an instant in time with nanosecond precision without leap seconds. The IANA Time Zone Database is used for time zone and daylight savings time. The time zone database contains code and data that represent the history of local time for many representative locations around the globe. It is updated periodically to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules.
type Time struct {
// sec gives the number of seconds elapsed since
// January 1, year 1 00:00:00 UTC.
sec int64
// nsec specifies a non-negative nanosecond
// offset within the second named by Seconds.
// It must be in the range [0, 999999999].
nsec int32
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// Only the zero Time has a nil Location.
// In that case it is interpreted to mean UTC.
loc *Location
}
I'm developing an international software that act as a simple project management software and I'm facing a problem. This problem is about date/hour and time zone.
When a message is sent from one time zone to another time zone I can store the UTC (GMT) time in my database and then have it displayed differently according to the user's time zone. But this can't be done when I only work with date.
If I say a task is due to the 21st of March. Should I consider that this date can be 20 or 22 in some other countries ? What are your advices on this problem ?
Let's say a user in New York sets a due date for a project as "anytime on Monday 26 January". That means "anytime from 0600 Monday 26 January to 0600 Tuesday 27 January" in Brussels and "anytime from 2000 Sunday 25 January to 2000 Monday 26 January" in L.A.
So completing the task at 2100 on Monday 26 is fine in Brussels and N.Y., but too late in L.A.
One possible work around is never just work with the date. If the time is not specified, either set it for 0000 hrs or 2400 hrs on the date specified in the timezone of the user.
The users may have to deal with strange due dates/times, but speaking as someone who used to work internationally, it kinda goes with the territory.
You won't be able to achieve what you are trying to do without storing the exact time. You simply don't have enough information.
When you don't have a time, assume that the time is the end of business in the main locale for the application, then translate that time as you would any other time. An alternative would be assuming end of the business day in local time and adjust that to UTC. Everyone using the application would need to understand whatever default time assumption you make when the time is not specified. Coordinating to the main office may be best in a large enterprise whereas coordinating to local time may be best in highly decentralized environments where the local context is equally important.
If you aren't storing the minutes and seconds you have to assume that the date being entered is the desired date and not to any adjustments for GMT. Just put it in the database as is. The people on the west coast will have to assume that the due date is the same regardless of where you are in the world. If you want to adjust for time zones, you'll have to collect more information, like hour, minutes, and seconds.
The easiest solution would be just to display as the same date for everyone. The deadline would then effectively be midnight in the latest timezone.
Otherwise, decide what the default time of the deadline should be in the timezone the task was created in, e.g. 21st March 17:00 EST or 22nd March 00:00 EST and display that in the local timezone. The timezone difference will then push it into the previous day or next day accordingly for the viewer.
SQL 2008 allows for a Date datatype that does not have any time value associated with it. That allows someone to say I need this done by this Date, but I don't care if it is +/- several hours. If the date selected is 1/1/2009 but it happens on 1/2/2009 at 2AM their time, they probably don't care.
When the user needs something done by a specific date and time, like close of business on 1/1/2009 then you need to store it in a DateTime as UTC and convert it to local time client-side.
This will take much of the complexity out of indicating when something is completed, it'll either be completed near a specific day or by a specific time.
If you have a single instance of a DB, I would store all dates in the datetime timestamp of your DB server. If you are timestamping rows, consider GetDate() in T-SQL or as default value of the timestamped date column. Then you have your single reference point for all times. Consider UTC format there.
Then, all clients accessing the date do their own conversion into "local time" , which can be interpreted by things like : user preferences, date time stamp on client computer, etc.
Without knowing more, it hard to say exactly what the resolution is.
Your solution depends on your application and requirements.
I'd first store UTC + offset in your data structures, so it's easy to display for any timezone.
Most likely if a task or meeting is due at 12pm on 21/March in London then it will occur at 2130 on 21/March in Adelaide (+0930), but that is an application requirement not any sinister timezone related standard.
If you want the ultimate in flexibility, add a flag that can make the even due simultaneously in every timezone or at the same time no matter where you are (staggered) and show the event accordingly.
You might want to store the date in a from that is timezone aware. This will help you in your calculations. SQL Server 2008 for instance supports a datetimeoffset that does precisely this. Alternatively if you're using SQL 2005 with a bit of effort you can write your own SQL CLR data type to support this.