How do I obtain the sub-second portion of an NSDate? - macos

Using OS X v10.10.1 (Yosemite) and Xcode 6.1.1.
I feel I must be overlooking something simple and obvious, but I just can't see it. I'm parsing the parts of an NSDate whose value is just the system date.
NSDate *currentDate = [NSDate date];
NSCalendar *systemCalendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponents = [systemCalendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit) fromDate:currentSystemDate];
I can then get the seconds as
NSInteger seconds = [dateComponents second];
but this only gives me the whole number of seconds. I need the decimal portion of the seconds as well. According to the documentation. I can use
NSInteger nanos = [dateComponents nanoseconds];
to get the "number of nanosecond units for the receiver". It doesn't work however. The method is legitimate, but nanos is set to the maximum value of NSInteger. This is (I presume) because I have not supplied a suitable constant in the components method. The problem with that is that there is no NSNanosecondCalendarUnit constant and I cannot find any suitable alternative.
I could construct a new date using the date components and then subtract the two to get an NSTimeInterval which I believe will give me what I need, but I'm finding it hard to believe that there isn't a more straightforward way to get it.
Is there a simple straightforward way in Cocoa to calculate the sub-second portion of an NSDate or do I have to use the approach I just suggested?

You are using the older deprecated names for the components. The new names include NSCalendarUnitNanosecond. Take a look at NSCalendar.h, and you'll see the old and new names.
NSDateComponents *dateComponents = [systemCalendar components:(NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond|NSCalendarUnitNanosecond) fromDate:currentSystemDate];

Related

Converting AppleScript Date into an NSdate object in ASOBJC?

So I have some AppleScriptObjC code that does some things with dates. One of the first things it does is convert todays date into an NSDate object.
set nowDate to current application's NSDate's |date|()
I'd like to be able to set nowDate to any date I choose. From everything I have read, up until 10.11 this was a very complex process. But in 10.11 it supposedly got easier. I couldn't actually find any examples on how it was easier.
What I'd really like is to be able to say
set aDate to current date
set day of aDate to "15"
set month of aDate to "5"
set year of aDate to "2020"
set hours of aDate to "13"
set minutes of aDate to "00"
set seconds of aDate to "00"
set nowDate to NSDate's aDate --or something simple like that
I also found this function that looks to be able to set a date to any date you would like, but I have not idea how to convert this into useful ASOBJC code (I fumbled around a bit with it, but got nowhere):
- (NSDate *)dateWithEra:(NSInteger)eraValue
year:(NSInteger)yearValue
month:(NSInteger)monthValue
day:(NSInteger)dayValue
hour:(NSInteger)hourValue
minute:(NSInteger)minuteValue
second:(NSInteger)secondValue
nanosecond:(NSInteger)nanosecondValue;
Bonus question... what is the integer value for the era we are in?
Although AppleScriptObjC will easily convert an NSDate to an AppleScript date, there isn't anything quite that simple for going the other way.
There is something quite simple: AppleScript date is implicitly bridged to NSDate when being used as a parameter so you can convert it easily with
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
set currentDate to current date
set cocoaDate to current application's NSDate's dateWithTimeInterval:0 sinceDate:currentDate
log cocoaDate
Although AppleScriptObjC will easily coerce an NSDate to an AppleScript date, there isn't anything quite that simple for going the other way. Cocoa has a ton of options for dates, so you will need to use a few statements to define what you want.
The conversion for your Objective-C method would be something like (don't forget the pipes around AppleScript terms):
use framework "Foundation"
use scripting additions
tell (current date) to set {theDay, theMonth, theYear, theHours, theMinutes, theSeconds} to {its day, its month as integer, its year, its hours, its minutes, its seconds}
set theCalendar to current application's NSCalendar's currentCalendar
theCalendar's dateWithEra:1 |year|:theYear |month|:theMonth |day|:theDay hour:theHours minute:theMinutes |second|:theSeconds nanosecond:0
You could also use NSDateFormatter to convert from an ISO date string:
tell ((current date) as «class isot» as string) to set dateString to it
set formatter to current application's NSDateFormatter's alloc's init
formatter's setDateFormat:"yyyy-MM-dd'T'HH:mm:ss"
# formatter's setLocale:(current application's NSLocale's alloc's initWithLocaleIdentifier:"en_US_POSIX")
formatter's dateFromString:dateString
For your bonus question, it depends on the calendar used, but for the Gregorian Calendar there are two eras, BCE/BC and CE/AD. From the Apple's Date and Time Guide, if you are using negative years for BCE, the era would be 0, otherwise it would be 1.

Get Windows Long Time Format

I'm trying to get the long time format in Windows (like "hh:mm:ss tt"). I can get the short time format like this:
GetLocaleInfoEx(NULL, LOCALE_STIMEFORMAT, format, 100);
I can't seem to find a constant for LOCALE_LTIMEFORMAT or anything like that. I can get the short time, short date and long date, but how can I query the current user's long time format?
As far as I can tell, windows defines the 'long' time format by LOCALE_STIMEFORMAT (set to something like "hh:mm:ss"), and the short time as LOCALE_SSHORTTIME (which, according to MSDN, is valid for Windows 7 and later).
Does that correspond to your findings, i.e. does it match the user preference in the Region and Language Control Panel item?
For the t specifier, if it is not included into the locale format, then either you are left with always using a custom format (like gbjbaanb said), or perhaps examining the format string for the presence of t or tt, and if absent adding it yourself (though, this might lead to odd results for cultures expecting the tt before the general time, for instance). This should not be necessary though, as the time format used by the locale is responsible for yielding time-strings that make sense (distinguishing between AM and PM, for instance).
What you are looking for is already included in LOCALE_STIMEFORMAT. Sample code:
wchar_t format[80]; // 80 is always enough
int ret = GetLocaleInfoEx(
LOCALE_NAME_USER_DEFAULT,
LOCALE_STIMEFORMAT,
format,
sizeof(format) / sizeof(*format));
if (ret == 0) die(GetLastError());
std::wcout << format << std::endl;
Output on my machine (I live in the USA):
h:mm:ss tt
The "tt" part will be absent for any culture or locale customization that does not display the AM/PM designator.
The LOCALE_STIMEFORMAT is the long time format. To get the short time format you can use LOCALE_SSHORTTIME starting with Windows 7 or cut off the seconds.
To confirm this simply change the long time format in your Control Panel / Region and Language settings.
Try using it with the LOCALE_S1159 and LOCALE_2359 constants which return the text for the AM/PM designators.
I think the issue is that time format is a time format, down to the second. You have to format it yourself if you want AM/PM just like you do with daylight savings time or timezone indicators.

How can I determine which date format I've received?

In dealing with a wide variety of JSON data being sent from various clients date format standardization is a real problem.
I might get any of these:
2013-10-05
2-6-13
Mon, Jul 13 2013
Sometimes there's hours, minutes and seconds as well as time zone information. For each client I've had to manually set up an NSDateFormatter, or if unable to get it parsing I've done substring searching. What I'd really like to do is get all this combined into a single method and somehow determine which type I'm dealing with. Then have some kind of switch statement that handles the parsing for that particular date.
I'm unable to change their formats, but how can I deal with them better?
This is not exactly the answer to your question but when I'm looking to parse dates without being sure of the format I usually go for NSDataDetector setup for dates only. The detector will often detect dates in multiple languages and format all at once. It's pretty neat.
Okay thanks for the comments. What I ended up doing is going to http://waracle.net/iphone-nsdateformatter-date-formatting-table/ and studying VERY CAREFULLY all the NSDateFormatter options. In particular it was the time zone +0000 at the end of RSS posts which was messing up my format conversions. RSS dates match RFC 822, which means you need to use THREE Z's at the end to properly catch it.
I was attempting things like +ZZZZ which were failing on the strings. That's why I was trimming the strings before. What ended up working was this:
#"Wed, 17 Jul 2013 03:23:18 +0000"
Needs formatter of:
#"EEE, dd MMM yyyy HH:mm:ss ZZZ"
After having correctly set up the DateFormatter for this case I was easily able to figure out others for the other formats. It turns out there's only 3 different date formats being used in the projects and I can easily store the right date format string along side each data source and plug that into the date formatter when needed. No more string searching needed and all data is cleanly converting to the correct NSDate.
So the answer is, make a sample project that converts various date formats, plug in the ones you need and use the DateFormatter codes correctly. Chances are your date problems aren't as bad as you think and a better understanding of them can lead to cleaner code.
Using my example below you will see (null) if your format string is incorrect.
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *dateStrings = #[
// From data source 1.
#[#"2012-04-18",#"yyyy-MM-dd"],
// From data source 2.
#[#"2012-04-19 18:29:35",#"yyyy-MM-dd HH:mm:ss"],
#[#"2011-05-04 11:10:50",#"yyyy-MM-dd HH:mm:ss"],
// From RSS feeds (pubDate)
#[#"Wed, 17 Jul 2013 02:24:23 +0000",#"EEE, dd MMM yyyy HH:mm:ss ZZZ"],
#[#"Tue, 25 Jun 2013 15:04:49 +0000",#"EEE, dd MMM yyyy HH:mm:ss ZZZ"]
];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
for(NSArray *datePair in dateStrings){
[formatter setDateFormat:datePair[1]];
NSDate *thisDate = [formatter dateFromString:datePair[0]];
[formatter setDateStyle:NSDateFormatterMediumStyle];
NSLog(#"%# = %#",datePair[0],[formatter stringFromDate:thisDate]);
}
}
return 0;
}

NSDateFormatter completion

I have an NSTextField with NSDateFormatter. The formatter accepts 'mm/dd/yy'.
Is it possible to complete the date automatically? So the user can type 'mm' and the formatter completes the current month and year.
Any particular reason you don't want to use an NSDatePicker? (It does require 10.4 and later, but that's less of an issue these days)...
No.
You are going to need some logic that detects when "mm" is present in the text field and then get the current date from NSDate and then pass that into the date formatter to get the string and then display it in the text field.
You could also investigate the various delegate methods available to NSTextField. If my memory serves me right, there is didBeginEditing (which is inherited from NSControlTextEditingDelegate, check that class reference). However, I'm not sure if that fires every time you input a new character...though it may. In which case it'd be a case of assigning a delegate that checks the NSText string and does a little comparison.

Cocoa: Element count of property list without building dictionary?

What is the best way to count the number of entries in a property list?
I currently build a dictionary from the plist entries (*) and then use the dictionary's count:
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:myPlistPath];
NSDictionary *myPlistDict = (NSDictionary *) [NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
NSLog(#"There are %d entries in the plist.", [myPlistDict count]);
This strikes me as unnecessarily "heavy", but I was not able to find a more efficient solution. Any ideas?
(*) targeting 10.5 and therefore using the deprecated +propertyListFromData:… class method.
Well... if you're converting to XML anyway, you could use NSXMLNode's childCount method. The documentation does suggest that it's more efficient than calling [children count], but the creation of the NSXMLNode might make this just as bad (or even worse than) the NSDictionary method.
Have you profiled? Are you working with particularly large plists? Are you requesting this count often? I say: use NSDictionary, cache the value if you request it often, and move on unless this is unacceptably slow. (Yeah, it looks ugly right now, but there are bigger things to worry about.)

Resources