Get first day of week of year in Objective-C - ios

I'm trying to find the first day of weeks using DateTools like so:
for (NSInteger week = 46; week <= 53; week++) {
NSDate *tempDate = [NSDate dateWithString:[NSString stringWithFormat:#"2015-%d", (int)week] formatString:#"Y-w"];
NSLog(#"INFO: tempDate: %#, day: %.2d, week: %d", tempDate, (int)[tempDate day], (int)week);
}
for (NSInteger week = 1; week <= 5; week++) {
NSDate *tempDate = [NSDate dateWithString:[NSString stringWithFormat:#"2016-%d", (int)week] formatString:#"Y-w"];
NSLog(#"INFO: tempDate: %#, day: %.2d, week: %d", tempDate, (int)[tempDate day], (int)week);
}
and I get this output:
INFO: tempDate: 2015-11-07 22:00:00 +0000, day: 08, week: 46
INFO: tempDate: 2015-11-14 22:00:00 +0000, day: 15, week: 47
INFO: tempDate: 2015-11-21 22:00:00 +0000, day: 22, week: 48
INFO: tempDate: 2015-11-28 22:00:00 +0000, day: 29, week: 49
INFO: tempDate: 2015-12-05 22:00:00 +0000, day: 06, week: 50
INFO: tempDate: 2015-12-12 22:00:00 +0000, day: 13, week: 51
INFO: tempDate: 2015-12-19 22:00:00 +0000, day: 20, week: 52
INFO: tempDate: 2015-12-26 22:00:00 +0000, day: 27, week: 53
INFO: tempDate: 2015-12-26 22:00:00 +0000, day: 27, week: 1
INFO: tempDate: 2016-01-02 22:00:00 +0000, day: 03, week: 2
INFO: tempDate: 2016-01-09 22:00:00 +0000, day: 10, week: 3
INFO: tempDate: 2016-01-16 22:00:00 +0000, day: 17, week: 4
INFO: tempDate: 2016-01-23 22:00:00 +0000, day: 24, week: 5
and as you can see, the week 53 from 2015 has the same day as the week 1 from 2016 (This site tells me that there are 53 weeks in 2015).
Actually, the week 1 from 2016 starts from 04.01.2016.
Also, notice the dateWithString:formatString: gives me the previous day of the first day of the week. Why is that? I can simply use dateByAddingDays:1 but I don't know if it's hackish and the problem should be solved somewhere else.
I tried using NSDateComponents as #DarkDust mentioned, to no avail. So this:
for (NSInteger week = 1; week <= 5; week++) {
NSDate *tempDate = [NSDate dateWithString:[NSString stringWithFormat:#"2016-%d", (int)week] formatString:#"Y-w"];
NSDateComponents *dateComponents = [NSDateComponents new];
dateComponents.year = 2016;
dateComponents.weekOfYear = week;
NSLog(#"INFO: tempDate: %#, day: %.2d, week: %d = %#", tempDate, (int)[tempDate day], (int)week, [calendar dateFromComponents:dateComponents]);
}
gives me this:
INFO: tempDate: 2015-12-26 22:00:00 +0000, day: 27, week: 1 = 2015-12-31 22:00:00 +0000
INFO: tempDate: 2016-01-02 22:00:00 +0000, day: 03, week: 2 = 2015-12-31 22:00:00 +0000
INFO: tempDate: 2016-01-09 22:00:00 +0000, day: 10, week: 3 = 2015-12-31 22:00:00 +0000
INFO: tempDate: 2016-01-16 22:00:00 +0000, day: 17, week: 4 = 2015-12-31 22:00:00 +0000
INFO: tempDate: 2016-01-23 22:00:00 +0000, day: 24, week: 5 = 2015-12-31 22:00:00 +0000
Here is the DateTools' dateWithString:formatString: implementation:
+ (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString {
return [self dateWithString:dateString formatString:formatString timeZone:[NSTimeZone systemTimeZone]];
}
+ (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString timeZone:(NSTimeZone *)timeZone {
static NSDateFormatter *parser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
parser = [[NSDateFormatter alloc] init];
});
parser.dateStyle = NSDateFormatterNoStyle;
parser.timeStyle = NSDateFormatterNoStyle;
parser.timeZone = timeZone;
parser.dateFormat = formatString;
return [parser dateFromString:dateString];
}
As for the duplicate report:
The top 3 are wrong, 2 of them with downvotes, and they don't even do what I need at all.

This is a bad idea what you are doing. You must understand a date is a time stamp which can be interpreted differently depending on the time zone and the calendar you are using. As it was already mentioned in comments you should use date components for your solution. Note that printing out the date may use a different format and the result may not be expected.
Next a first weekday in a year depends on definition. If I remember correctly some standards (or all) will treat the first week of the year depending on what day of week is the first day. In other words if 1.1 is sunday then the first week of the year is in december but if it is tuesday then it is in january.
So if you want to find the beginning of the first monday of the given year the code should look something like this (I did not test it):
+ (NSDate *)firstWeekInYear:(NSInteger)year {
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSDate *toReturn = nil;
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = year;
NSDate *beginningOfTheYear = [calendar dateFromComponents:components];
NSInteger day = [calendar component:NSCalendarUnitWeekday fromDate:beginningOfTheYear];
NSInteger daysToAdd = (8-day)%7;
toReturn = [calendar dateByAddingUnit:NSCalendarUnitDay value:daysToAdd toDate:beginningOfTheYear options:kNilOptions];
return toReturn;
}
So you need to choose the calendar, create date components with a target year, get the date from those components with the calendar to get the beginning of the year. Then find out what weekday that is and increase such a number of days so the result is the beginning of the first monday in a year.

I finally figured it out. #Matic's solution doesn't work in iOS 7 and it's hard to understand, so I managed to find a simpler solution that works on iOS 7 too:
+ (NSDate *)getFirstDayInWeek:(NSInteger)week ofYear:(NSInteger)year {
/* create the calendar only once */
static NSCalendar *calendar = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
calendar.firstWeekday = 2;
calendar.minimumDaysInFirstWeek = 4;
});
NSDateComponents *components = [NSDateComponents new]; // create an empty date components object
components.year = year;
components.weekOfYear = week; // set the number of the week in the year
components.weekday = calendar.firstWeekday; // important! set the result date's day of week
return [calendar dateFromComponents:components];
}

Related

NSCalendar dateFromComponents returns strange date [duplicate]

This question already has an answer here:
NSDateComponents weekOfYear returns wrong date
(1 answer)
Closed 5 years ago.
The NSCalendar dateFromComponents function returns unexpected date.
Here is the use case :
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
[calendar setTimeZone:timeZone];
Where timezone is: Local Time Zone (Europe/Paris (GMT+1) offset 3600)
NSDateComponents *finalComponents = [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitYear | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekday fromDate:now];
[finalComponents setWeekday:weekDay];
At this point, I have the following value for finalComponents :
Calendar Year: 2018
Hour: 23
Minute: 59
Week of Year: 1
Weekday: 2
The [calendar dateFromComponents:finalComponents] returns 2018-12-31 22:59:00 +0000 and I expect 2017-12-31 22:59:00 +0000
Any help is welcome
May this help you..!
Note: I used value for weekday as 1 (for IST it gives me correct date). You can update it according to your timezone.
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
[calendar setTimeZone:NSTimeZone.localTimeZone];
NSDate *currentDate = [NSDate date];
NSDateComponents *finalComponents = [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitYear | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekday fromDate:currentDate];
[finalComponents setWeekday: 1];
[finalComponents setYearForWeekOfYear:finalComponents.year]; // Update year dynamically here
NSLog(#"Current Date: %#",currentDate);
NSLog(#"Print Date: %#",[calendar dateFromComponents:finalComponents]);
Current Date: Fri Jan 5 20:00:53 2018
Print Date: Sun Dec 31 20:00:00 2017
Ask me, if you need any further help..!
The components that you extract seem to be ambiguous, resulting in a weird date. If you include the NSCalendarUnitYearForWeekOfYear in your selected components, you should get the correct year:
NSDateComponents *finalComponents = [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitYear | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekday | NSCalendarUnitYearForWeekOfYear fromDate:now];
For me adding the extra component makes it so that I get the correct year.

IOS Components From Date Changes Timezone

I am using
NSDateComponents *components = [CURRENT_CALENDAR components:DATE_COMPONENTS fromDate:_startDate];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
_date = [CURRENT_CALENDAR dateFromComponents:components];
With dates I receive from an API.
The _date returns 2 different outputs depending on the calendar day:
2016-01-04 05:00:00 +0000
2015-10-26 04:00:00 +0000
As if there was a change in time zone.
Is there a reason the time of _date changes from 5 to 4 ?Is there something to prevent that?
Problem is that unexpected time offset (-1) reflects in all the other dates I create with dateFromComponents:components
Output for different dates showing the offset
2016-01-04 05:00:00 +0000
2015-12-21 05:00:00 +0000
2015-12-14 05:00:00 +0000
2015-12-07 05:00:00 +0000
2015-11-23 05:00:00 +0000
2015-11-16 05:00:00 +0000
2015-11-09 05:00:00 +0000
2015-11-02 05:00:00 +0000
2015-10-26 04:00:00 +0000
2015-10-19 04:00:00 +0000
2015-10-22 04:00:00 +0000
2015-10-01 04:00:00 +0000
2015-09-24 04:00:00 +0000

How to get valid quarter count instead of NSDateComponents.quarter is returning 0

NSDateComponents.quarter is returning 0 instead of valid quarter count.
I googled and it is said it is a bug for apple found in 2012. However, now it is 2015 and iOS 8.3, bug still exists.
What I need is the quarter count from firstDate to lastDate
for example:
unitFlags = NSQuarterCalendarUnit;
(lldb) po firstDate
2009-12-31 16:00:00 +0000
(lldb) po lastDate
2013-09-30 16:00:00 +0000
(lldb) po [gregorian components:unitFlags fromDate:firstDate toDate:lastDate options:0];
<NSDateComponents: 0x170158b50>
Quarter: 0

How to get NSDateComponents between two NSDateComponents?

I've two NSDateComponents, I want all NSDateComponents in between those two, I've tried the following,
NSDateComponents *first = ...;
NSDateComponents *second = ...;
BOOL boolDone = NO;
while (!boolDone) {
[array addObject:first];
first.day+=1;
NSLog(#"%#",first);
if([[first date] compare:[second date]] == NSOrderedSame)
{
boolDone = YES;
}
}
NSLog(#"All dates : %#",array);
After the loop, it just prints the date I've in first NSDateComponent...!! What's wrong?
Here's a log to understand
2014-01-18 19:47:16.413 testCalendar[4274:a0b]
Calendar Year: 2014
Month: 1
Leap month: no
Day: 19
2014-01-18 19:47:16.415 testCalendar[4274:a0b]
Calendar Year: 2014
Month: 1
Leap month: no
Day: 20
2014-01-18 19:47:16.416 testCalendar[4274:a0b]
Calendar Year: 2014
Month: 1
Leap month: no
Day: 21
2014-01-18 19:47:16.416 testCalendar[4274:a0b]
Calendar Year: 2014
Month: 1
Leap month: no
Day: 22
2014-01-18 19:47:16.417 testCalendar[4274:a0b]
Calendar Year: 2014
Month: 1
Leap month: no
Day: 23
2014-01-18 19:47:16.418 testCalendar[4274:a0b]
23-1-2014
23-1-2014
23-1-2014
23-1-2014
23-1-2014
You always add the same element to the array. The array just hold pointers to its
elements, therefore at the end of the loop, it contains n pointers to the same
object first. Changing
[array addObject:first];
to
[array addObject:[first copy]];
should solve the problem.

Parsing NSString to NSDate using NSDateFormatter

I have such string: "Tue, 22 Oct 2013 1:59 pm EEST"
i am trying to set these parsing rules:
[formatter setDateFormat: #"EEE, dd MMM yyyy hh:mm a z"];
but this returns nil when i perform:
[formatter dateFromString: #"Tue, 22 Oct 2013 1:59 pm EEST"];
What's the right regular expression for such format?
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"EEE, dd MMM yyyy hh:mm a zzz"];
[formatter setLocale:locale];
NSDate* date = [formatter dateFromString:#"Tue, 22 Oct 2013 1:59 PM EEST"];
NSLog(#"date: %#",date);
O/P:-date: 2013-10-22 10:59:00 +0000
hh is a padded hour ("01" in your case) but your string doesn't have padded hours (just "1" ) so it should be just h instead.
You can see a practical examples of the different formatting components below (output from this GitHub gist). The date being formatted is 1987-08-27 15:24:03
format result
--------------
yy 87
yyyy 1987
M 8
MM 08
MMM Aug
MMMM August
dd 27
HH 15
hh 03 // <--.
h 3 // <--'--- note the difference
a PM
mm 24
m 24
ss 03
s 3

Resources