NSDateFormatter same string for different dates - ios

Can someone explain why NSDateFormatter return same strings from different dates:
NSDateFormatter *f = [NSDateFormatter new];
f.dateFormat = #"MM/dd/yyyy";
NSDate *dateYear0 = [[NSCalendar currentCalendar] dateWithEra:0 year:0 month:3 day:31 hour:0 minute:0 second:0 nanosecond:0];
NSDate *dateYear1 = [[NSCalendar currentCalendar] dateWithEra:0 year:1 month:3 day:31 hour:0 minute:0 second:0 nanosecond:0];
NSString *dateStrYear0 = [f stringFromDate:dateYear0];
NSString *dateStrYear1 = [f stringFromDate:dateYear1];
NSLog(#"dateYear0=%# \t\t dateStrYear0=%#",dateYear0,dateStrYear0);
NSLog(#"dateYear1=%# \t\t dateStrYear1=%#",dateYear1,dateStrYear1);
Both dateStr1 and dateStr2 have the same values:
NSLog result:
dateYear0=0001-03-30 21:57:56 +0000 dateStrYear0=03/31/0001
dateYear1=0000-03-30 21:57:56 +0000 dateStrYear1=03/31/0001
Its looks like 0 year affect this somehow?
Thanks

That's because year 0 doesn't exist in Gregorian Calendar. When dealing with AD dates minimum possible value for NSDate is "0001-01-01 00:00:00 +0000" therefore it uses "0001" as year whenever a lower value used than 1. You can check minimum NSDate value with distantPast class method.
Also if you want to use BC dates you should check Apple's guideline for more information.

Related

Format date to secounds

Background: I have dates stored in files.
What I would like to do: I would like to take the difference between two dates in seconds. I can't find any way to do it. My date format looks like that:
2015-23-02-12-23-43
Try this out:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-mm-dd-hh-mm-ss"];
NSDate *date = [dateFormatter dateFromString:string1];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
NSInteger hour = [components hour];
NSInteger minute = [components minute];
Try this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *date = [dateFormatter dateFromString:EnterYourStringHere];
NSString *str = [dateFormatter stringFromDate:date];
If all you need is seconds then use
NSTimeInterval dateInterval = [date timeIntervalSince1970];
Then
NSString secondsString = [NSString stringWithFormat: #"%.0f", dateInterval];
EDIT:
If you have a file full of date strings that look like your example, 2015-23-02-12-23, then you could use code like this:
NSDateFormatter *myFormatter = [[NSDateFormatter alloc] init];
[myFormatter setDateFormat:#"yyyy-mm-dd-hh-mm-ss"];
//an example - you'll read from your file
NSString *aDateString = #"2015-23-02-12-23";
NSDate aDate = [myFormatter dateFromString: aDateString];
NSTimeInterval dateSeconds = [aDate timeIntervalSince1970];
That will give you dateSeconds as a double precision floating point number, which is the norm for numeric date calculations since it deals with fractions of seconds. You can then do numeric comparisons of date's time intervals.
Note that most UNIX systems use the numeric values returned by the timeIntervalSince1970 method, but Mac and iOS uses numeric date values returned by the method timeIntervalSinceReferenceDate. The two methods have different "epoch dates", or dates where they start counting from zero.
timeIntervalSince1970 uses midnight on Jan 1, 1970 (GMT)
timeIntervalSinceReferenceDate uses midnight on Jan 1, 2001 (GMT)
Which you use is up to you, just be sure to use the same method in all cases.
By the way, your date string format is horrible. having all those 2 digit numbers separated by dashes doesn't give the reader any way to tell apart month/day/hours/minutes/seconds. It's all a jumble. You'd be much better off using a standard date format.
In the US it's common to display dates in the form mm/dd/yyyyy (or yyyy/mm/dd, or even yyyy/dd/mm), and times as hh:mm:ss, so in "mm/dd/yyyyy hh:mm:ss" format you'd get:"09/02/2015 13:09:39" (I'm using human-readable date format strings for discussion, not those intended to set up a date formatter.)

Compare Date object?

I have this date object from Parse, "2015-07-24 20:36:38 +0000" and I would like to compare it to today's date to see how much time has passed since the date on the date object. Any ideas how to do this?
I have this method I use to get the hour and minute, but I'm stuck on how to use this to compare dates outside of the current day.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm"];
NSString *formattedTime = [dateFormatter stringFromDate: date];
You can use an NSTimeInterval for this, the code is:
NSTimeInterval timeInterval = [date timeIntervalSinceNow];
with date being the date from parse as an NSDate, not NSString. and there should be no need to format it before comparing. Hope this helps
If you want this formatted as a string, in iOS 8 and later you can use NSDateComponentsFormatter:
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitHour;
NSString *string = [formatter stringFromDate:date toDate:[NSDate date]];

NSDate returning different value on conversion from string - Not GMT related

I hope this isn't GMT related or I will feel rather stupid.
2 quick related questions. Why is this converting to a different date? Is losing a day. I looked at time zones as stated in other answers but it is always the GMT timezone 0000 which is what I expected. I presume the error is in my setDateFormat but I can't see how to fix it.
NSString *stringFromDate = self.dateLabel.currentTitle;
NSLog(#"StringFromDateWeight! %#", stringFromDate);
NSDateFormatter *df = [[NSDateFormatter alloc] init];
//Convert Back to NSDate
[df setDateFormat:#"ddMMyyyy"];
NSDate *inputedDate = [df dateFromString: stringFromDate];
NSLog(#"StringFromDateWeight2! %#", inputedDate);
NSLog(#"StringFromDateWeight! %#", stringFromDate); is
17072013
NSLog(#"StringFromDateWeight2! %#", inputedDate); is
2013-07-16 23:00:00 +0000
I am also using the code below to compare 2 dates and am I right in that it returns in seconds? How would I change it to return in days?
int intervall = (int) [theDate timeIntervalSinceDate: now];
If you don't explicitly set a timezone NSDateFormatter will use your local timezone. You don't set one, so your formatter will create a NSDate that is at "midnight July 17" in your timezone. The description method of NSDate will return a date that is formatted in UTC timezone. Since you get "July 16 23:00:00" I guess your timezone is UTC+1.
You have two options. Calculate in UTC by setting the timezone explicitly.
df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
Or, usually more useful, don't look at the description of NSDate and use [inputedDate descriptionWithLocale:[NSLocale currentLocale]] for debugging, which will print the date formatted for your timezone.
If you want to display the date to the user use another NSDateFormatter (preferably with dateStyle and timeStyle and not dateFormat, because hardcoded dateFormats are evil)
It's just the display that is different, the underlying NSDate object is still the same.
Regarding your second question:
In many timezones there are 2 days each year that don't have 24 hours, so you can't calculate anything with the seconds you get from timeIntervalSinceDate:.
You have to use NSDateComponents and NSCalendar. Fortunately there is already a method that does exactly what you want. components:fromDate:toDate:options:
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:inputedDate toDate:[NSDate date] options:0];
NSLog(#"Date was %d days (and %d hours, %d minutes and %d seconds) ago", components.day, components.hour, components.minute, components.second);
If you only need the number of days you can remove all components except NSDayCalendarUnit

Number of days between two NSDate dates with time zone

I calculate number of days between two dates:
NSDateComponents *datesDiff = [calendar components: NSDayCalendarUnit
fromDate: someDate
toDate: currentDate
options: 0];
But this method has one disadvantage - it doesn't take in account time zone.
So, for example, if I'm in +2GMT and local time is 1:00AM, current date is yesterday.
How to compare dates in specified time zone (without 'hacking')?
PS: Preventing answers with calculation of time difference, I need difference of actual days:
yesterday 23:00 vs. today 1:00 - 1 day
yesterday 1:00 vs. today 23:00 - 1 day
today 1:00 vs. today 23:00 - 0 days
(all this in current time zone)
I don't know if it meets your criteria of not being hacky, but a fairly simple way seems to be defining a GMT adjusted date something like this:
NSDate *newDate = [oldDate dateByAddingTimeInterval:(-[[NSTimeZone localTimeZone] secondsFromGMT])
See this question for more details.
Why don't you configure the dateformatter to default all dates to GMT time and then compare.
NSDate *now = [NSDate date];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"GMT"]; // Sets to GMT time.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setCalendar:gregorianCalendar];
[formatter setTimeZone:timeZone];
[formatter setDateFormat:#"yyyyMMddHH"];
NSString *dateString = [formatter stringFromDate:now];
// do whatever with the dates
[gregorianCalendar release];
[formatter release];

IOS NSDateFormatter Problems

We are building an app which requires the storage of a date for an entry on the device, the app will be international so we hit two dilema's / challenges.
User Preferences
If the user chooses the 12 hour rahter than 24 hour format we are returned from [NSDate date] a date like this 2012-07-17 11:26:03 AM which for sorting in a SQLite database is less than optimal as we cannot store it as a date.
User Locale
Typically this is ok however here in blighty we have a wonderfult thing called british summertime. which adds one hour every October 25th - 30th in a cycle and removes one hour every March 25 - 31th in a cycle so if no adjustment is made for 8 months of the year the time is one hour behind.
What I need to achieve is a consistent date formatted like this: 2012-07-17 11:26:03 no matter where the device is located and also taking into account where GMT+1 comes into place.
Any help would be awesome.
EDIT*
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd'T'HH:mm";
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT+01:00"];
[dateFormatter setTimeZone:gmt];
NSString *timeStamp = [dateFormatter stringFromDate:[NSDate date]];
NSDate *localDate = [dateFormatter dateFromString:timeStamp];
NSLocale* currentLocale = [NSLocale currentLocale];
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
NSLog(#"Country Code: %#", countryCode);
NSLog(#"Current Loacle: %#", currentLocale);
if(([countryCode isEqualToString: #"GB"])){
NSDate *mydate = [NSDate date];
NSDate *fiddlyFoo = [mydate dateByAddingTimeInterval:3600];
NSLog(#"Print GMT +1 %#",fiddlyFoo);
} else {
NSLog(#"Print GMT Local %#",localDate);
}
I'm doing something like this now. Note that NSDate "knows" about the current timezone and daylight savings time etc. So you just need to get the GMT version of the time in a sortable representation. I'd suggest RFC 3339 but you can use variations on it per your needs:
This code:
NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
// Create a local date for London, for testing purposes
NSDateComponents *comps = [NSDateComponents new];
[comps setTimeZone:[NSTimeZone timeZoneWithName:#"Europe/London"]];
[comps setDay:1];
[comps setMonth:7];
[comps setYear:2012];
[comps setHour:14];
NSDate *date = [gregorian dateFromComponents:comps];
// Just want to show this date is 2PM in London July 1st
NSDateFormatter *curFormat = [NSDateFormatter new];
[curFormat setDateStyle:NSDateFormatterFullStyle];
[curFormat setTimeStyle:NSDateFormatterFullStyle];
NSLog(#"Reference date is good no: %#", [curFormat stringFromDate:date]);
// So now we get the date as a rfc3339 string, referenced to GMT
NSString *timeString;
{
NSDateFormatter *rfc3339 = [NSDateFormatter new];
[rfc3339 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
rfc3339.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
timeString = [rfc3339 stringFromDate:date];
}
// referenced to UTC (sortable with any other time), can save in SQL DB etc
NSLog(#"Date as rfc3339 string: %#", timeString);
// Now lets convert it back into a BST time
NSDate *newDate;
{
NSDateFormatter *rfc3339 = [NSDateFormatter new];
[rfc3339 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
rfc3339.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
newDate = [rfc3339 dateFromString:timeString];
// we want to show this as a string 2012-07-17 11:26:03
NSDateFormatter *newFormatter = [NSDateFormatter new];
[newFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"]; // local time
[newFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"Europe/London"]];
NSLog(#"Local string using 24 hour clock: %#", [newFormatter stringFromDate:newDate]);
}
Generates this output, which I believe is what you want:
TimeTester[58558:f803] Reference date is good no: Sunday, July 1, 2012 9:00:00 AM Eastern Daylight Time
TimeTester[58558:f803] Date as rfc3339 string: 2012-07-01T13:00:00Z
TimeTester[58558:f803] Local string using 24 hour clock: 2012-07-01 14:00:00

Resources