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];
}
Related
I am calling a legacy service that returns all times in a string format assuming that the timezone is EST. How do I create a DateTime instance and specify that the timezone is EST? When I display the times to the user's I will translate to their device timezone.
I wrote a package for this. It's called Instant, and it can fetch the latest DateTime in any given zone worldwide. Take a detailed look at https://aditya-kishore.gitbook.io/instant/
The basic usage to get a DateTime in a timezone is fairly simple.
Note: I assume below that your legacy service delivers a String with format HH:MM in 24 hr time, and that it doesn't deliver a date. If any of the following is untrue, lemme know and I'll change the code sample.
List<int> _hourmin (String parse) {
List<int> timeinint = [];
if (parse.length == 5) {
//if parse is something like 10:00
timeinint.add(int.parse(parse.substring(0,2))); //Add hours (10)
timeinint.add(int.parse(parse.substring(3,5))); //Add minutes (0)
} //hours >= 10
if (parse.length == 4) {
//if parse is something like 4:06
timeinint.add(int.parse(parse.substring(0,1))); //Add hours (4)
timeinint.add(int.parse(parse.substring(2,4))); //Add minutes (6)
} //hours < 10
return timeinint;
}
DateTime utcDateTimeFromZone(
{#required String zone,
int year = 0,
int month = 0,
int day = 0,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0}) {
hour = (hour - timeZoneOffsets[zone].truncate()) % 24; //Convert to UTC from timezone
if (hour<0) {
hour = 24 + hour;
}
minute = (minute - ((timeZoneOffsets[zone]%1)*60).round()) % 60;
if (minute<0) {
minute = 60 + minute;
}
//Convert to UTC from timezone
DateTime inUTC = DateTime.utc(year, month, day, hour, minute, second, millisecond, microsecond); //Initialize in UTC time
return inUTC;
}
//We get an array containing the hours minutes
List<int> time = _hourmin("4:25"); //put your API call as the param here
//This is what you need
DateTime finished = utcDateTimeFromZone(zone: "EST", hour: time[0], minute: time[1]);
print(formatTime(time: finished.toLocal())); //1:25 for me, because I'm PST, but this works with any local timezone
Hope that clears it up!
The included DateTime class only supports the local timezone or UTC but no specific timezone.
https://pub.dartlang.org/packages/timezone would provide that but it looks like it needs an update for Dart 2.0.0
https://pub.dartlang.org/packages/time_machine is a newer one.
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.
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];
}
}
on click I want select/deselect random multiple dates. user should be able to select multiple dates (Ex: I should be able to select 2 Dec 2014, 8 Dec 2014 and 18 dec 2014. all these three dates should look selected.). Current I m using DLSCalendar. The DLSCalendar supports single date selection and range selection.
any others lib that supports the above mention functionality is welcome
Thanks.
Changes in DSLCalendarView related to select/deselect random multiple dates.
Ex: I should be able to select 2 Dec 2014, 8 Dec 2014 and 18 dec 2014. all these three dates should look selected.
Thanks :)
Please be more precise in what you want to accomplish.
If you are trying to create random dates in objective-c you can do the following:
// Objective-c Random date picker
//SET Day integers
int lowerday = 1;
int upperday = 31;
//SET Month integers
int lowermonth = 1;
int uppermonth = 12;
//SET Year integers
int loweryear = 1;
int upperyear = 2025; //Or any random year, but always higher then loweryear!
//Generate random Date
int monthvalue = lowermonth + arc4random() % (uppermonth - lowermonth); //Month
int yearvalue = loweryear + arc4random() % (upperyear - loweryear ); //Year
//Days differ each month so we should account for that
NSArray *highmonth =#[#"1",#"3",#"5",#"7",#"8",#"10",#"12"]; //Months with 31 days
NSArray *lowmonth =#[#"4",#"6",#"9",#"11"]; //Months with 30 days
NSString *secondmonth =#"2";
NSString *random_month =[NSString stringWithFormat:#"%d",monthvalue];
int dayvalue;
if ([highmonth containsObject:random_month]) {
//Do nothing the upperday is correctly set
}else if([lowmonth containsObject:random_month]){
upperday=30;
}else if([random_month isEqualToString:secondmonth]){
//To options 28 or 29 days
if (fmod(monthvalue, 1.0) == 0.0) {
//29 Days
upperday=29;
}else{
//28 Days
upperday=28;
}
}
dayvalue = lowerday + arc4random() % (upperday - lowerday ); //Day
//Create NSDate and give our random numbers
NSString *dateString = [NSString stringWithFormat:#"%d-%d-%d",dayvalue,monthvalue,yearvalue];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy"];
NSDate *dateFromString=[dateFormatter dateFromString:dateString];
//Check Random Date
if (dateFromString==nil) {
NSLog(#"Could not create random date");
}else{
NSLog(#"Succes! The Date was: %#",dateFromString);
}
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.