System timezone vs selected timezone - ios

What's the best way to compare two time zones?
I'm facing an issue while comparing two NSTimeZone instances using - (BOOL)isEqualToTimeZone:(NSTimeZone *)aTimeZone method.
NSString *timeZoneName = ...
NSTimeZone *sytemTimeZone = [NSTimeZone systemTimeZone];
NSTimeZone *selectedTimeZone = [NSTimeZone timeZoneWithName:timeZoneName];
if ([sytemTimeZone isEqualToTimeZone:selectedTimeZone])
isEqual = YES;
else
isEqual = NO;
Step 1: Go to Settings (Application) > General > Date & Time > Time Zone and search for "Austin". The entry that you'll get will be "Austin, U.S.A". Select this Time Zone. This SHOULD be your new system time zone now!
Step 2: Create a small iPhone/iPad application. Use [NSTimeZone knownTimeZoneNames] to get a list of time zone names. Then try to find "Austin". It's not there! So, i guess we can use "America/Chicago" as the timezone?
Why is the Setting's timezone list different from ours?
Step 3: Now compare the system timezone with time zone for "America/Chicago". They don't match.

I've posted a bug for this - The bug is that Austin isn't in the known time zone list yet is offered by the settings app.
As per an Apple Engineer:
For two NSTimeZones to compare equal, both the names and the data (as
returns by -data) must be equal. You have to be careful when
comparing time zones, because two zones can have the same offset from
GMT but not be the same time zone for historical reasons. Remember
that time zones are used for all sorts of calendrical calculations,
meaning that they are only equal if, throughout history, they've
always had the same GMT offset, the same daylight savings transitions,
and so on.

Related

NSTimeZone isDaylightSavingTime giving wrong value

Currently GMT-0700(US/pacific) is already in day-light-saving
But I am getting "NO" from NSTimeZone
NSTimeZone *timeZone = [NSTimeZone timeZoneForSecondsFromGMT:secondsFromGMT]; //Getting timezone as GMT-0700
BOOL isDaylightSavingTime = [timeZone isDaylightSavingTime]; //getting boolean value as NO
How to fix this issue?
REQUIREMENT :I want to know ,my receiver is using dayLightSavingTime or not.i will get only receiver offset value.I have to support different timezones()..What is the best approach to do this
timeZoneForSecondsFromGMT is not specific enough.
The most accurate way is to create the time zone with the (full) region name:
NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:#"America/Los_Angeles"];
This is not a wrong value. You get timezone GMT-0700 but this is not a Pacific timezone. To create pacific timezone you need:
timeZone = [NSTimeZone timeZoneWithName:#"PST"];
This is short description from apple documentation:
+ (instancetype)timeZoneForSecondsFromGMT:(NSInteger)seconds;
Description Returns a time zone object offset from Greenwich Mean
Time by a given number of seconds. The name of the new time zone is
GMT +/– the offset, in hours and minutes. Time zones created with this
method never have daylight savings, and the offset is constant no
matter the date.
Other answers mentioning timeZoneWithName are correct but there's one more detail I don't think has been mentioned. The reason that timeZoneForSecondsFromGMT doesn't work is that GMT does not have daylight savings time (or summer time, as it's more sensibly called in some other countries). GMT doesn't jump forward or back; it always moves ahead by one second per second. Since you ask for a fixed number of seconds from GMT, the result also does not have GMT. If it gave you a time zone that observed daylight saving time, the number of seconds from GMT would have to change twice a year. But since you asked for a fixed number of seconds, you get a result that doesn't do that, and never reports daylight saving time in effect.

iOS app occasionally sends an invalidly-formatted ISO8601 date to REST API

A couple of times a day, our PHP REST API logs an error causing by an invalidly-formatted ISO8601 date, coming from a GET request sent by our iOS app. The interesting thing is that most of the calls are fine (eg. 2015-07-07T00:00:00+10:00), but every so often we get a strange one (eg. 2015-07-07T12:00:00 am+10:00).
The code I believe is causing this is as follows:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
NSString *iso8601StringStart = [dateFormatter stringFromDate:self.searchStartTime];
Is there any circumstance in which NSDateFormatter could somehow (incorrectly) get am/pm from "yyyy-MM-dd'T'HH:mm:ssZZZZZ", when it's clearly the unintended behaviour? Are there certain kinds of NSDate that cause different behaviour? I'm stumped. The date given is always created via dateFromComponents.
I do not believe that that format string could ever generate the date with the am/pm annotations which you show. If I were you, my first course would be to double check that those dates are really being generated by those lines of code.
However, if you're sure this is happening, the only issue I can see is that it might be incorrect that you are not explicitly setting the locale and the calendar of the date formatter object. The date format syntax is defined by the unicode consortium, and the governing spec does say in section 4.5 that "If locales are not listed, dayPeriods fallback to AM/PM". I don't understand the whole document, but it suggests that being very explicit is the safest path.
If your only requirement is ISO8601, then you could use RFC3339 in UTC time zone, since this is a profile of ISO8601. This creates a correct formatter for that format:
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z"
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)!
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
My final solution (towards which I was nudged by algal's answer):
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
The Unicode spec was helpful (thanks algal), as was this Apple Technical QA, which suggested the en_US_POSIX as a specific solution.
"On iOS, the user can override the default AM/PM versus 24-hour time setting (via Settings > General > Date & Time > 24-Hour Time), which causes NSDateFormatter to rewrite the format string you set, which can cause your time parsing to fail."
Most helpfully, I found this explanation of the behaviour by huyz, although a little old:
When iPhone users change their region format between, say, “United States” and “France”, the users’ “24-Hour Time” setting is automatically switched to the mode that is most prevalent in that region. In France, that would set 24-Hour Time to “ON”, and in the U.S., that would set it to “OFF”. The users can then manually override that setting and that’s where trouble starts.
The problem comes from NSDateFormatter somehow “getting stuck” in the 12 or 24-hour time mode that the user has manually selected. So if a French user manually selects 12-hour mode, and the application requested NSDateFormatter to output time with the 24-hour format “HHmm”, it would actually receive time in a 12-hour format, e.g. “01:00 PM”, as if the application had instead requested “hhmm aa”. The reverse would happen if a US user manually selected 24-hour mode: outputting time with the 12-hour format “hhmm aa” would actually get you time in the 24-hour format instead, e.g. “17:00″.

Ignoring time-zone changes after saving NSDate

My apps are time-zone agnostic: I want the dates and times saved and displayed in a manner that ignores changes in the user's time zone. Once I've recorded the local date/time of some user action in my app (typically by saving timeIntervalSince1970 in an sqlite DB), I want to show the same date/time string regardless if the user's time zone has changed or not, and regardless of the user's various time zone settings in his device.
For example, if a user's action occurs at 1:15 pm local time in San Francisco, and then he opens my app a few days later in New York, I want that prior action to still appear as 1:15 pm (not 4:15 pm), and even if he's manually set some other time zone in his device (e.g., Chicago). A second action in New York at 9:00 pm there, should forevermore display as 9:00 pm, even when back in California.
I could achieve my goal by saving every date/time as a string (based on the user's local time zone), and then never use the date methods again -- but then I couldn't do date/time arithmetic (e.g., sorting).
How can I be time-zone agnostic when saving and retrieving dates/times?
(Apple's Calendar app seems to behave this way when Time Zone Override is on, but only if I manually set the event time.)
Here's the working solution (#Hot Licks deserves the credit, but he didn't post an answer):
Subclass NSDate and add a gmtDate method that does the conversion from local TZ to GMT. gmtDate uses #"yyyy-MM-dd HH:mm" as the format string, which also drops seconds from the value. Like this:
+(NSDate *)gmtDate
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm"; // drops the seconds
dateFormatter.timeZone = [NSTimeZone systemTimeZone]; // the local TZ
NSString *localTimeStamp = [dateFormatter stringFromDate:[NSDate date]];
// localTimeStamp is the current clock time in the current TZ
// adjust date so it'll be the same clock time in GMT
dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSDate *gmtDate = [dateFormatter dateFromString:localTimeStamp];
return gmtDate;
}
In didFinishLaunchingWithOptions: add [NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]] so all date formatters make date strings in GMT by default (but never include the timezone in the format string).
When the date/time of a user action needs to be saved, get the date via [NSDate gmtDate] and save that value in your database.
Record the time zone along with the timestamp. Use that time zone when formatting the timestamp later. You'll need to add another column to your database table.
Either save the time zone, as Rob suggested, or save the time as both an NSDate and a string. Use the string for the UI and the NSDate for calculations.
Or hey, do all three, if you need them.

NSDate fixing timezones

I know that NSDate doesn't have timezone information.
However, I'm trying to understand how to manipulate them properly.
At the moment I'm passing a date into an object. That date is the user selected date at time 00:00:00.
i.e. if the user hits October 21st then the NSDate passed in should be. 21/10/2013 00:00:00.
However, it isn't it's 20/10/2013 23:00:00. (One hour before).
Now, this is nothing about formatting them or displaying them. I'm just using the NSDates.
I'm creating the date using NSDateComponents and NSCalendar.
I guess my question is...
How can I tell what date an NSDate is actually referring to in my local time zone?
I need to send a UNIX time stamp for 00:00:00 and 23:59:59 for a given date. However, at the moment when I set the hour, minute and second to 0, 0 and 0 then I'm not getting midnight in the current time zone I'm getting midnight in GMT.
This isn't what I want.
Fixed?
OK, I've fixed it... I think. At least, it's doing what I want it to do.
The trick is...
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
[dateComponents setSecond:timeZone.secondsFromGMT];
I've been confused by this many times. When you NSLog an NSDate, you'll always get the output in GMT. So the 20/10/2013 23:00:00 (GMT) you're seeing is the same as your expected 21/10/2013 00:00:00 (BST). The UNIX timestamp for both of these dates would be the same because it doesn't take into account timezone - it's always UTC.
If you want to output in a user-readable format, an NSDateFormatter will format the date using your current timezone and locale.

Find DST (Daylight Savings Time) timestamp using Objective-C/C?

Is there a way to get the specific date (way) when daylight davings begins and ends for each country using C or Objective-C?
In the Mexico, summer time begins on the first Sunday in April at 2:00am, and ends on the last Sunday in October at 2:00am. In many POSIX systems this is written as
M4.1.0/2,M10.5.0/2
(Begins: Month 4, 1st Sunday at 02:00AM, Ends: Month 10, last Sunday at 02:00AM)
I know it is possible to know if daylight savings is currently active using
NSTimeZone* systemTimeZone = [NSTimeZone systemTimeZone];
BOOL dstIsOn = [systemTimeZone isDaylightSavingTime];
and that it is possible to get the time until the next Daylight Savings begins
NSTimeZone* systemTimeZone = [NSTimeZone systemTimeZone];
NSTimeInterval delta = [systemTimeZone daylightSavingTimeOffset];
BUT: How would I go about finding the specific day that the daylight savings begins/ends?
Is there some killer table out there that I have not been able to find?
Any help would be greatly appreciated!
Yes, there is - you can use [NSTimeZone nextDaylightSavingTimeTransitionAfterDate:] method. This returns an NSDate, which you can use with daylightSavingTimeOffsetForDate to establish what the offset is.
Normally you'd use this to find the next offset, but you can obviously run it more than once with different dates to get a series of upcoming daylight saving changes. There is also a convenience method nextDaylightSavingTimeTransition which will always return the next transition.

Resources