Why [calendar dateFromComponents:] always return hour 16 ? - ios

When I pass the [NSDate date] to NSDateComponents , then pass back after the weekday modified, I always got the hour 16:00:00 . Why ?
Code belows:
{
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
NSDateComponents *firstDayOfWeek = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday fromDate:now];
firstDayOfWeek.weekday = 1; // Monday as first day of week
NSDate *firstDayOfWeekDate = [calendar dateFromComponents:firstDayOfWeek];
NSLog(#"first day = %#", firstDayOfWeekDate);
}
{
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
NSDateComponents *firstDayOfWeek = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:now];
firstDayOfWeek.weekday = 1; // Monday as first day of week
firstDayOfWeek.hour = 1;
firstDayOfWeek.minute = 1;
firstDayOfWeek.second = 1;
NSDate *firstDayOfWeekDate = [calendar dateFromComponents:firstDayOfWeek];
NSLog(#"first day = %#", firstDayOfWeekDate);
}
And the output is :
now = 2016-01-18 03:14:08 +0000
first day = 2016-01-17 16:00:00 +0000
now = 2016-01-18 03:14:08 +0000
first day = 2016-01-17 17:01:01 +0000
Why the hour begins with 16 ?

NSDateComponents isn't making it 16:00:00. It's making it midnight in your current timezone. But the NSLog is showing you the equivalent GMT time. That's what the +0000 means: e.g. GMT +00:00.
Bottom line, if you want to see the resulting NSDate object in your local time zone, you should use NSDateFormatter.

This is due to timezone.
Try to add NSDateFormatter before you output the date with NSLog, e.g.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[NSLocale currentLocale]];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterLongStyle];
NSString *dateDisplay = [dateFormatter stringFromDate:firstDayOfWeekDate];
NSLog(#"first day = %#",dateDisplay);
You can refer this article for more examples.

Related

NSCalendar return wrong date for previous Monday in first week of next year

I am using below code to calculate previous Monday using random date. Which work file but my code break on next year date.below is the code for same :
NSDate *date = [NSDate dateWithTimeIntervalSince1970:1483620311.228];
NSLog(#"current date ===> : %#", date);
NSCalendar *calendar;
calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[calendar setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en-US"]];
NSTimeZone *zone = [NSTimeZone timeZoneWithName:#"Australia/Sydney"];
[calendar setTimeZone:zone];
NSDateComponents *nowComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitWeekday | NSCalendarUnitWeekOfYear | NSCalendarUnitWeekOfMonth | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitTimeZone fromDate:date];
if (nowComponents.weekday == 1) { //If my date fall in sunday then I am moving to previous week.
[nowComponents setWeekOfYear:[nowComponents weekOfYear]-1];
}
[nowComponents setWeekday:2]; //Monday
NSDate *previousMonday = [calendar dateFromComponents:nowComponents];
NSLog(#"previousMonday date ===> : %#", previousMonday);
In Log I am getting as :
current date ===> : 2017-01-05 12:45:11 +0000
previousMonday date ===> : 2018-01-01 12:45:11 +0000
I have seen other stack overflow resource but not getting expected solution.
There is a much smarter way to do that using nextDateAfterDate of NSCalendar. It can search backwards for the match of a single component in this case NSCalendarUnitWeekday == 2
NSDate *date = [NSDate dateWithTimeIntervalSince1970:1483620311.228];
NSLog(#"current date ===> : %#", date);
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *previousMonday = [calendar nextDateAfterDate:date
matchingUnit:NSCalendarUnitWeekday
value:2
options:NSCalendarMatchNextTime | NSCalendarSearchBackwards];
NSLog(#"previousMonday date ===> : %#", previousMonday);
The time zone and locale don't really matter.

NSDateComponents issue - set incorrect day [duplicate]

This question already has answers here:
Unexpected value from NSDate
(2 answers)
Closed 6 years ago.
There are many issues similar to mine, but something goes wrong. Why function setDay sets to 1 day less? Where did I go wrong?
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *today = [NSDate date];
NSLog(#"today date = %#", today);
NSDateComponents *components = [calendar components:(NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear ) fromDate:today];
[components setDay:1];
NSLog(#"modified date = %#", [calendar dateFromComponents:components]);
Log:
today date = 2016-07-04 10:01:30 +0000
modified date = 2016-06-30 21:00:00 +0000
Modified date should be 2016-07-01
Likely it is 2016-07-01, 24:00 GMT. NSLog() prints it out for GMT, not your locale time.
See here: Unexpected value from NSDate
You should add NSCalendarUnitHour in NSDateComponents like this then then you get correct date
NSDateComponents *components = [calendar components:(NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear |NSCalendarUnitHour) fromDate:today];
Try this code:
NSDateFormatter* df = [[NSDateFormatter alloc] init];
[df setTimeZone:[NSTimeZone defaultTimeZone]]; // your time 00:00:00
[df setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSLog(#"modified date = %#", [df stringFromDate:[calendar dateFromComponents:components]]); // Date format NSString

Constructing an NSDate from today's date and a string with the time

If I have a string representing a time, say "10:45 am", and do the following to parse the string:
NSDateFormatter *dateFormat;
dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"h:mm a"];
NSLog(#"%#", [dateFormat dateFromString:#"10:45 AM"]);
I would get this logged:
2013-09-09 17:52:30.416 TimeTest[49491:a0b] 2000-01-01 15:45:00 +0000
How can I create an NSDate for the current day at the given time? I tried this
NSDate *date = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"h:mm a"];
NSDate *time = [formatter dateFromString:#"10:45 AM"];
NSDateComponents *timeComponents = [[NSCalendar currentCalendar] components:(NSHourCalendarUnit | NSMinuteCalendarUnit ) fromDate:time];
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
NSDateComponents *newComponents = [[NSDateComponents alloc]init];
newComponents.timeZone = [NSTimeZone systemTimeZone];
[newComponents setDay:[dateComponents year]];
[newComponents setMonth:[dateComponents month]];
[newComponents setYear:[dateComponents year]];
[newComponents setHour:[timeComponents hour]];
[newComponents setMinute:[timeComponents minute]];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *combinedDate = [gregorianCalendar dateFromComponents:newComponents];
NSLog(#"%#", combinedDate);
with the result
2013-09-09 19:57:14.506 TimeTest[49712:a0b] 2019-03-06 15:45:00 +0000
How should I go about this?
I'm not exactly sure what you are looking for. For what I understand, you want to build a date with the current year, month and day, but with your supplied time by parsing it from a string.
If that is the case, as others have pointed out, you need to play with NSDateComponents.
Based on your code I wrote these lines. They should build a date by merging two dates. The current one and the one you parsed.
// Get the full current date
NSDate *date = [NSDate date];
// Get the current calendar
NSCalendar *calendar = [NSCalendar currentCalendar];
// Split the date into components but only take the year, month and day and leave the rest behind
NSDateComponents *dateComponents = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
// Build the date formatter
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"h:mm a"];
// Convert the string time into an NSDate
NSDate *time = [formatter dateFromString:#"10:45 AM"];
// Split this one in components as well but take the time part this time
NSDateComponents *timeComponents = [[NSCalendar currentCalendar] components:(NSHourCalendarUnit | NSMinuteCalendarUnit ) fromDate:time];
// Do some merging between the two date components
dateComponents.hour = timeComponents.hour;
dateComponents.minute = timeComponents.minute;
// Extract the NSDate object again
NSDate *result = [calendar dateFromComponents:dateComponents];
// Check if this was what you where looking for
NSLog(#"%#",result);
Please be aware that this sample code is by far non-optimized. There are more crisp ways to obtain what you are looking for by using time intervals, but I felt like you wanted a dirty simple example on how to do components copy and paste and then extracting dates.
This will create a date for the beginning of the day in the current time zone.
NSDate *today = [NSDate date];
NSTimeInterval interval;
NSCalendar *cal = [NSCalendar currentCalendar];
[cal rangeOfUnit:NSDayCalendarUnit
startDate:&today
interval:&interval
forDate:today];
Now we add the time:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
// I have to set the locale to posix_en_us, as my system is using 24hour style as default
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[dateFormatter setDateFormat:#"hh:mm a"];
NSDate *time = [dateFormatter dateFromString:#"10:45 AM"];
NSDateComponents *comps = [cal components:(NSHourCalendarUnit | NSMinuteCalendarUnit)
fromDate:time];
NSDate *dateAndTime = [cal dateByAddingComponents:comps
toDate:today
options:0];
dateAndTime will now be todays date with 10:45 am in the local timezone.
controlling in the debugger:
po dateAndTime
$0 = 0x41b7df138c00000d 2013-09-10 08:45:00 +0000
This is correct, as my timezone is 2 hours ahead to GMT, as we still have summer time.

issue with NSDate I am removing year but takes 0001 as year

Below is my code, and I will explain things in it
NSDateFormatter *formater = [[NSDateFormatter alloc] init] ;
[formater setDateFormat:#"MM-dd"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger desiredComponents = (NSDayCalendarUnit | NSMonthCalendarUnit);
NSDateComponents *firstComponents = [calendar
components:desiredComponents fromDate:fDate];
NSDateComponents *secondComponents = [calendar
components:desiredComponents fromDate:eDate];
NSDateComponents *thirdComponents = [calendar
components:desiredComponents fromDate:mdate];
NSDate *from = [calendar dateFromComponents:firstComponents];
NSDate *end = [calendar dateFromComponents:secondComponents];
NSDate *middle = [calendar dateFromComponents:thirdComponents];
NSString * froms = [formater stringFromDate:from];
NSLog(#"sringfroms:%#",froms);
NSLog(#"datefrom:%#",from);
NSString * ends = [formater stringFromDate:from];
NSLog(#"sringends:%#",ends);
NSLog(#"dateend:%#",end);
NSString * middles = [formater stringFromDate:from];
NSLog(#"sringmiddles:%#",middles);
NSLog(#"datemiddle:%#",middle);
And this is what gets printed.
sringfroms:06-30
datefrom:0001-06-29 18:06:32 +0000
sringends:06-30
dateend:0001-08-02 18:06:32 +0000
sringmiddles:06-30
datemiddle:0001-07-14 18:06:32 +0000
You can see that i am removing year from date which get printed in string but when i make date it takes 0001 automatically. I am using kal calendar and i want to mark for month and day only so it should get marked for every year
if (([middle compare:from] == NSOrderedDescending)
&&([middle compare:end] == NSOrderedAscending)) {
[holidays addObject:[Holiday holidayNamed:naMMe
birthDate:bDate iDs:aIDDS date:middle]];
}
Any help will be highly appreciated
Its OKAY, since you are not using NSYearCalendarUnit in your following code
NSInteger desiredComponents = (NSDayCalendarUnit | NSMonthCalendarUnit);
so NSDate is assuming 0001 as default year, if you add NSYearCalendarUnit in the above code it will give correct year, like below
NSInteger desiredComponents = (NSDayCalendarUnit
| NSMonthCalendarUnit | NSYearCalendarUnit);
Well,if you are not mensioned any component then it'l take always
first value(01,0001 etc) that is default nature of NSCalendar,for resolving your
problem you need to convert that date to your date formatt.

UITableview title bar with date for week?

How can I have the title of UITableview with date?
I know I can edit the title using:
self.title=#"meals for ..";
I want to add the date for one week. How can I do that?
You can get a date like this:
NSDate *theDateToday = [NSDate date];
And format it like this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEE MMM dd"];
NSString *theDateAsAString = [dateFormatter stringFromDate:theDateToday];
Combine that with what you have like this:
self.title = [NSString stringWithFormat:#"meals for %#", theDateAsAString];
As for finding the days of the week, you could try adapting this answer.
- (NSString *)getWeekStartDateAsString:(NSDate *)date {
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int dayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[components setDay:([components day] - ((dayofweek) - 2))];// for beginning of the week.
NSDate *beginningOfWeek = [gregorian dateFromComponents:components];
NSDateFormatter *dateFormat_first = [[NSDateFormatter alloc] init];
[dateFormat_first setDateFormat:#"yyyy-MM-dd"];
dateString2Prev = [dateFormat stringFromDate:beginningOfWeek];
weekstartPrev = [[dateFormat_first dateFromString:dateString2Prev] retain];
return weekstartPrev;
}
- (NSString *)getWeekEndDateAsString:(NSDate *)date {
NSCalendar *gregorianEnd = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *componentsEnd = [gregorianEnd components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int Enddayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[componentsEnd setDay:([componentsEnd day]+(7-Enddayofweek)+1)];// for end day of the week
NSDate *EndOfWeek = [gregorianEnd dateFromComponents:componentsEnd];
NSDateFormatter *dateFormat_End = [[NSDateFormatter alloc] init];
[dateFormat_End setDateFormat:#"yyyy-MM-dd"];
dateEndPrev = [dateFormat stringFromDate:EndOfWeek];
weekEndPrev = [[dateFormat_End dateFromString:dateEndPrev] retain];
return weekEndPrev;
}
You end result might look like this:
self.title = [NSString stringWithFormat:#"meals for %#-%#", [self getWeekStartDateAsString:theDateToday], [self getWeekEndDateAsString:theDateToday]];
A rarely known NSCalendar method will be the best option IMO
rangeOfUnit:startDate:interval:forDate:. It gives you the start and the duration (interval) for a certain time unit. With it it is easy to find the start of the week in the used calendar and add the range-1 to get the latest second in that week.
With the also rarely seen localizedStringFromDate:dateStyle:timeStyle: Available in OS X v10.6, iOS 4.0 it is easy to create a localized representation of the dates.
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfTheWeek
interval:&interval
forDate:now];
//startOfWeek is 2013-06-02 22:00:00 +0000 now (note: it is GMT with timezone support for my timezone Europe/Berlin/DST)
endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval-1];
// 2013-06-09 21:59:59 +0000
NSString *text = [NSDateFormatter localizedStringFromDate:startOfTheWeek
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterNoStyle];
text = [text stringByAppendingString:#" to "];
text = [text stringByAppendingFormat:#"%#", [NSDateFormatter localizedStringFromDate:endOfWeek
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterNoStyle]];
self.title=text;
results in 03.06.13 to 09.06.13 for my german locale, where Monday is start of the week.

Resources