NSDateComponents not printing accurate day/month/year times [duplicate] - objective-c

This question already has answers here:
Age extracted from birth date always off by inconsistent amount
(2 answers)
Closed 5 years ago.
I'm using NSDateComponents to get each day, month, year, hour, minute, etc separately but day, month, year and seconds are not accurate with 2017-11-08 1:00:00....Does anybody know what I'm missing?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd hh:mm:ss"];
NSDate *date = [dateFormatter dateFromString:#"2017-11-08 1:00:00"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
NSInteger hour = [components hour];
NSInteger minute = [components minute];
NSInteger second = [components second];
NSLog(#"day: %ld",(long)day);
NSLog(#"month: %ld",(long)month);
NSLog(#"year: %ld",(long)year);
NSLog(#"hour: %ld",(long)hour);
NSLog(#"minute: %ld",(long)minute);
NSLog(#"second: %ld",(long)second);
Output:
day: 2147483647
month: 2147483647
year: 2147483647
hour: 1
minute: 0
second: 2147483647

You're only getting the hour and minute, because that's all you're asking for. If you actually ask for the day, month, year, and second, you'll get them:
NSCalendarUnit units = NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear |
NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:units fromDate:date];

Related

Xcode - Set week of month, then get the week of year

Currently trying to use NSDateFormatter and NSDateComponents to set the week number for a month, then find the week of year that would be.
Let’s say we have a date: 20/05/2020, then set the week number to 0, or 1, or 2, etc.. then find out the week of year from that.
So far with NSDateComponents I’ve tried doing:
NSDate *date = self.date; // 20/05/2020
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
[components setWeekOfMonth:1];
NSDate *newDate = [calendar dateFromComponents:components];
NSInteger weekOfYear = [calendar components:NSCalendarUnitWeekOfYear fromDate:newDate].weekOfYear;
I'm always getting the output 18.
Expected output:
Change week of month to: 1 | output: 19 (4th)
Change week of month to: 2 | output: 20 (11th)
Change week of month to: 3 | output: 21 (18th)
Change week of month to: 4 | output: 22 (25th)
The problem is setting setWeekOfMonth in the date components has no effect.
Instead set the weekday and weekdayOrdinal
NSDate *date = self.date; // 20/05/2020
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
components.weekday = 2;
components.weekdayOrdinal = 3;
NSDate *newDate = [calendar dateFromComponents:components];
NSInteger weekOfYear = [calendar components:NSCalendarUnitWeekOfYear fromDate:newDate].weekOfYear;
This is a different approach:
It calculates the first monday in the current month, then it enumerates the next mondays until the end of the month.
- (NSArray *)yearOfWeeksInCurrentMonth
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components: NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
components.weekday = 2;
components.weekdayOrdinal = 1;
NSDate *firstMonday = [calendar dateFromComponents:components];
NSInteger weekOfYear = [calendar component:NSCalendarUnitWeekOfYear fromDate:firstMonday];
__block NSMutableArray *result = [NSMutableArray array];
[result addObject:#[firstMonday, #(weekOfYear)]];
NSDateComponents *weekComponents = [calendar components: NSCalendarUnitWeekday fromDate:firstMonday];
[calendar enumerateDatesStartingAfterDate:firstMonday matchingComponents:weekComponents options:NSCalendarMatchNextTime usingBlock:^(NSDate * _Nullable date, BOOL exactMatch, BOOL * _Nonnull stop) {
if (date != nil) {
if ( [calendar component:NSCalendarUnitMonth fromDate:date] > components.month ) {
*stop = YES;
} else {
NSInteger weekOfYear = [calendar component:NSCalendarUnitWeekOfYear fromDate:date];
[result addObject:#[date, #(weekOfYear)]];
}
}
}];
return [result copy];
}
The return value is an array containing the date (NSDate) and the corresponding weekOfYear (NSNumber)
(
(
"2020-05-03 22:00:00 +0000",
19
),
(
"2020-05-10 22:00:00 +0000",
20
),
(
"2020-05-17 22:00:00 +0000",
21
),
(
"2020-05-24 22:00:00 +0000",
22
)
)

Why is constructed NSDate from components not same as components used as input?

I am trying to set a NSDate from components; the hour is the only thing that I need to actually set, the remainder is from current date. This is my code:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
// put in date with start time
int month = [components month];
int day = [components day];
int year = [components year];
[components setHour: startTime];
[components setMinute: 0];
[components setSecond: 0];
NSDate *startDate = [gregorian dateFromComponents: components];
This is the result from the console:
Printing description of components:
Calendar Year: 2015
Month: 10
Leap month: no
Day: 3
Hour: 1730
Minute: 0
Second: 0
Printing description of startDate:
2015-10-25 15:57:12 +0000
Notice that the month and time have changed from what was supplied. My question is why did it change (for future reference) and what do I do to fix this?

Age extracted from birth date always off by inconsistent amount

I'm using the following code to convert a user-supplied birthdate to its equivalent years from the current date. The output is always off by an inconsistent amount in years and very large numbers in days and months.
NSDateFormatter *tempFormatter = [[NSDateFormatter alloc] init];
[tempFormatter setDateFormat:#"yyyy-mm-dd hh:mm:ss"];
NSDate *birthDate = [tempFormatter dateFromString:[NSString stringWithFormat:#"%#-%#-%# 01:00:00",self.birthYear.text,self.birthMonth.text,self.birthDay.text]];
NSDate* now = [NSDate date];
NSDateComponents* ageComponents = [[NSCalendar currentCalendar]
components:NSYearCalendarUnit
fromDate:birthDate
toDate:now
options:0];
NSInteger years = [ageComponents year];
NSInteger days = [ageComponents day];
NSInteger months = [ageComponents month];
NSLog(#"Years: %ld, Days: %ld, Months: %ld",years,days, months);
What gets outputted when I input "06-16-1986" is the following
Years: 28, Days: 9223372036854775807, Months: 9223372036854775807
The year is off by 1 and the months and days are producing extremely large numbers. I get the same issue using various dates. For example, "12-07-1989" produces
Years: 25, Days: 9223372036854775807, Months: 9223372036854775807
What am I doing wrong here?
the problem is in this line
NSDateComponents* ageComponents = [[NSCalendar currentCalendar]
components:NSYearCalendarUnit
fromDate:birthDate
toDate:now
options:0];
if you want month & days to be calculated you need to include that in the components like this
NSDateComponents* ageComponents = [[NSCalendar currentCalendar]
components:( NSYearCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit)
fromDate:birthDate
toDate:now
options:0];
also your format is wrong for month string
[tempFormatter setDateFormat:#"yyyy-mm-dd hh:mm:ss"];
should be corrected as
[tempFormatter setDateFormat:#"yyyy-MM-dd hh:mm:ss"];
now you will get correct date.
explanation.
as mentioned on apple documentation these are 1-based. so incase you dont provide a value it will put 1 as default. so earlier your month format was "mm" and it was not correctly setting a month for the birthday hence it was 1986-01-16 so now its 2014-04-01 (in singapore). So you get 28 years which is correct.
Add |NSMonthCalendarUnit|NSDayCalendarUnit to your components, change the date format from yyyy-mm-dd hh:mm:ss to yyyy-MM-dd hh:mm:ss:
NSDateFormatter *tempFormatter = [[NSDateFormatter alloc] init];
[tempFormatter setDateFormat:#"yyyy-MM-dd hh:mm:ss"];
NSDate *birthDate = [tempFormatter dateFromString:[NSString stringWithFormat:#"%#-%#-%# 01:00:00",#"2000", #"04",#"01"]];
NSDate* now = [NSDate date];
NSDateComponents* ageComponents = [[NSCalendar currentCalendar]
components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
fromDate:birthDate
toDate:now
options:0];
NSInteger years = [ageComponents year];
NSInteger days = [ageComponents day];
NSInteger months = [ageComponents month];
NSLog(#"Years: %d, Days: %d, Months: %d",years,days, months);

Getting NSDate of yesterday at 12:00 and tonight

Lets say its now morning in my place 10:00AM .
I need to get 2 NSDates , one of yesterday(or today) night at 12:01 , and the other of today at 11:59 at night (sorry i am little bit confused with AM and PM, and i don't want it to mess the question ).
I have seen here something like this :
NSDate *today = [NSDate date];
//intervals taken from Google
NSDate *yesterday = [today dateByAddingTimeInterval: -86400.0];
But this number is a const.
How would i subtract from now's date what is needed to get today's 12:01 at night (past) and todays 11:59 at night (future) ?
Thanks.
You write your own logic as you need
You will get the NSDate components using
NSDate *date = [NSDate date];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
NSInteger hours = [components hour];
NSInteger mins = [components minute];
NSInteger secs = [components second];
Now you can change the hours and mins a
hours -= 10;
mins -= 120
Now again the form the date object
[date setHour:hours];
[date setMinute:mins];

number of calendarweeks for year (ISO 8601 definition)

how can I calculate the number of calendarweeks in objective-C for a given year.
I tried:
[calendar rangeOfUnit:NSWeekOfYearCalendarUnit inUnit: NSYearCalendarUnit forDate: [NSDate date]].length
but it returns 54.
Thanks
You are using NSWeekOfYearCalendarUnit, so you must use the corresponding larger unit which is NSYearForWeekOfYearCalendarUnit.
NSCalendar *calendar = [NSCalendar currentCalendar];
calendar.firstWeekday = 2;
calendar.minimumDaysInFirstWeek = 4;
int n = [calendar rangeOfUnit:NSWeekOfYearCalendarUnit inUnit:NSYearForWeekOfYearCalendarUnit forDate: [NSDate date]].length;
NSLog(#"%d", n); // 52
Finally, note that both NSWeekOfYearCalendarUnit and NSYearForWeekOfYearCalendarUnit are iOS 5.0 and OS X 10.7 only.
Edit
As noted by #lnafziger, if you use a date that is in a calendar week from the previous year or next year such as 1/1/2016, this will calculate the number of weeks in that year (2015 in the example), and not the actual year of the date (2016 in the example). If this is not what you want, you can change the date like follows:
NSDateComponents *components = [calendar components:NSYearCalendarUnit fromDate:date];
components.month = 3;
date = [calendar dateFromComponents:components];
After a significant amount of testing, here is a function which will return it for any year:
- (NSUInteger)iso8601WeeksForYear:(NSUInteger)year {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *firstThursdayOfYearComponents = [[NSDateComponents alloc] init];
[firstThursdayOfYearComponents setWeekday:5]; // Thursday
[firstThursdayOfYearComponents setWeekdayOrdinal:1]; // The first Thursday of the month
[firstThursdayOfYearComponents setMonth:1]; // January
[firstThursdayOfYearComponents setYear:year];
NSDate *firstThursday = [calendar dateFromComponents:firstThursdayOfYearComponents];
NSDateComponents *lastDayOfYearComponents = [[NSDateComponents alloc] init];
[lastDayOfYearComponents setDay:31];
[lastDayOfYearComponents setMonth:12];
[lastDayOfYearComponents setYear:year];
NSDate *lastDayOfYear = [calendar dateFromComponents:lastDayOfYearComponents];
NSDateComponents *result = [calendar components:NSWeekCalendarUnit fromDate:firstThursday toDate:lastDayOfYear options:0];
return result.week + 1;
}
Basically, per the spec, the total number of weeks is the same as the total number of Thursday's in the year, which is what this calculates.
I have tested it for the entire 400 year cycle (starting in the year 0 and the year 2000) and all cases match the spec.