I've spent a good few hours trying to make this work. Here's what I'm trying to do:
Take input from a UITextField in the form HH:mm AM/PM
Convert that string into an NSDate object, and update the month/day/year properties of that NSDate to reflect the curren month/day/year.
Add 4 hours to the time of the NSDate object.
That last bit isn't working. It works for times such as 12:00 PM and 12:30 PM but for times such as 2:30 PM, it will output 4:30 PM rather than 6:30 PM as expected. Here is my code, broken up to reflect those three tasks.
Task 1 - checking to see if the text input was HH:mm AM/PM
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]];
[format setTimeZone:[NSTimeZone systemTimeZone]];
[format setDateFormat:#"HH:mm a"];
NSString *dateString = textField.text;
NSLog(#"input datestring: %#", dateString);
NSDate *parsed = [format dateFromString:dateString];
if (parsed) {
NSLog(#"datestring is valid, %#", parsed);
}
Task 2- Updating the m/d/y components of that date to reflect today's.
//gregorian calendar, get the hour and minute components from the input time
NSCalendar *greg = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [greg components: NSCalendarUnitHour | NSCalendarUnitMinute fromDate:parsed];
//get the month/day/year components from the current date
NSDateComponents *comps = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
//set the components of the original date to the month/day/year components of today
[components setYear:comps.year];
[components setDay:comps.day];
[components setMonth:comps.month];
//create the new date.
NSDate* newDate = [greg dateFromComponents:components];
NSLog(#"########### %#", newDate);
Task 3 - Add 4 hours.
int hours = 4 ;
NSString *output;
NSDateComponents *add = [[NSDateComponents alloc] init];
[add setHour:hours];
newDate = [greg dateByAddingComponents:add toDate:newDate options:0];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateStyle:NSDateFormatterShortStyle];
[df setTimeStyle:NSDateFormatterShortStyle];
output = [df stringFromDate:newDate];
NSLog(#"new date: %#", output);
Log:
2015-08-27 16:03:35.672 Del Taco[1960:117431] input datestring: 2:30 pm
2015-08-27 16:03:35.673 Del Taco[1960:117431] datestring is valid, 2000-01-01 20:30:00 +0000
2015-08-27 16:03:35.673 Del Taco[1960:117431] ########### 2015-08-27 19:30:00 +0000
2015-08-27 16:03:35.674 Del Taco[1960:117431] new date: 8/27/15, 4:30 PM
I think I have this figured out. It was my date formatter where I set the format like this:
#"HH:mm a"
I went to Unicode's website to double check that I was doing this right, and ended up changing my format string to:
#"h:mm a"
And things seem to be working out well!
Related
I want to change hour in NSDate. I did something like this:
NSDate *final = [gregorian dateBySettingUnit:NSCalendarUnitHour value:hour.intValue ofDate:self.dateForNewEvent options:NSCalendarMatchStrictly];
where self.dateForNewEvent = 2018-07-09 07:24:13 +0000
and hour.intValue = 5 and i expect date = 2018-07-09 05:00:00 + 0000 but i got 2018-07-10 03:00:00 UTC. How should I do it to get expected date ?
To change the hours of a specific NSDate, you need to manipulate it via NSDateComponents. Please try below code for the same:
NSDate *now = [NSDate date]; // YOUR DATE INSTANCE
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:now];
[components setHour:5];
NSDate *today5am = [calendar dateFromComponents:components];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
NSLog(#"Date ===>>> %#",[dateFormatter stringFromDate:[NSDate date]]);
Hope this helps!
I think you are mixing NSDate with NSCalendar: NSDate is a point in time, internally represented in UTC. To get the local date/time as it is displayed on a calendar or watch , you use NSCalendar.
So if you are in MEST (UTC+2) and set the time to "5" hours on your calendar, this will be UTC "3" hours.
To get the calendar date/time back, you could use components(_:from:) from NSCalendar.
Just check this code:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
NSDate * dateForNewEvent = [dateFormatter dateFromString: #"2018-07-09T07:24:13+00:00"];
NSLog(#"dateForNewEvent: %#", dateForNewEvent);
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *comps = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:dateForNewEvent];
[comps setHour:5];
NSDate *final = [gregorian dateFromComponents:comps];
NSLog(#"final: %#", final);
Output as expected:
2018-07-09 11:11:43.542 jdoodle[22:22] dateForNewEvent: 2018-07-09 07:24:13 +0000
2018-07-09 11:11:43.542 jdoodle[22:22] final: 2018-07-09 05:24:00 +0000
I am taking an NSDate, and pulling just a 2-digit number, representing the day of the month into an NSString. One of the dates in question is:
2013-11-30 00:00:00 +0000
I use:
NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
[formatter2 setDateFormat:#"dd"];
NSString *datefromdate = [formatter2 stringFromDate:articleDate];
NSLog(#"Date%#", datefromdate);
[formatter2 release];
but the log comes back
29
You are probably in a negative time zone i.e. GMT minus something. This is why 2013-11-30 00:00:00 +0000 GMT is on the 29th day when you log it. Set the formatter to GMT and you will be fine.
Set the timezone you want the time date formatter to use. NSDate is the first instant of 1 January 2001, GMT and thus has no timezone information in it.
So, this, according to Apple, is going to get complicated needing up to five classes: NSDateFormatter, NSDate, NSCalendar, NSTimeZone and finally NSDateComponents.
If all you want is the day you can use NSDateComponents.
Example:
NSString *dateString = #"2013-11-30 00:00:00 +0000";
NSDateFormatter *inDateFormatter = [[NSDateFormatter alloc] init];
[inDateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss Z"];
NSDate *date = [inDateFormatter dateFromString:dateString];
NSLog(#"dateFromString: %#", date);
NSTimeZone *timezone = [NSTimeZone timeZoneForSecondsFromGMT:0];
// Using date formatter, result is a string
NSDateFormatter *outDateFormatter = [NSDateFormatter new];
[outDateFormatter setTimeZone:timezone];
[outDateFormatter setDateFormat:#"dd"];
NSString *dayString = [outDateFormatter stringFromDate:date];
NSLog(#"date formatter day: %#", dayString);
// Using date components, result is an integer
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone: timezone];
NSDateComponents *dateComponents = [calendar components:NSDayCalendarUnit fromDate:date];
NSInteger day = [dateComponents day];
NSLog(#"date components day: %i", day);
NSLog output:
dateFromString: 2013-11-30 00:00:00 +0000
date formatter day: 30
date components day: 30
This is how I setup my datePicker
self.datePicker = [[UIDatePicker alloc] init];
self.datePicker.timeZone = [NSTimeZone localTimeZone];
This is how I save the date that I selected
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
[formatter setTimeZone:[NSTimeZone localTimeZone]];
dateToSave = [formatter dateFromString:self.dateTextField.text];
NSLog(#"date saved = %#", dateToSave);
If I select Nov 18 2013 from the date picker, the NSLog shows
date saved = 2013-11-17 16:00:00 +0000
However, somewhere in my code, I need to get the difference in days between today's date and the date that I selected in the datepicker.
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSDayCalendarUnit fromDate:[NSDate date] toDate:dateSaved options:0];
NSLog(#"number of days => %i", [dateComponents day]);
Today is Nov 10. The date I saved is Nov 18. But the number of days difference is 7, instead of 8.
Your time zone is -8. 2013-11-17 16:00:00 +0000 equals to 2013-11-18 00:00:00 -0800.
Use [NSTimeZone timeZoneForSecondsFromGMT:0] instead of [NSTimeZone localTimeZone]
(This answer refers to the updated question about calculating the number of days
between two dates.)
The problem is that [NSDate date] is the current date+time, not the start of the current day. For example, if
[NSDate date] = "2013-11-10 10:00:00"
dateSaved = "2013-11-18 00:00:00" (both in your *local* timezone)
then the difference between
these two dates is "7 days and 14 hours". Therefore you get 7 as the number of days.
So you have to calculate the start of the current day first:
NSDate *startOfDay;
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit
startDate:&startOfDay
interval:NULL
forDate:[NSDate date]];
and then use it in the calculation of the difference:
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSDayCalendarUnit
fromDate:startOfDay
toDate:dateSaved
options:0];
NSDateFormatter *date_form=[[NSDateFormatter alloc]init];
[date_form setDateFormat:#"dd/MM/yyyy"];
NSDate *seletected_date = [datepicker date];
NSString *dateToSave=[[NSString alloc] initWithFormat:#"%#",[date_form stringFromDate:seletected_date]];
NSLog(#"date saved = %#", dateToSave);
Remove localtimezone
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.
I have the following code to set the time on a date to 0 hours, 0 minutes, 0 seconds by using the YEAR, MONTH and DAY components from the date to construct a new one:
NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
NSDate *newDate = [gregorian dateFromComponents:comps];
NSLog(#"date: %#, newDate: %#", date, newDate);
The output is:
date: 2012-11-06 11:44:09 +0000, newDate: 2012-11-05 23:00:00 +0000
but I was expecting the new date to be: 2012-11-06 00:00:00 +0000
What's happening that I should know of?
NSLog shows the dates using -[NSDate description] which, in turn, converts the absolute time stored in the NSDate to a string. This conversion is done using UTC as the time zone.
For you case it's probably best to do the date calculations in UTC as well. To do so adjust the calendar object that does the calculations:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
See this answer for a similar Stack Overflow question.
This should solve your issue with daylight savings:
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setCalendar:[NSCalendar currentCalendar]];
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
NSString *string = [formatter stringFromDate:[NSDate date]];