creating formatted NSDate using NSDateFormatter or mktime - ios

I'm just trying to create a NSDate object with string as:2011-02-10 4:30:45
Take a look at the code:
Approach OBJC:
-(NSDate *)get_date_df:(NSString *)dstr
{
NSLog(#"1> %#",dstr);
NSDateFormatter *df_in = [[[NSDateFormatter alloc] init]autorelease];
[df_in setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *d = [df_in dateFromString:dstr];
NSLog(#"2> %#",d);
NSLog(#"3> %#",[df_in stringFromDate:d]);
return d;
}
And here's the output:
1> 2011-02-10 4:30:45
2> 2011-02-09 23:00:45 +0000
3> 2011-02-10 04:30:45
So why is 2> getting printed wrong?
Suppose at 2011-02-10 4:20:45, I try setting up a LocalNotification with that date, it fires instantly because the processor assumes the time is already expired!
UILocalNotification *lnf = [[UILocalNotification alloc] init];
[lnf setFireDate:[self get_date_df:#"2011-02-10 4:30:45"]];
[lnf setAlertBody:#"WTF"];
[[UIApplication sharedApplication]scheduleLocalNotification:lnf];
[lnf release];
And, I've already tried setting up various TimeZones and NSLocale for en_US, en_FR. No help.
Approach C:
And FYI now I'm switching to C, trying something with:
-(NSDate *)get_date_c:(const char *)date_str
{
char date[10][6] = {0};
sscanf(date_str,"%[0-9] - %[0-9] - %[0-9] %[0-9] : %[0-9] : %[0-9]",date[0],date[1],date[2],date[3],date[4],date[5]);
struct tm *t_info = (struct tm *)malloc(sizeof(struct tm));
t_info->tm_sec = atoi(date[5]);
t_info->tm_min = atoi(date[4]);
t_info->tm_hour = atoi(date[3]);
t_info->tm_mday = atoi(date[2]);
t_info->tm_mon = atoi(date[1]);
t_info->tm_year = atoi(date[0]);
NSLog(#"ans: %f",(double)mktime(t_info));
NSDate *d = [NSDate dateWithTimeIntervalSince1970:mktime(t_info)];
free(t_info);
return d;
}
Output:
ans: -1.000000
There again I'm stuck with converting time_t to NSTimeInterval.
So, thanks in advance if you could help me out with any approach :(

In your input string and format yyyy-MM-dd HH:mm:ss you didn't specify the timezone. So it defaults to UTC.

Related

What is the proper way to convert date object with separate timezone into NSDate

My app ingests data from a web service (PHP) which provides dates in this format:
endDate = {
date = "2020-09-30 16:16:08.000000";
timezone = "-04:00";
"timezone_type" = 1;
};
This is the code I have been using to convert to NSDate, and it works as far as I can tell, in every test, but it fails on a few devices according to user reports and debug logs.
Note that the correct conversion of this date determines if content is unlocked in the app, so when it fails, customers contact us about it.
NSDictionary* dateDict = [responseDict objectForKey:#"endDate"];
NSString* strEndDate = [dateDict objectForKey:#"date"];
NSString* strOffset = [dateDict objectForKey:#"timezone"];
NSTimeInterval zoneSeconds = 0;
NSRange rng = [strOffset rangeOfString:#":"];
if (rng.location != NSNotFound && rng.location >= 1)
{
NSString* hoursOnly = [strOffset substringToIndex:rng.location];
NSInteger offsetValue = [hoursOnly integerValue];
zoneSeconds = (3600 * offsetValue);
}
NSDateFormatter* df = [[NSDateFormatter alloc] init];
NSTimeZone *timeZone = [NSTimeZone timeZoneForSecondsFromGMT:zoneSeconds];
[df setTimeZone:timeZone];
[df setDateFormat:#"yyyy-MM-dd HH:mm:ss.000000"];
NSDate* newEndDate = [df dateFromString:strEndDate];
However, debug logs from a few users show that the dateFromString call is failing and returning nil.
We have one user who has 2 iOS devices, and using the same account (same date) the app performs as expected on one of them, but fails on the other. Same Apple ID, both running iOS12. Debug logs show both devices received the same date from the server, yet one of them failed to convert the date from a string to NSDate.
My assumption so far is that there is some setting or configuration on the device(s) where this fails that is different. But I have fiddled with calendar and date settings all day, and cannot get this to fail. I know the user in question has both devices configured to the same time zone.
Is there a better, more correct way to do this date conversion which might be more robust?
When using an arbitrary date format it's highly recommended to set the locale of the date formatter to the fixed value en_US_POSIX.
Rather than calculating the seconds from GMT it might be more efficient to strip the milliseconds with regular expression, append the string time zone and use an appropriate date format.
This code uses more contemporary syntax to set date formatter properties with dot notation and dictionary literal key subscription
NSDictionary *dateDict = responseDict[#"endDate"];
NSString *strEndDate = dateDict[#"date"];
NSString *strTimeZone = dateDict[#"timezone"];
NSString *dateWithoutMilliseconds = [strEndDate stringByReplacingOccurrencesOfString:#"\\.\\d+" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, strEndDate.length)];
NSString *dateWithTimeZone = [NSString stringWithFormat:#"%#%#", dateWithoutMilliseconds, strTimeZone];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.locale = [NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"];
df.dateFormat = #"yyyy-MM-dd HH:mm:ssZZZZZ"];
NSDate *newEndDate = [df dateFromString:dateWithTimeZone];
The question was actually similar to (What is the best way to deal with the NSDateFormatter locale "feechur"?) as was suggested originally, but it was this other question (NSDateFormatter fails to return a datetime for UK region with 12 hour clock set) which really made it click for me - its the UK region with the 12hour clock which causes the code to fail, but the dateFormatter was easily fixed by simply setting the locale to "un_US_POSIX" as suggested in the answer to that question (it was also suggested below by vadian - I did not try his code however). Thank you to everyone who contributed hints and leads!

Formatting a month string is not working

I'm trying to create month strings that look like "Jan", "Feb", "Mar"... Here is my code:
- (NSString *)getMonthNameString:(int)monthNumber {
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"MMM"];
NSArray *monthNames = [formatter standaloneMonthSymbols];
NSString *monthName;
if (monthNumber > 0) {
return monthNames[monthNumber - 1];
}
return monthNames[1];
}
So if the month number is 1, I'm expecting the code to provide month name as "Jan" and if it is 2, it has to provide month name as "Feb" and so on. But the problem is that even though I have set the format as MMM, it is still creating month names of type "January", "February" etc instead of "Jan","Feb" etc. How do I sort this out?
Try:
-(NSString*)getMonthNameString:(int)monthNumber
{
NSDateFormatter *formate = [NSDateFormatter new];
[formate setDateFormat:#"MMM"];
NSArray *monthNames = [formate shortMonthSymbols];
NSString *monthName;
if (monthNumber > 0)
{
monthName = [monthNames objectAtIndex:(monthNumber - 1)];
}
return monthName;
}
That's usually not what a NSDateFormatter is for - it is for converting real dates, and not just month numbers.
If you want to stick with it, I suggest
-(NSString*)getMonthNameString:(int)monthNumber {
NSDateFormatter *formatter = [NSDateFormatter new];
return formatter.shortMonthSymbols[monthNumber-1];
}
I see no benefit in the extra check for > 0. This just masks programming errors. You might want to add an assertion to catch that during development. (Why should an invalid number return January anyway?)
Creating formatters is expensive, though - you might want to reuse the same instance over and over again.
Or just access an array directly, i.e.
-(NSString*)getMonthNameString:(int)monthNumber {
return #[#"Jan", #"Feb", ...][monthNumber-1]; // write up to December of course
}
standaloneMonthSymbols is a property of NSDateFormatter. I don't think it uses the dateFormat you've set. Try using shortStandaloneMonthSymbols property instead (or veryShortStandaloneMonthSymbols if you just need one letter symbol).
tahavath is right. Specifically, you want to use the shortStandaloneMonthSymbols property to get it to print "Jan" or "Feb" etc.
Try the following:
-(NSString*)getMonthNameString:(int)monthNumber
{
NSDateFormatter *formate = [NSDateFormatter new];
[formate setDateStyle:NSDateFormatterMediumStyle];
[formate setDateFormat:#"MMM"];
NSArray *monthNames = [formate standaloneMonthSymbols];
NSString *monthName;
if (monthNumber > 0)
{
monthName = [monthNames objectAtIndex:(monthNumber - 1)];
}
return monthName;
}
Check your code #Karuppu MGR
-(NSString*)getMonthNameString:(int)monthNumber
{
NSDateFormatter *formate = [NSDateFormatter new];
[formate setDateFormat:#"MMM"];
NSArray *monthNames = [formate standaloneMonthSymbols];
NSString *monthName;
if (monthNumber > 0 && monthNumber<13)
{
monthName = [monthNames objectAtIndex:(monthNumber - 1)];
// your process is right but , here you have attached "return value " so every time return the monthNames array value.
}
return monthName; // if you pass zero or greathan twelve monthName return nil value
}

Load an image on specific date and time in iOS [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
My question is: how do I load a specific image on a specific date and time in iOS?
I have searched the net but did not find anything useful.
I have a list of images in an imageArray and want every image to be shown on a specific date, time and order.
Fx. say I want to load MyImage on MyDateAndTime. How can I do this?
Image 1 - DateAndTime 1
Image 2 - DateAndTime 2
Image 3 - DateAndTime 3
Any suggestions is appreciated, please provide some source code if possible.
I put simple logic, edit it as per your requirement otherwise if you have any query related to my answer then please tells to me.
Best way is store your image with Name of dateTime (dd_MM_yyyy_HH_mm_ss) and access image name such like,
NSString *imageName
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd_MM_yyyy_HH_mm_ss"];
imageName = [NSString stringWithFormat:#"%#.png", [formatter stringFromDate:[NSDate date]]]; // here you can set specific dateTime, i putted current dateTime
Here you get imageName such like 19_10_2013_6_13_21.png
And by this image name you can get image from bundle or document directory.
If all you want is to show a different image every minute, use this, otherwise skip below to see helpful date information.
NSTimeInterval secondsInMinute = 60;
[NSTimer timerWithTimeInterval:secondsInMinute target:self selector:#selector(minuteChanged:) userInfo:nil repeats:YES];
- (void)minuteChanged:(id)sender {
// change image here
}
You question could have many different answers, do you want to create this date dynamically? or is it a a predefined date? One solution is to get get the timeInterval of the date you are looking for.
NSDate* rightNow = [NSDate date];
NSTimeInterval timeInterval = [rightNow timeIntervalSince1970];
// since time intervals are in seconds we can just append the
// date as easily as adding time
NSInteger secondsInMinute = 60;
NSInteger minutesInHour = 60;
NSInteger hoursInDay = 24;
NSInteger daysInWeek = 7;
NSInteger secondsInWeek = secondsInMinute * minutesInHour * hoursInDay * daysInWeek;
timeInterval = timeInterval + secondsInWeek;
NSDate* aWeekInFuture = [NSDate dateWithTimeIntervalSince1970:timeInterval];
that i would say is the easiest to under stand to set a date, but you could also use components to set a future date dynamically. This leads into some problems but here is how it's done.
NSDate* rightNow = [NSDate date];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* dateCompenents = [calendar components:(NSDayCalendarUnit | NSWeekCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:rightNow];
[dateCompenents setDay:dateCompenents.day + 7];
NSDate* aWeekInFuture = [calendar dateFromComponents:dateCompenents];
to help explain this, here is some console logs
(lldb) po rightNow
$0 = 0x0b933440 2013-10-19 12:43:55 +0000
(lldb) po aWeekInFuture
$1 = 0x0ba32a60 2013-10-26 04:00:00 +0000
you see how the date is accurate for the day, year, month, but look at the exact time, the current time (right now) is 12:43:55 but the week in he future is 4:00:00 this is because i did not ask for the NSMinutesCalendarUnit, NSHoursCalendarUnit, NSSecondsCalendarUnit... so if i wanted a perfect date that would be inadequate unless i ask for every single thing, but you specifically may not need to be so accurate in fact you may even want to set your own time.
Now if you want a static date, a date the user enters, you will need to use NSDateFormatter example below
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setCalendar:currentCalendar];
[dateFormatter setDateFormat:#"mm/dd/yyyy"];
NSDate* birthdayDate = [dateFormatter dateFromString:#"10/05/2013"];
Now you wanted to know how would you know if today is the specified date that is saved. Lets say you stored the date in NSUserDefaults or on a server or some place, the easiest way to compare the dates is with the compare function of an NSDate
NSDate* rightNow = [NSDate date];
NSDate* storedDate = [[NSUserDefaults standardUserDefaults] objectForKey#"storedDate"] // some date from server, or UserDefaults
NSComparisonResult = [rightNow compare:storedDate];
this is a bit inadequate since it test for perfection but it will return values of NSOrderedSame if they are equal, NSOrderedDescending if storedDate is behind rightNow, and NSOrderedAscending if storedDate is in front of rightNow. This is all specific down to the time interval. If you just want a generic day, you will have to test it via components
NSDate* rightNow = [NSDate date];
NSDate* birthdayDate = [[NSUserDefaults standardUserDefaults] objectForKey#"birthday"]
NSDateComponents* todayComponents = [currentCalendar components:(NSDayCalendarUnit | NSMonthCalendarUnit) fromDate:rightNow];
NSDateComponents* birthdayComponents = [currentCalendar components:(NSDayCalendarUnit | NSMonthCalendarUnit) fromDate:birthdayDate];
BOOL dayIsTheSame = ( todayComponents.day == birthdayComponents.day );
BOOL monthIsTheSame = ( todayComponents.month == birthdayComponents.month );
BOOL todayIsBirthday = ( dayIsTheSame && monthIsTheSame );
if (todayIsBirthday) {
[self.imgViewBirthday setImage[UIImage imageNamed:#"cake.png"]];
}
In your question you specified an array of images, lets say you have a different image depending on which hour it is, or which minute, you would use the component, todayComponent.minute after asking for the NSMinutesCalendarUnit as the index of this array;
UIImage* currentImageToDisplay = [self.arrayOfImage objectAtIndex:todayComponent.minute];
self.imageView.image = currentImageToDisplay;
References:
NSDate,
NSDateFormatter,
NSDateComponents,
NSCalendar,
NSTimer
If I understood your problem now, one fancy approach using a recursive block you might check out is this:
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray* dates = #[#1, #1, #1, #1, #1, #1, #1, #1, #1];
NSArray* urls = #[#"A", #"B", #"C", #"D", #"E", #"F", #"G", #"H", #"I"];
NSEnumerator* dateIter = [dates objectEnumerator];
NSEnumerator* urlIter = [urls objectEnumerator];
typedef void(^block_t)(NSEnumerator* dateIter, NSEnumerator* urlIter);
block_t asyncFunc;
__block __weak block_t _asyncFunc = asyncFunc = ^(NSEnumerator* dateIter, NSEnumerator* urlIter) {
NSNumber* date = [dateIter nextObject];
NSString* url = [urlIter nextObject];
if (date != nil && url != nil) {
double delayInSeconds = [date doubleValue];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(0, 0), ^(void){
NSLog(#"%#", url);
_asyncFunc(dateIter, urlIter);
});
}
else {
printf("\n");
return;
}
};
// start:
asyncFunc(dateIter, urlIter);
sleep (10);
}
return 0;
}
Note:
The "dates" are actually "delays" and the URLs are actually just strings in this example. You should be able to adjust this as you like. Of course, NSLog(#"%#", url); would actually display your image.
Also, the block asyncFunc is asynchronous!

NSDate compare always returns NSOrderedSame

I have a problem with NSDate comparison. I tried out several solutions that I found while googling my problem but nothing changes: I compare two dates and I always get NSOrderedSame or difference between NSTimeIntervals = 0.0000;
My code looks like this:
NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
[inputFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
for (checklisteModel *updateRecord in updateArray)
{
NSDate *serverDate = [inputFormatter dateFromString:updateRecord.last_update];
NSString *lastUpdateFromLocalRecord = [self getLastUpdateForChecklisteItemWithID:updateRecord.checklisteID];
NSDate *localDate = [inputFormatter dateFromString:lastUpdateFromLocalRecord];
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
NSLog(#"checkliste ID: %i",updateRecord.checklisteID);
NSLog(#"server Date: ----- %#",[fmt stringFromDate:serverDate]);
NSLog(#"local Date: ----- %#",lastUpdateFromLocalRecord);
NSLog(#"difference of dates: %f",[serverDate timeIntervalSinceDate:localDate]);
NSTimeInterval distanceBetweenDates = [serverDate timeIntervalSinceDate:localDate];
double secondsInMinute = 60;
NSInteger secondsBetweenDates = distanceBetweenDates / secondsInMinute;
if (secondsBetweenDates == 0)
NSLog(#"seconds between == 0");
else if (secondsBetweenDates < 0)
NSLog(#"seconds between < 0");
else
NSLog(#"seconds between > 0");
//last_update on server is earlier than local
if ([serverDate compare:localDate] == NSOrderedAscending)
{
NSLog(#"Server data is earlier than local data");
NSLog([NSString stringWithFormat:#"server data: %#, local data: %#",updateRecord.last_update,lastUpdateFromLocalRecord]);
}
// server data is later than local data
else if ([serverDate compare:localDate] == NSOrderedDescending)
{
NSLog(#"Server data is later than local data");
NSLog([NSString stringWithFormat:#"server data: %#, local data: %#",updateRecord.last_update,lastUpdateFromLocalRecord]);
}
else
{
NSLog(#"Server data and local data are the same");
}
}
And part of my debugging shows this:
checkliste ID: 21
server Date: -----
local Date: ----- 2012-10-29 10:13:46
difference of dates: 3810.000000
seconds between > 0
Server data is later than local data
server data: 2012-10-29 11:17:16, local data: 2012-10-29 10:13:46
NSLog(#"server Date: ----- %#",updateRecord.last_update);
You're not logging serverDate here, so as Hot Licks suggested, there's a good chance that it's nil. You can send messages to nil in Objective-C, and the result is 0 or nil. What do you think the value of NSOrderedSame is? I'll bet a dollar that it's 0.
[inputFormatter setDateFormat:#"yyyy-MM-dd HH:mm"];
I notice that your date formatter's input format doesn't exactly match what you're logging, so maybe the difference between the expected input and what you're providing is enough to prevent the formatter from creating a date. Try adding seconds to the date format for inputFormatter.

IOS 6 NSDateFormatter

Please help me with the dateformatter on IOS6, please see the code below
NSString stringDate = #"12/31/9999";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"MM/dd/yyyy"];
NSDate *dateCheck = [dateFormatter dateFromString:stringDate];
NSLog(#"Date = %#", dateCheck);
Output is
Date = 1999-12-31 08:00:00 +0000
This was the output when converting the string date to date 12/31/9999.
From the previous version of IOS6 the output is
Date = 9999-12-31 08:00:00 +0000 // Correct
I made a fix for this for my company's enterprise applications.
It should fix this issue for date formatters using a known format string (like the ones we use to parse dates from our sqlite database).
However, it will not fix:
NSDateFormatters that have isLenient set to true.
NSDateFormatters that use a style, instead of a format string, for parsing.
It does not seem to cause negative side effects on iOS 5 or 5.1. I have not tested anything earlier than that. However, I do mess with the internals of NSDateFormatter a bit, so this may not pass the App Store submission process. However, if you write programs under the Enterprise program (or just use ad hoc deployment), this shouldn't be a problem. Also, it will try to get out of the way if you have isLenient on, but there are no guarantees that you won't run into any issues.
I would like to stress that this is a Temporary Solution. I have not tested this in all possible situations, so you should implement this at your own risk.
I created the following category:
NSDateFormatter+HotFix.h
#import <Foundation/Foundation.h>
#interface NSDateFormatter (HotFix)
- (NSDate*)dateFromString:(NSString *)string;
#end
NSDateFormatter+HotFix.m
#import "NSDateFormatter+HotFix.h"
#import <objc/runtime.h>
#implementation NSDateFormatter (HotFix)
- (NSDate*)dateFromString:(NSString *)string
{
if (!string) return nil;
//HACK: Use the original implementation
void* baseFormatter = nil;
object_getInstanceVariable(self, "_formatter", &baseFormatter);
if (!baseFormatter) return nil;
//Use the underlying CFDateFormatter to parse the string
CFDateRef rawDate = CFDateFormatterCreateDateFromString(kCFAllocatorDefault, (CFDateFormatterRef)baseFormatter, (CFStringRef)string, NULL);
NSDate* source = (NSDate*)rawDate;
//We do not support lenient parsing of dates (or styles), period.
if (source && !self.isLenient && self.dateStyle == NSDateFormatterNoStyle && self.timeStyle == NSDateFormatterNoStyle)
{
//If it worked, then find out if the format string included a year (any cluster of 1 to 5 y characters)
NSString* format = [self dateFormat];
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:#"y{1,5}" options:NSRegularExpressionCaseInsensitive error:NULL];
NSArray* matches = [regex matchesInString:format options:0 range:NSMakeRange(0, [format length])];
if ([matches count] > 0)
{
for (NSTextCheckingResult* result in matches)
{
//Check for the y grouping being contained within quotes. If so, ignore it
if (result.range.location > 0 && result.range.location + result.range.length < [format length] - 1)
{
if ([format characterAtIndex:result.range.location - 1] == '\'' &&
[format characterAtIndex:result.range.location + result.range.length + 1] == '\'') continue;
}
NSString* possibleYearString = [string substringWithRange:result.range];
NSInteger possibleYear = [possibleYearString integerValue];
if (possibleYear > 3500)
{
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* dateComp = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:source];
dateComp.year = possibleYear;
return [calendar dateFromComponents:dateComp];
}
}
}
}
return [source autorelease];
}
#end
It will replace the existing dateFromString method of NSDateFormatter. It works by trying to parse the string normally, then checking to see if the formatString has a set of year formatting characters inside it. If it does, it manually pulls the year out and checks if it is greater than 3500. Finally, if this is the case, it rewrites the output to have the correctly parsed year.
Simply include it in your project and it will take effect. You do not need to import the header into every file that uses a NSDateFormatter, just having the .m compiled in will modify the class. If you have any other categories that change dateFromString: then the effects of this class cannot be defined.
I hope this helps.

Resources