2 * Copyright (c) 2000-2004 Apple Computer, 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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
36 #include <CoreFoundation/CFDate.h>
37 #include <CoreFoundation/CFTimeZone.h>
44 namespace CSSMDateTimeUtils
47 #define UTC_TIME_NOSEC_LEN 11
48 #define UTC_TIME_STRLEN 13
49 #define GENERALIZED_TIME_STRLEN 15
50 #define LOCALIZED_UTC_TIME_STRLEN 17
51 #define LOCALIZED_TIME_STRLEN 19
52 #define MAX_TIME_STR_LEN 30
56 GetCurrentMacLongDateTime(sint64
&outMacDate
)
58 CFTimeZoneRef timeZone
= CFTimeZoneCopyDefault();
59 CFAbsoluteTime absTime
= CFAbsoluteTimeGetCurrent();
60 absTime
+= CFTimeZoneGetSecondsFromGMT(timeZone
, absTime
);
62 outMacDate
= sint64(double(absTime
+ kCFAbsoluteTimeIntervalSince1904
));
66 TimeStringToMacSeconds (const CSSM_DATA
&inUTCTime
, uint32
&ioMacDate
)
69 TimeStringToMacLongDateTime(inUTCTime
, ldt
);
70 ioMacDate
= uint32(ldt
);
74 * Given a CSSM_DATA containing either a UTC-style or "generalized time"
75 * time string, convert to 32-bit Mac time in seconds.
76 * Returns nonzero on error.
79 TimeStringToMacLongDateTime (const CSSM_DATA
&inUTCTime
, sint64
&outMacDate
)
89 ::memset( &date
, 0, sizeof(date
) );
91 if ((inUTCTime
.Data
== NULL
) || (inUTCTime
.Length
== 0))
93 MacOSError::throwMe(paramErr
);
96 /* tolerate NULL terminated or not */
97 len
= inUTCTime
.Length
;
98 if (inUTCTime
.Data
[len
- 1] == '\0')
103 case UTC_TIME_STRLEN
: // 2-digit year, not Y2K compliant
106 case GENERALIZED_TIME_STRLEN
: // 4-digit year
109 default: // unknown format
110 MacOSError::throwMe(paramErr
);
113 cp
= (char *)inUTCTime
.Data
;
115 /* check that all characters except last are digits */
116 for(i
=0; i
<(sint32
)(len
- 1); i
++) {
117 if ( !(isdigit(cp
[i
])) ) {
118 MacOSError::throwMe(paramErr
);
122 /* check last character is a 'Z' */
123 if(cp
[len
- 1] != 'Z' ) {
124 MacOSError::throwMe(paramErr
);
131 /* two more digits */
143 * 0 <= year <= 50 : assume century 21
144 * 50 < year < 70 : illegal per PKIX
145 * 70 < year <= 99 : assume century 20
151 MacOSError::throwMe(paramErr
);
153 /* else century 20, OK */
155 /* bug fix... we need to end up with a 4-digit year! */
158 /* by definition - tm_year is year - 1900 */
159 //tmp->tm_year = x - 1900;
167 /* in the string, months are from 1 to 12 */
168 if((x
> 12) || (x
<= 0)) {
169 MacOSError::throwMe(paramErr
);
171 /* in a tm, 0 to 11 */
172 //tmp->tm_mon = x - 1;
180 /* 1..31 in both formats */
181 if((x
> 31) || (x
<= 0)) {
182 MacOSError::throwMe(paramErr
);
192 if((x
> 23) || (x
< 0)) {
193 MacOSError::throwMe(paramErr
);
203 if((x
> 59) || (x
< 0)) {
204 MacOSError::throwMe(paramErr
);
214 if((x
> 59) || (x
< 0)) {
215 MacOSError::throwMe(paramErr
);
220 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
221 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(date
, timeZone
);
224 // Adjust abstime to local timezone
225 timeZone
= CFTimeZoneCopyDefault();
226 absTime
+= CFTimeZoneGetSecondsFromGMT(timeZone
, absTime
);
229 outMacDate
= sint64(double(absTime
+ kCFAbsoluteTimeIntervalSince1904
));
232 void MacSecondsToTimeString(uint32 inMacDate
, uint32 inLength
, void *outData
)
234 sint64 ldt
= sint64(uint64(inMacDate
));
235 MacLongDateTimeToTimeString(ldt
, inLength
, outData
);
238 void MacLongDateTimeToTimeString(const sint64
&inMacDate
,
239 uint32 inLength
, void *outData
)
241 // @@@ this code is close, but on the fringe case of a daylight savings time it will be off for a little while
242 CFAbsoluteTime absTime
= inMacDate
- kCFAbsoluteTimeIntervalSince1904
;
244 // Remove local timezone component from absTime
245 CFTimeZoneRef timeZone
= CFTimeZoneCopyDefault();
246 absTime
-= CFTimeZoneGetSecondsFromGMT(timeZone
, absTime
);
249 timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
250 CFGregorianDate date
= CFAbsoluteTimeGetGregorianDate(absTime
, timeZone
);
255 sprintf((char *)(outData
), "%04d%02d%02d%02d%02d%02dZ",
256 int(date
.year
% 10000), date
.month
, date
.day
,
257 date
.hour
, date
.minute
, int(date
.second
));
259 else if (inLength
== 14)
261 /* UTC - 2 year digits - code which parses this assumes that
262 * (2-digit) years between 0 and 49 are in century 21 */
263 sprintf((char *)(outData
), "%02d%02d%02d%02d%02d%02dZ",
264 int(date
.year
% 100), date
.month
, date
.day
,
265 date
.hour
, date
.minute
, int(date
.second
));
268 MacOSError::throwMe(paramErr
);
272 CFDateToCssmDate(CFDateRef date
, char *outCssmDate
)
274 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
275 CFGregorianDate gd
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(date
), timeZone
);
276 sprintf(outCssmDate
, "%04d%02d%02d%02d%02d%02dZ", (int)gd
.year
, gd
.month
, gd
.day
, gd
.hour
, gd
.minute
, (unsigned int)gd
.second
);
281 CssmDateToCFDate(const char *cssmDate
, CFDateRef
*outCFDate
)
283 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
285 unsigned int year
, month
, day
, hour
, minute
, second
;
286 sscanf(cssmDate
, "%4d%2d%2d%2d%2d%2d", &year
, &month
, &day
, &hour
, &minute
, &second
);
293 *outCFDate
= CFDateCreate(NULL
, CFGregorianDateGetAbsoluteTime(gd
, timeZone
));
298 CssmDateStringToCFDate(const char *cssmDate
, unsigned int len
, CFDateRef
*outCFDate
)
300 CFTimeZoneRef timeZone
;
304 unsigned isUtc
=0, isLocal
=0, x
, 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