Wrong systemTimeZone - ios

I'm trying to get systemTimeZone, but it gives me wrong data:
NSTimeZone * currentDateTimeZone = [NSTimeZone systemTimeZone];
NSString* name = [currentDateTimeZone name];
int myGMT = (int)roundf([currentDateTimeZone secondsFromGMT]/60.f/60.f);
I'm living in Budapest,Hungary. It's in GMT+1, but I'm getting myGMT = 2.
But name is ok : name = Europe/Budapest
Why?

The current GMT offset for the Europe/Budapest timezone is GMT+2, because
the Daylight Saving Time started at Sunday, 30 March 2014, and the clocks were
advanced by one hour (see http://www.timeanddate.com/worldclock/city.html?n=50).
You can verify that with
BOOL isDst = [currentDateTimeZone isDaylightSavingTime];
// --> YES
NSTimeInterval dstOffset = [currentDateTimeZone daylightSavingTimeOffset];
// --> 3600
If necessary, you can compute
[currentDateTimeZone secondsFromGMT] - [currentDateTimeZone daylightSavingTimeOffset]
to get the "default" GMT offset for your timezone.

Related

CMMotionActivity startDate is 1970 not 2016

My CMMotionActivity object has the property timestamp and startDate.
Printing both of these shows the value is from 1970, not 2016 as expected.
[self.motionActivityManager startActivityUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMotionActivity *activity) {
NSLog(#"startDate = %#",activity.startDate);
NSLog(#"timestamp = %f",activity.timestamp);
}];
2016-07-20 17:46:53.294 MyApp[292:30246] timestamp = 8648.852289
2016-07-20 17:46:54.229 MyApp[292:30246] startDate = 1970-01-01 02:24:09 +0000
My device's time and date is set correctly, though it has been turned off for weeks.
EDIT: Today I got this: No code changes.
2016-07-21 11:26:34.292 MyApp[229:7473] startDate = 2016-07-21 10:26:34 +0000
2016-07-21 11:26:34.293 MyApp[229:7473] timestamp = 662.524030
Eh... Close enough. "timestamp" (from NSLogItem) is a NSTimeInterval so a phone running time or something.
I guess 24hrs or a phone reboot fixes the issue.

How to offset NSDate with UTC timezone offset without hardcoded manual calculation

Imagine the current local time being 15:11 UTC. I retrieve a data set from the server showing the opening closing time of a business displayed like so:
{
close = {
day = 3;
time = 0200;
};
open = {
day = 2;
time = 1700;
};
I also receive a utc-offset property exposed like so: "utc_offset" = "-420”; which I imagine is a minute offset giving an hour offset of 7 hours which seems right considering the timezone I'm in is UTC and the business location's opening hours information I'm receiving is for a business in Los Angeles who are 7 hours behind.
How do I use this property to then be able to do any time calculations on it
I want to determine whether the current local time falls between the open and close time that bit I have figured out but the calculations come out wrong considering the time comparison is done in the local timezone when it needs to be offset before calculating against that time range.
I'm trying to avoid doing things like
Psuedocode:
NSDate.date hour componenent + (UTC_offset / 60 = -7 hours)
Update:
Here's how I'm currently checking if the business is open right now
if currentArmyTime.compare(String(openInfo.time)) != .OrderedAscending && currentArmyTime.compare(String(closeInfo.time)) != .OrderedDescending {
//The business is open right now, though this will not take into consideration the business's time zone offset.
}
Is it easier to offset the current time?
Before you can use the 'open' and 'close' times in date operations you need to create an NSDate from a calendar that has been set to the time zone for those times. Here's an example:
// Create calendar for the time zone
NSInteger timeOffsetInSeconds = -420 * 60;
NSTimeZone *tz = [NSTimeZone timeZoneForSecondsFromGMT:timeOffsetInSeconds];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
calendar.timeZone = tz;
// Create an NSDate from your source data
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = 1;
comps.month = 1;
comps.year = 2016;
comps.hour = 8;
comps.minute = 0;
NSDate *openTime = [calendar dateFromComponents:comps];
// 'openTime' can now be to compared with local time.
NSLog(#"openTime = %#", openTime); // Result is openTime = 2016-01-01 15:00:00 +0000
You should put the above code into a method that takes in the raw time and the time offset to apply.

(Obj-c) Identify morning, afternoon or night from a date string

I'm working in Objective-C. I have this three values in string, which they determine the part of the day
NSString *morning = #"7:01";
NSString *afternoon = #"12:01"
NSString *night = #"19:01"
And my original date is this, also in String
NSString *currentTime = #"Sat, 17 Oct 2015 9:58 am CDT"
I need to identify if the current date is morning, afternoon or night according with the current date as String. Anyone have a solution?
Your question has received a negative score (-4 at the time of writing) as people obviously feel you haven't shown the effort SO expects. However your question hides a gotcha and something which Apple recently made harder, which makes it interesting.
Your sample time is:
NSString *currentTime = #"Sat, 17 Oct 2015 9:58 am CDT"
which would seem to be "morning". However this is exactly the same time as:
Sat, 17 Oct 2015 2:58 pm GMT
which would seem to be "afternoon". Both these times are:
Sat, 17 Oct 2015 14:58 UTC
Why is this an issue?
An NSDate is a point in time without any associated time zone. The class NSDateFormatter, and associated methods on NSDate itself, will parse a date-time string and produce the absolute UTC point in time the string represents. Any time zone in the string, such as CDT in the example, is allowed for in determining the absolute time point but is not directly represented in the NSDate value that results.
When the NSCalendar class is used to break out the parts of a date it does so in relation to a time zone, which defaults to the system time zone.
What all this adds up to is if your app is running, say, on a computer in the UK and you follow the suggestion in the comments:
parse the time (using NSDateFormatter)
break out the hour & min to get an NSDateComponents value (using NSCalendar); and
compare the hour & min to your boundaries then your sample time will be reported as "afternoon"
Not good :-(
What you need is to parse the date-time (getting a standard UTC time point) and the time zone, you can then pass that time zone to NSCalendar and the rest is easy.
Apple makes it harder
Prior to OS X 10.9 & iOS 7 the NSDateFormatter class returned an NSCalendarDate date value, that type was a subclass of NSDate and also stored an NSTimeZone value. So parsing your sample returned both the time point "Sat, 17 Oct 2015 14:58 UTC" and the time zone "UTC-5". With that information NSCalendar could be used to break out the hour & min and correctly determine the time is "morning".
NSCalendarDate is now deprecated, and while it is still possible to use it this could change at any moment. Apple do not yet appear to have provide an alternative "parse date and time zone" method.
Parsing both the date and time zone offset
From the simple observation that if you parse "Sat, 17 Oct 2015 9:58 am CDT" ignoring the time zone and treating it as UTC the result is an absolute time point which differs by 5 hours, the time zone offset for CDT, from the one obtained if the string is parsed taking the time zone into account you have a method to obtain the time zone - parse the string twice, once ignoring the time zone, and determine the difference.
This may not be the best algorithm, but it does work... (you may insert here warnings about premature optimisation!)
So here goes (minimal comments, look the methods up in the documentation, error checking etc. - treat as an outline only):
- (BOOL) dateAndZoneFromString:(NSString *)timeString // the date-time string
dateFormat:(NSString *)dateFormat // the format of the date-time, should contain a time zone format at the end
parsedDate:(NSDate **)date // NSDate representing the absolute time point
parsedZone:(NSTimeZone **)zone // NSTimeZone representing the time zone of the original string
error:(NSError **)error
{
NSDateFormatter *df = [NSDateFormatter new];
// parse timeString taking time zone into account
df.dateFormat = dateFormat;
NSDate *absDate = [df dateFromString:timeString];
// parse timeString ignoring the time zone by removing the format specifier from dateFormat
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"UTC"];
df.dateFormat = [dateFormat stringByReplacingOccurrencesOfString:#" *[zZvV]+$" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, dateFormat.length)];
NSDate *zonelessDate;
NSRange range = NSMakeRange(0, timeString.length);
if ( [df getObjectValue:&zonelessDate forString:timeString range:&range error:error] )
{
// parse successful, calculate the difference and construct an NSTimeZone value
NSTimeInterval offset = [zonelessDate timeIntervalSinceDate:absDate];
NSTimeZone *timezone = [NSTimeZone timeZoneForSecondsFromGMT:offset];
*date = absDate;
*zone = timezone;
return YES;
}
else
return NO;
}
If you pass #"Sat, 17 Oct 2015 9:58 am CDT" and for format #"E, d MMM y h:m a z" to this method it will return the time point "Sat, 17 Oct 2015 14:58 UTC" as an NSDate and the time zone "UTC-5" as an NSTimeZone.
If you pass #"Sat, 17 Oct 2015 2:58 pm GMT" then it will return the same absolute time point and a time zone of "UTC+0".
For this point you can use these values with NSCalendar, NSDateComponents, and simple comparisons to determine morning/afternoon/night.
HTH
-(void)ShowTimeMessage
{
// For calculating the current date
NSDate *date = [NSDate date];
// Make Date Formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh a EEEE"];
// hh for hour mm for minutes and a will show you AM or PM
NSString *str = [dateFormatter stringFromDate:date];
// NSLog(#"%#", str);
// Sperate str by space i.e. you will get time and AM/PM at index 0 and 1 respectively
NSArray *array = [str componentsSeparatedByString:#" "];
// Now you can check it by 12. If < 12 means Its morning > 12 means its evening or night
NSString *message;
NSString *timeInHour;
NSString *am_pm;
NSString *DayOfWeek;
if (array.count>2)
{
// am pm case
timeInHour = array[0];
am_pm = array[1];
DayOfWeek = array[2];
}
else if (array.count>1)
{
// 24 hours case
timeInHour = array[0];
DayOfWeek = array[1];
}
if (am_pm)
{
if ([timeInHour integerValue]>=4 && [timeInHour integerValue]<=9 && [am_pm isEqualToString:#"AM"])
{
message = [NSString stringWithFormat:#"Morning"];
}
else if (([timeInHour integerValue]>=10 && [timeInHour integerValue]!=12 && [am_pm isEqualToString:#"AM"]) || (([timeInHour integerValue]<4 || [timeInHour integerValue]==12) && [am_pm isEqualToString:#"PM"]))
{
message = [NSString stringWithFormat:#"Afternoon"];
}
else if ([timeInHour integerValue]>=4 && [timeInHour integerValue]<=9 && [am_pm isEqualToString:#"PM"])
{
message = [NSString stringWithFormat:#"Evening"];
}
else if (([timeInHour integerValue]>=10 && [timeInHour integerValue]!=12 && [am_pm isEqualToString:#"PM"]) || (([timeInHour integerValue]<4 || [timeInHour integerValue]==12) && [am_pm isEqualToString:#"AM"]))
{
message = [NSString stringWithFormat:#"Night"];
}
}
else
{
if ([timeInHour integerValue]>=4 && [timeInHour integerValue]<10)
{
message = [NSString stringWithFormat:#"Morning"];
}
else if ([timeInHour integerValue]>=10 && [timeInHour integerValue]<16)
{
message = [NSString stringWithFormat:#"Afternoon"];
}
else if ([timeInHour integerValue]>=16 && [timeInHour integerValue]<22)
{
message = [NSString stringWithFormat:#"Evening"];
}
else
{
message = [NSString stringWithFormat:#"Night"];
}
}
if (DayOfWeek)
{
_timeLbl.text=[NSString stringWithFormat:#"%# %#",DayOfWeek,message];
}
}

How to show date with timezone for date, relevantly to defined country/zone?

I need to show a date in concrete time zone including DST (European time). App will be used in Lithuania, so time zone is +3 at summer and +2 at other time. The thing is, I have just a list of dates and I don't know how to show +3 for summer dates and +2 for other dates. Currently, I have time zones:
// Eastern European Summer Time UTC + 3 hours
NSTimeZone *timeZoneWithDst = [NSTimeZone timeZoneWithAbbreviation:#"EEST"];
//Eastern European Time UTC + 2 hours
NSTimeZone *timeZoneWithoutDst = [NSTimeZone timeZoneWithAbbreviation:#"EET"];
But how to loop through my list of dates and calculate should I add +3 or +2 to date?
UPDATE Finally I got it working by applying Martin R. suggestion to use time zone by name, not by abbreviation. In this way, date with this time zone handles DST automatically. Here's my code for converting dates:
NSTimeZone *TimeZone = [NSTimeZone timeZoneWithName:#"Europe/Vilnius"];
NSInteger seconds = [myTimeZone secondsFromGMTForDate:someDate];
NSDate *result = [NSDate dateWithTimeInterval:seconds sinceDate:someDate];
To convert an NSDate to a string representation, use NSDateFormatter. By default, it uses the local time zone. To display the date according to a concrete time zone, you can set
NSTimeZone *tz = [NSTimeZone timeZoneWithName:#"Europe/Vilnius"];
[dateFormatter setTimeZone:tz];
(According to http://www.timezoneconverter.com/cgi-bin/findzone, the time zone for Lithuania is "Europe/Vilnius".)
This is a very similar alternative that worked for me, in Swift:
var currentDate: NSDate {
let currentLocalTime = NSDate()
let localTimeZone = NSTimeZone.systemTimeZone()
let secondsFromGTM = NSTimeInterval.init(localTimeZone.secondsFromGMT)
let resultDate = NSDate(timeInterval: secondsFromGTM, sinceDate: currentLocalTime)
return resultDate
}

NSMutableDictionary setObject:forKey (ios 6 without ARC) results in NULL

The following code was working prior to upgrading to iOS 6. It works in the 5.1 iPhone simulator as well, but fails with 6.0 simulator and device.
Trying to setObject:forKey in a loop to an NSMutableDictionary. Have tried adding in the loop (as the following code shows) and also tried by initializing with arrays for objects and keys which results in the same failure. Another strange bit of information is that sometimes it works but fails most of the time. The object being added is a UILocalNotification and the key is an object that represents a WeekDay (more than a simple string). The output of running is shown below. The UILocalNotifications and keys are clearly not NULL but the added pair in the MutableDictionary has NULL for some of the objects most of the time. Mostly it's the last added day (key) whose object is null. I'm completely at a loss as to how this breaking, thanks in advance for any help!
copy method for WeekDay (NSCopying Protocol):
- (id)copyWithZone:(NSZone *)zone
{
WeekDay * copy = [[WeekDay alloc] initWithDay:self.day];
return copy;
}
code snippet using setObject:forKey:
NSMutableDictionary * newAlarmsDictionary = [[NSMutableDictionary alloc] init];
NSArray * theDayKeys = [[_daysEnabledDict allKeys] sortedArrayUsingSelector:#selector(compare:)];
NSMutableArray * tempNotifyArray = [[NSMutableArray alloc] init];
UILocalNotification * theAlarm = nil;
WeekDay * theWeekDay = nil;
for (int i=0; i < [theDayKeys count]; i++) {
if ([[_daysEnabledDict objectForKey:[theDayKeys objectAtIndex:i]] boolValue] == TRUE) {
theWeekDay = [theDayKeys objectAtIndex:i];
NSDate * now = [NSDate date];
... deleted lines setting up fire date for UILocalNotification, not significant to problem ...
theAlarm = [[UILocalNotification alloc] init];
theAlarm.fireDate = itemDate;
theAlarm.repeatInterval = NSWeekCalendarUnit;
theAlarm.timeZone = [NSTimeZone localTimeZone];
theAlarm.soundName = UILocalNotificationDefaultSoundName;
theAlarm.applicationIconBadgeNumber = 0;
[newAlarmsDictionary setObject:theAlarm forKey:theWeekDay];
[tempNotifyArray addObject:theAlarm];
[theAlarm release];
}
}
}
NSLog(#"--Debug: tempNotifyArray---- %# -------------", tempNotifyArray);
NSLog(#"--Debug: newAlarmsDictionary ====== %# =============", newAlarmsDictionary);
Here is the output of the two NSlog statements at the end of the code snippet. This particular run adds 4 notifications, for days wed thru sat. The 'alarms' put into the tempNotifyArray are valid but when added to the dictionary (one in this case) is null.
2012-11-26 11:07:01.087 MedTrack[9728:11303] --Debug: tempNotifyArray---- (
"<UIConcreteLocalNotification: 0x7277940>{fire date = Wednesday, November 28, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Wednesday, November 28, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}",
"<UIConcreteLocalNotification: 0x8883280>{fire date = Thursday, November 29, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Thursday, November 29, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}",
"<UIConcreteLocalNotification: 0x75c6590>{fire date = Friday, November 30, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Friday, November 30, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}",
"<UIConcreteLocalNotification: 0x75c83e0>{fire date = Saturday, December 1, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Saturday, December 1, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}"
) -------------
2012-11-26 11:07:01.097 MedTrack[9728:11303] --Debug: newAlarmsDictionary ====== {
"[WeekDay] 6 (Sat)" = (null);
"[WeekDay] 3 (Wed)" = "<UIConcreteLocalNotification: 0x7277940>{fire date = Wednesday, November 28, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Wednesday, November 28, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}";
"[WeekDay] 4 (Thu)" = "<UIConcreteLocalNotification: 0x8883280>{fire date = Thursday, November 29, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Thursday, November 29, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}";
"[WeekDay] 5 (Fri)" = "<UIConcreteLocalNotification: 0x75c6590>{fire date = Friday, November 30, 2012, 11:06:00 AM Eastern Standard Time, time zone = America/Toronto (EST) offset -18000, repeat interval = NSWeekCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Friday, November 30, 2012, 11:06:00 AM Eastern Standard Time, user info = {\n Temp = Fred;\n}}";
The issue here is that you implement -copyWithZone:, but you fail to implement -isEqual:. Without knowing the full structure of your object, I cannot answer how that should be implemented, but here's a good basis:
- (BOOL)isEqual:(id)otherObject;
{
if ([otherObject isKindOfClass:[self class]]) {
WeekDay *otherWeekDay= (WeekDay *)otherObject;
if (self.day != [otherWeekDay day]) return NO;
if (self.name != [otherWeekDay name]) return NO;
return YES;
}
return NO;
}
- (NSUInteger) hash;
{
return self.day ^ [self.name hash];
}

Resources