2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 * cssmdatetime.cpp -- CSSM date and time utilities for the Mac
27 #define _CPP_CSSM_DATE_TIME_UTILS
30 #include "cssmdatetime.h"
34 #include <security_utilities/errors.h>
35 #include <CoreFoundation/CFDate.h>
36 #include <CoreFoundation/CFTimeZone.h>
43 namespace CSSMDateTimeUtils
46 #define UTC_TIME_NOSEC_LEN 11
47 #define UTC_TIME_STRLEN 13
48 #define GENERALIZED_TIME_STRLEN 15
49 #define LOCALIZED_UTC_TIME_STRLEN 17
50 #define LOCALIZED_TIME_STRLEN 19
51 #define MAX_TIME_STR_LEN 30
55 GetCurrentMacLongDateTime(sint64
&outMacDate
)
57 CFTimeZoneRef timeZone
= CFTimeZoneCopyDefault();
58 CFAbsoluteTime absTime
= CFAbsoluteTimeGetCurrent();
59 absTime
+= CFTimeZoneGetSecondsFromGMT(timeZone
, absTime
);
61 outMacDate
= sint64(double(absTime
+ kCFAbsoluteTimeIntervalSince1904
));
65 TimeStringToMacSeconds (const CSSM_DATA
&inUTCTime
, uint32
&ioMacDate
)
68 TimeStringToMacLongDateTime(inUTCTime
, ldt
);
69 ioMacDate
= uint32(ldt
);
73 * Given a CSSM_DATA containing either a UTC-style or "generalized time"
74 * time string, convert to 32-bit Mac time in seconds.
75 * Returns nonzero on error.
78 TimeStringToMacLongDateTime (const CSSM_DATA
&inUTCTime
, sint64
&outMacDate
)
88 ::memset( &date
, 0, sizeof(date
) );
90 if ((inUTCTime
.Data
== NULL
) || (inUTCTime
.Length
== 0))
92 MacOSError::throwMe(errSecParam
);
95 /* tolerate NULL terminated or not */
96 len
= inUTCTime
.Length
;
97 if (inUTCTime
.Data
[len
- 1] == '\0')
102 case UTC_TIME_STRLEN
: // 2-digit year, not Y2K compliant
105 case GENERALIZED_TIME_STRLEN
: // 4-digit year
108 default: // unknown format
109 MacOSError::throwMe(errSecParam
);
112 cp
= (char *)inUTCTime
.Data
;
114 /* check that all characters except last are digits */
115 for(i
=0; i
<(sint32
)(len
- 1); i
++) {
116 if ( !(isdigit(cp
[i
])) ) {
117 MacOSError::throwMe(errSecParam
);
121 /* check last character is a 'Z' */
122 if(cp
[len
- 1] != 'Z' ) {
123 MacOSError::throwMe(errSecParam
);
130 /* two more digits */
142 * 0 <= year <= 50 : assume century 21
143 * 50 < year < 70 : illegal per PKIX
144 * 70 < year <= 99 : assume century 20
150 MacOSError::throwMe(errSecParam
);
152 /* else century 20, OK */
154 /* bug fix... we need to end up with a 4-digit year! */
157 /* by definition - tm_year is year - 1900 */
158 //tmp->tm_year = x - 1900;
166 /* in the string, months are from 1 to 12 */
167 if((x
> 12) || (x
<= 0)) {
168 MacOSError::throwMe(errSecParam
);
170 /* in a tm, 0 to 11 */
171 //tmp->tm_mon = x - 1;
179 /* 1..31 in both formats */
180 if((x
> 31) || (x
<= 0)) {
181 MacOSError::throwMe(errSecParam
);
191 if((x
> 23) || (x
< 0)) {
192 MacOSError::throwMe(errSecParam
);
202 if((x
> 59) || (x
< 0)) {
203 MacOSError::throwMe(errSecParam
);
213 if((x
> 59) || (x
< 0)) {
214 MacOSError::throwMe(errSecParam
);
219 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
220 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(date
, timeZone
);
223 // Adjust abstime to local timezone
224 timeZone
= CFTimeZoneCopyDefault();
225 absTime
+= CFTimeZoneGetSecondsFromGMT(timeZone
, absTime
);
228 outMacDate
= sint64(double(absTime
+ kCFAbsoluteTimeIntervalSince1904
));
231 void MacSecondsToTimeString(uint32 inMacDate
, uint32 inLength
, void *outData
)
233 sint64 ldt
= sint64(uint64(inMacDate
));
234 MacLongDateTimeToTimeString(ldt
, inLength
, outData
);
237 void MacLongDateTimeToTimeString(const sint64
&inMacDate
,
238 uint32 inLength
, void *outData
)
240 // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
241 CFAbsoluteTime absTime
= inMacDate
- kCFAbsoluteTimeIntervalSince1904
;
243 // Remove local timezone component from absTime
244 CFTimeZoneRef timeZone
= CFTimeZoneCopyDefault();
245 absTime
-= CFTimeZoneGetSecondsFromGMT(timeZone
, absTime
);
248 timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
249 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(absTime
, timeZone
);
254 sprintf((char *)(outData
), "%04d%02d%02d%02d%02d%02dZ",
255 int(date
.year
% 10000), date
.month
, date
.day
,
256 date
.hour
, date
.minute
, int(date
.second
));
258 else if (inLength
== 14)
260 /* UTC - 2 year digits - code which parses this assumes that
261 * (2-digit) years between 0 and 49 are in century 21 */
262 sprintf((char *)(outData
), "%02d%02d%02d%02d%02d%02dZ",
263 int(date
.year
% 100), date
.month
, date
.day
,
264 date
.hour
, date
.minute
, int(date
.second
));
267 MacOSError::throwMe(errSecParam
);
271 CFDateToCssmDate(CFDateRef date
, char *outCssmDate
)
273 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
274 CFGregorianDate gd
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date
), timeZone
);
275 sprintf(outCssmDate
, "%04d%02d%02d%02d%02d%02dZ", (int)gd
.year
, gd
.month
, gd
.day
, gd
.hour
, gd
.minute
, (unsigned int)gd
.second
);
280 CssmDateToCFDate(const char *cssmDate
, CFDateRef
*outCFDate
)
282 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
284 unsigned int year
, month
, day
, hour
, minute
, second
;
285 sscanf(cssmDate
, "%4d%2d%2d%2d%2d%2d", &year
, &month
, &day
, &hour
, &minute
, &second
);
292 *outCFDate
= CFDateCreate(NULL
, CFGregorianDateGetAbsoluteTime(gd
, timeZone
));
297 CssmDateStringToCFDate(const char *cssmDate
, unsigned int len
, CFDateRef
*outCFDate
)
299 CFTimeZoneRef timeZone
;
303 unsigned isUtc
=0, isLocal
=0, i
;
305 unsigned noSeconds
=0;
308 if((cssmDate
== NULL
) || (len
== 0) || (outCFDate
== NULL
))
311 /* tolerate NULL terminated or not */
312 if(cssmDate
[len
- 1] == '\0')
316 case UTC_TIME_NOSEC_LEN
: // 2-digit year, no seconds, not y2K compliant
320 case UTC_TIME_STRLEN
: // 2-digit year, not Y2K compliant
323 case GENERALIZED_TIME_STRLEN
: // 4-digit year
326 case LOCALIZED_UTC_TIME_STRLEN
: // "YYMMDDhhmmssThhmm" (where T=[+,-])
328 // deliberate fallthrough
329 case LOCALIZED_TIME_STRLEN
: // "YYYYMMDDhhmmssThhmm" (where T=[+,-])
332 default: // unknown format
336 cp
= (char *)cssmDate
;
338 /* check that all characters except last (or timezone indicator, if localized) are digits */
339 for(i
=0; i
<(len
- 1); i
++) {
340 if ( !(isdigit(cp
[i
])) )
341 if ( !isLocal
|| !(cp
[i
]=='+' || cp
[i
]=='-') )
344 /* check last character is a 'Z', unless localized */
345 if(!isLocal
&& cp
[len
- 1] != 'Z' ) {
353 /* two more digits */
365 * 0 <= year < 50 : assume century 21
366 * 50 <= year < 70 : illegal per PKIX
367 * 70 < year <= 99 : assume century 20
387 /* in the string, months are from 1 to 12 */
388 if((x
> 12) || (x
<= 0)) {
398 /* 1..31 in both formats */
399 if((x
> 31) || (x
<= 0)) {
409 if((x
> 23) || (x
< 0)) {
419 if((x
> 59) || (x
< 0)) {
433 if((x
> 59) || (x
< 0)) {
441 ti
= (*cp
++ == '+') ? 1 : -1;
446 x
= atoi( szTemp
) * 60 * 60;
452 x
= atoi( szTemp
) * 60;
453 ti
+= ((ti
< 0) ? (x
*-1) : x
);
455 timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, ti
);
456 if (!timeZone
) return 1;
457 *outCFDate
= CFDateCreate(NULL
, CFGregorianDateGetAbsoluteTime(gd
, timeZone
));
463 }; // end namespace CSSMDateTimeUtils
465 } // end namespace Security