60

I'm getting started developing for the iPhone and as such I am looking at different tutorials online as well as trying some different things out myself. Currently, I'm trying to create a countdown until midnight. To get the number of hour, minutes, and seconds, I do the following (which I found somewhere):

NSDate* now = [NSDate date];

int hour = 23 - [[now dateWithCalendarFormat:nil timeZone:nil] hourOfDay];
int min = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] minuteOfHour];
int sec = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] secondOfMinute];
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min,sec];

However, each place I use -dateWithCalendarFormat:timeZone: I get the following error:

warning: 'NSDate' may not respond to '-dateWithCalendarFormat:timeZone:'
(Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments.)
warning: no '-hourOfDay' method found
error: invalid operands to binary - (have 'int' and 'id')

This seems like something very simple. What am I missing?

Also, I've noticed at different places and at different times the asterisk (*) is located either right after the time NSDate* now or right before the variable NSDate *now. What is the difference in the two and why would you use one versus the other?

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
Jason
  • 17,276
  • 23
  • 73
  • 114

14 Answers14

59

You have problems with iOS 4.2? Use this Code:

NSDate *currDate = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"dd.MM.YY HH:mm:ss"];
NSString *dateString = [dateFormatter stringFromDate:currDate];
NSLog(@"%@",dateString);

-->20.01.2011 10:36:02

Hover
  • 591
  • 4
  • 2
  • 5
    actually `YY` will output the year in 2 digits. `YYYY` will output the year in 4 digits. – Bot Jan 24 '12 at 20:10
48

You must use the following:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit  | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:yourDateHere];
NSInteger hour = [dateComponents hour];
NSInteger minute = [dateComponents minute];
NSInteger second = [dateComponents second];
[gregorian release];

There is no difference between NSDate* now and NSDate *now, it's just a matter of preference. From the compiler perspective, nothing changes.

Massimo Cafaro
  • 25,429
  • 15
  • 79
  • 93
  • Why do you have to use NSCalendar is this a recent change? There is a lot of code out there that is using NSDate for these things. – Jason Jul 01 '09 at 17:40
  • 2
    Jason: NSDate is still there. You're using NSCalendarDate (that's what -dateWithCalendarFormat:timeZone: returns), which is slated for deprecation and doesn't exist on the iPhone. – Peter Hosey Jul 02 '09 at 02:32
21

You can also use:


CFGregorianDate currentDate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), CFTimeZoneCopySystem());
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02.0f", currentDate.hour, currentDate.minute, currentDate.second];
CFRelease(currentDate); // Don't forget this! VERY important

I think this has the following advantages:

  1. No direct memory allocation.
  2. Seconds is a double instead of an integer.
  3. No message calls.
  4. Faster.
Mark
  • 6,108
  • 3
  • 34
  • 49
12

// you can get current date/time with the following code:

    NSDate* date = [NSDate date];
    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
    NSTimeZone *destinationTimeZone = [NSTimeZone systemTimeZone];
    formatter.timeZone = destinationTimeZone;
    [formatter setDateStyle:NSDateFormatterLongStyle];
    [formatter setDateFormat:@"MM/dd/yyyy hh:mma"];
    NSString* dateString = [formatter stringFromDate:date];
Hemant Sharma
  • 171
  • 1
  • 6
5

Replace this:

NSDate* now = [NSDate date];
int hour = 23 - [[now dateWithCalendarFormat:nil timeZone:nil] hourOfDay];
int min = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] minuteOfHour];
int sec = 59 - [[now dateWithCalendarFormat:nil timeZone:nil] secondOfMinute];
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, min,sec];

With this:

NSDate* now = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit  | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:now];
NSInteger hour = [dateComponents hour];
NSInteger minute = [dateComponents minute];
NSInteger second = [dateComponents second];
[gregorian release];
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second];
4

You need to do something along the lines of the following:

NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSHourCalendarUnit fromDate:now];
NSLog(@"%d", [components hour]);

And so on.

Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
4

Also, I've noticed at different places and at different times the asterisk (*) is located either right after the time NSDate* now or right before the variable NSDate *now. What is the difference in the two and why would you use one versus the other?

The compiler doesn't care, but putting the asterisk before the space can be misleading. Here's my example:

int* a, b;

What is the type of b?

If you guessed int *, you're wrong. It's just int.

The other way makes this slightly clearer by keeping the * next to the variable it belongs to:

int *a, b;

Of course, there are two ways that are even clearer than that:

int b, *a;

int *a;
int b;
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Yes, this is a good argument for either: always attach the * to the variable name; or never declare two variables on the same line. Personally, since I prefer to think of the "pointer to" as part of the type, I choose the latter solution, but people should definitely be aware of this issue. – Peter N Lewis Jul 02 '09 at 03:15
1

NSDate* now and NSDate *now are the same thing: a pointer to an NSDate object.

You probably want to use descriptionWithCalendarFormat:timeZone:locale: rather than dateWithCalendarFormat: — the latter returns an NSCalendarDate, which the docs say is scheduled to be deprecated at some point.

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
John
  • 1,549
  • 1
  • 13
  • 15
1

Here's another way:

NSDate *now = [NSDate date];

//maybe not 100% approved, but it works in English.  You could localize if necessary
NSDate *midnight = [NSDate dateWithNaturalLanguageString:@"midnight tomorrow"]; 

//num of seconds between mid and now
NSTimeInterval timeInt = [midnight timeIntervalSinceDate:now];
int hours = (int) timeInt/3600;
int minutes = ((int) timeInt % 3600) / 60;
int seconds = (int) timeInt % 60;

You lose subsecond precision with the cast of the NSTimeInterval to an int, but that shouldn't matter.

Jeff Hellman
  • 2,117
  • 18
  • 17
  • On second look, the iPhone SDK may not have -(NSDate) dateWithNaturalLanguageString: (NSString *) str method so this may be a Leopard/Tiger only solution. Sorry – Jeff Hellman Jul 01 '09 at 19:40
1

Original poster - the way you're determining seconds until midnight won't work on a day when daylight savings starts or ends. Here's a chunk of code which shows how to do it... It'll be in number of seconds (an NSTimeInterval); you can do the division/modulus/etc to get down to whatever you need.

NSDateComponents *dc = [[NSCalendar currentCalendar] components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:[NSDate date]];
[dc setDay:dc.day + 1];
NSDate *midnightDate = [[NSCalendar currentCalendar] dateFromComponents:dc];
NSLog(@"Now: %@, Tonight Midnight: %@, Hours until midnight: %.1f", [NSDate date], midnightDate, [midnightDate timeIntervalSinceDate:[NSDate date]] / 60.0 / 60.0);
James Boutcher
  • 2,593
  • 1
  • 25
  • 37
0

There is no difference in the location of the asterisk (at in C, which Obj-C is based on, it doesn't matter). It is purely preference (style).

chrish
  • 2,352
  • 1
  • 17
  • 32
0

It took me a while to locate why the sample application works but mine don't.

The library (Foundation.Framework) that the author refer to is the system library (from OS) where the iphone sdk (I am using 3.0) is not support any more.

Therefore the sample application (from about.com, http://www.appsamuck.com/day1.html) works but ours don't.

  • Foundation is still part of the iPhone SDK. You really can't go without it. What's missing is NSCalendarDate, which has never been available on the iPhone (as far as I know). – Peter Hosey Aug 15 '09 at 18:22
0
+ (NSString *)displayCurrentTimeWithAMPM 
{
    NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
    [outputFormatter setDateFormat:@"h:mm aa"];

    NSString *dateTime = [NSString stringWithFormat:@"%@",[outputFormatter stringFromDate:[NSDate date]]];

    return dateTime;
}

return 3:33 AM

TONy.W
  • 1,948
  • 19
  • 10
0
CFGregorianDate currentDate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), CFTimeZoneCopySystem());
countdownLabel.text = [NSString stringWithFormat:@"%02d:%02d:%2.0f", currentDate.hour, currentDate.minute, currentDate.second];

Mark was right this code is MUCH more efficient to manage dates hours min and secs. But he forgot the @ at the beginning of format string declaration.

Franakin
  • 9
  • 1
  • 1
    Don't forget the 0 in front of the third 2, while you're at it. Otherwise, seconds fewer than 10 will come out as (for example) “:6”. – Peter Hosey Apr 08 '10 at 16:10