Bug Summary

File:Other/Adium Spotlight Importer/../../Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.m
Location:line 236, column 4
Description:dead store

Annotated Source Code

1/*NSCalendarDate+ISO8601Parsing.m
2 *
3 *Created by Peter Hosey on 2006-02-20.
4 *Copyright 2006 Peter Hosey. All rights reserved.
5 */
6
7#include <ctype.h>
8#include <string.h>
9
10#import "NSCalendarDate+ISO8601Parsing.h"
11
12#ifndef DEFAULT_TIME_SEPARATOR
13# define DEFAULT_TIME_SEPARATOR ':'
14#endif
15unichar ISO8601ParserDefaultTimeSeparatorCharacter = DEFAULT_TIME_SEPARATOR':';
16
17static unsigned read_segment(const unsigned char *str, const unsigned char **next, unsigned *out_num_digits) {
18 unsigned num_digits = 0U;
19 unsigned value = 0U;
20
21 while(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
22 value *= 10U;
23 value += *str - '0';
24 ++num_digits;
25 ++str;
26 }
27
28 if(next) *next = str;
29 if(out_num_digits) *out_num_digits = num_digits;
30
31 return value;
32}
33static unsigned read_segment_4digits(const unsigned char *str, const unsigned char **next, unsigned *out_num_digits) {
34 unsigned num_digits = 0U;
35 unsigned value = 0U;
36
37 if(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
38 value += *(str++) - '0';
39 ++num_digits;
40 }
41
42 if(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
43 value *= 10U;
44 value += *(str++) - '0';
45 ++num_digits;
46 }
47
48 if(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
49 value *= 10U;
50 value += *(str++) - '0';
51 ++num_digits;
52 }
53
54 if(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
55 value *= 10U;
56 value += *(str++) - '0';
57 ++num_digits;
58 }
59
60 if(next) *next = str;
61 if(out_num_digits) *out_num_digits = num_digits;
62
63 return value;
64}
65static unsigned read_segment_2digits(const unsigned char *str, const unsigned char **next) {
66 unsigned value = 0U;
67
68 if(isdigit__isctype ( ( * str ) , 0x00000400L )(*str))
69 value += *str - '0';
70
71 if(isdigit__isctype ( ( * ++ str ) , 0x00000400L )(*++str)) {
72 value *= 10U;
73 value += *(str++) - '0';
74 }
75
76 if(next) *next = str;
77
78 return value;
79}
80
81//strtod doesn't support ',' as a separator. This does.
82static double read_double(const unsigned char *str, const unsigned char **next) {
83 double value = 0.0;
84
85 if(str) {
86 unsigned int_value = 0;
87
88 while(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
89 int_value *= 10U;
90 int_value += (*(str++) - '0');
91 }
92 value = int_value;
93
94 if(((*str == ',') || (*str == '.'))) {
95 ++str;
96
97 register double multiplier, multiplier_multiplier;
98 multiplier = multiplier_multiplier = 0.1;
99
100 while(isdigit__isctype ( ( * str ) , 0x00000400L )(*str)) {
101 value += (*(str++) - '0') * multiplier;
102 multiplier *= multiplier_multiplier;
103 }
104 }
105 }
106
107 if(next) *next = str;
108
109 return value;
110}
111
112static BOOL is_leap_year(unsigned year) {
113 return \
114 ((year % 4U) == 0U)
115 && (((year % 100U) != 0U)
116 || ((year % 400U) == 0U));
117}
118
119@implementation NSCalendarDate(ISO8601Parsing)
120
121/*Valid ISO 8601 date formats:
122 *
123 *YYYYMMDD
124 *YYYY-MM-DD
125 *YYYY-MM
126 *YYYY
127 *YY //century
128 * //Implied century: YY is 00-99
129 * YYMMDD
130 * YY-MM-DD
131 * -YYMM
132 * -YY-MM
133 * -YY
134 * //Implied year
135 * --MMDD
136 * --MM-DD
137 * --MM
138 * //Implied year and month
139 * ---DD
140 * //Ordinal dates: DDD is the number of the day in the year (1-366)
141 *YYYYDDD
142 *YYYY-DDD
143 * YYDDD
144 * YY-DDD
145 * -DDD
146 * //Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week
147 *yyyyWwwd
148 *yyyy-Www-d
149 *yyyyWww
150 *yyyy-Www
151 *yyWwwd
152 *yy-Www-d
153 *yyWww
154 *yy-Www
155 * //Year of the implied decade
156 *-yWwwd
157 *-y-Www-d
158 *-yWww
159 *-y-Www
160 * //Week and day of implied year
161 * -Wwwd
162 * -Www-d
163 * //Week only of implied year
164 * -Www
165 * //Day only of implied week
166 * -W-d
167 */
168+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict timeSeparator:(unichar)timeSep getRange:(out NSRange *)outRange {
169 if (!str || ![str length]) {
170 if (outRange) {
171 outRange->location = NSNotFound;
172 outRange->length = 0U;
173 }
174
175 return nil0;
176 }
177
178 NSCalendarDate *now = [NSCalendarDate calendarDate];
179 unsigned
180 //Date
181 year = 0U,
182 month_or_week = 0U,
183 day = 0U,
184 //Time
185 hour = 0U;
186 NSTimeInterval
187 minute = 0.0,
188 second = 0.0;
189 //Time zone
190 signed tz_hour = 0;
191 signed tz_minute = 0;
192
193 enum {
194 monthAndDate,
195 week,
196 dateOnly
197 } dateSpecification = monthAndDate;
198
199 if(strict) timeSep = ISO8601ParserDefaultTimeSeparatorCharacter;
200 NSAssertdo { if ( ! ( ( timeSep != '\0' ) ) ) { [ [ NSAssertionHandler
currentHandler ] handleFailureInMethod : _cmd object : self file
: [ NSString stringWithCString : "/Users/ryan/Projects/Adium/Other/Adium Spotlight Importer/../../Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.m"
] lineNumber : 200 description : ( ( @ "Time separator must not be NUL."
) ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) ] ; } } while ( 0
)
(timeSep != '\0', @"Time separator must not be NUL.");
201
202 BOOL isValidDate = ([str length] > 0U);
203 NSTimeZone *timeZone = nil0;
204 NSCalendarDate *date = nil0;
205
206 const unsigned char *ch = (const unsigned char *)[str UTF8String];
207
208 NSRange range = { 0U, 0U };
209 const unsigned char *start_of_date = 0;
210 if (strict && isspace__istype ( ( * ch ) , 0x00004000L )(*ch)) {
211 range.location = NSNotFound;
212 isValidDate = NO( BOOL ) 0;
213 } else {
214 //Skip leading whitespace.
215 unsigned i = 0U;
216 for(unsigned len = strlen((const char *)ch); i < len; ++i) {
217 if(!isspace__istype ( ( ch [ i ] ) , 0x00004000L )(ch[i]))
218 break;
219 }
220
221 range.location = i;
222 ch += i;
223 start_of_date = ch;
224
225 unsigned segment;
226 unsigned num_leading_hyphens = 0U, num_digits = 0U;
227
228 if (*ch == 'T') {
229 //There is no date here, only a time. Set the date to now; then we'll parse the time.
230 isValidDate = isdigit__isctype ( ( * ++ ch ) , 0x00000400L )(*++ch);
231
232 year = [now yearOfCommonEra];
233 month_or_week = [now monthOfYear];
234 day = [now dayOfMonth];
235 } else {
Value stored to 'segment' is never read
236 segment = 0U;
237
238 while(*ch == '-') {
239 ++num_leading_hyphens;
240 ++ch;
241 }
242
243 segment = read_segment(ch, &ch, &num_digits);
244 switch (num_digits) {
245 case 0:
246 if(*ch == 'W') {
247 if((ch[1] == '-') && isdigit__isctype ( ( ch [ 2 ] ) , 0x00000400L )(ch[2]) && ((num_leading_hyphens == 1U) || ((num_leading_hyphens == 2U) && !strict))) {
248 year = [now yearOfCommonEra];
249 month_or_week = 1U;
250 ch += 2;
251 goto parseDayAfterWeek;
252 } else if(num_leading_hyphens == 1U) {
253 year = [now yearOfCommonEra];
254 goto parseWeekAndDay;
255 } else
256 isValidDate = NO( BOOL ) 0;
257 } else
258 isValidDate = NO( BOOL ) 0;
259 break;
260
261 case 8: //YYYY MM DD
262 if(num_leading_hyphens > 0U)
263 isValidDate = NO( BOOL ) 0;
264 else {
265 day = segment % 100U;
266 segment /= 100U;
267 month_or_week = segment % 100U;
268 year = segment / 100U;
269 }
270 break;
271
272 case 6: //YYMMDD (implicit century)
273 if(num_leading_hyphens > 0U)
274 isValidDate = NO( BOOL ) 0;
275 else {
276 day = segment % 100U;
277 segment /= 100U;
278 month_or_week = segment % 100U;
279 year = [now yearOfCommonEra];
280 year -= (year % 100U);
281 year += segment / 100U;
282 }
283 break;
284
285 case 4:
286 switch(num_leading_hyphens) {
287 case 0: //YYYY
288 year = segment;
289
290 if(*ch == '-') ++ch;
291
292 if(!isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch)) {
293 if(*ch == 'W')
294 goto parseWeekAndDay;
295 else
296 month_or_week = day = 1U;
297 } else {
298 segment = read_segment(ch, &ch, &num_digits);
299 switch(num_digits) {
300 case 4: //MMDD
301 day = segment % 100U;
302 month_or_week = segment / 100U;
303 break;
304
305 case 2: //MM
306 month_or_week = segment;
307
308 if(*ch == '-') ++ch;
309 if(!isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch))
310 day = 1U;
311 else
312 day = read_segment(ch, &ch, NULL( ( void * ) 0 ));
313 break;
314
315 case 3: //DDD
316 day = segment % 1000U;
317 dateSpecification = dateOnly;
318 if(strict && (day > (365U + is_leap_year(year))))
319 isValidDate = NO( BOOL ) 0;
320 break;
321
322 default:
323 isValidDate = NO( BOOL ) 0;
324 }
325 }
326 break;
327
328 case 1: //YYMM
329 month_or_week = segment % 100U;
330 year = segment / 100U;
331
332 if(*ch == '-') ++ch;
333 if(!isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch))
334 day = 1U;
335 else
336 day = read_segment(ch, &ch, NULL( ( void * ) 0 ));
337
338 break;
339
340 case 2: //MMDD
341 day = segment % 100U;
342 month_or_week = segment / 100U;
343 year = [now yearOfCommonEra];
344
345 break;
346
347 default:
348 isValidDate = NO( BOOL ) 0;
349 } //switch(num_leading_hyphens) (4 digits)
350 break;
351
352 case 1:
353 if(strict) {
354 //Two digits only - never just one.
355 if(num_leading_hyphens == 1U) {
356 if(*ch == '-') ++ch;
357 if(*++ch == 'W') {
358 year = [now yearOfCommonEra];
359 year -= (year % 10U);
360 year += segment;
361 goto parseWeekAndDay;
362 } else
363 isValidDate = NO( BOOL ) 0;
364 } else
365 isValidDate = NO( BOOL ) 0;
366 break;
367 }
368 case 2:
369 switch(num_leading_hyphens) {
370 case 0:
371 if(*ch == '-') {
372 //Implicit century
373 year = [now yearOfCommonEra];
374 year -= (year % 100U);
375 year += segment;
376
377 if(*++ch == 'W')
378 goto parseWeekAndDay;
379 else if(!isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch)) {
380 goto centuryOnly;
381 } else {
382 //Get month and/or date.
383 segment = read_segment_4digits(ch, &ch, &num_digits);
384 NSLog(@"(%@) parsing month; segment is %u and ch is %s", str, segment, ch);
385 switch(num_digits) {
386 case 4: //YY-MMDD
387 day = segment % 100U;
388 month_or_week = segment / 100U;
389 break;
390
391 case 1: //YY-M; YY-M-DD (extension)
392 if(strict) {
393 isValidDate = NO( BOOL ) 0;
394 break;
395 }
396 case 2: //YY-MM; YY-MM-DD
397 month_or_week = segment;
398 if(*ch == '-') {
399 if(isdigit__isctype ( ( * ++ ch ) , 0x00000400L )(*++ch))
400 day = read_segment_2digits(ch, &ch);
401 else
402 day = 1U;
403 } else
404 day = 1U;
405 break;
406
407 case 3: //Ordinal date.
408 day = segment;
409 dateSpecification = dateOnly;
410 break;
411 }
412 }
413 } else if(*ch == 'W') {
414 year = [now yearOfCommonEra];
415 year -= (year % 100U);
416 year += segment;
417
418 parseWeekAndDay: //*ch should be 'W' here.
419 if(!isdigit__isctype ( ( * ++ ch ) , 0x00000400L )(*++ch)) {
420 //Not really a week-based date; just a year followed by '-W'.
421 if(strict)
422 isValidDate = NO( BOOL ) 0;
423 else
424 month_or_week = day = 1U;
425 } else {
426 month_or_week = read_segment_2digits(ch, &ch);
427 if(*ch == '-') ++ch;
428 parseDayAfterWeek:
429 day = isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch) ? read_segment_2digits(ch, &ch) : 1U;
430 dateSpecification = week;
431 }
432 } else {
433 //Century only. Assume current year.
434 centuryOnly:
435 year = segment * 100U + [now yearOfCommonEra] % 100U;
436 month_or_week = day = 1U;
437 }
438 break;
439
440 case 1:; //-YY; -YY-MM (implicit century)
441 NSLog(@"(%@) found %u digits and one hyphen, so this is either -YY or -YY-MM; segment (year) is %u", str, num_digits, segment);
442 unsigned current_year = [now yearOfCommonEra];
443 unsigned century = (current_year % 100U);
444 year = segment + (current_year - century);
445 if(num_digits == 1U) //implied decade
446 year += century - (current_year % 10U);
447
448 if(*ch == '-') {
449 ++ch;
450 month_or_week = read_segment_2digits(ch, &ch);
451 NSLog(@"(%@) month is %u", str, month_or_week);
452 }
453
454 day = 1U;
455 break;
456
457 case 2: //--MM; --MM-DD
458 year = [now yearOfCommonEra];
459 month_or_week = segment;
460 if(*ch == '-') {
461 ++ch;
462 day = read_segment_2digits(ch, &ch);
463 }
464 break;
465
466 case 3: //---DD
467 year = [now yearOfCommonEra];
468 month_or_week = [now monthOfYear];
469 day = segment;
470 break;
471
472 default:
473 isValidDate = NO( BOOL ) 0;
474 } //switch(num_leading_hyphens) (2 digits)
475 break;
476
477 case 7: //YYYY DDD (ordinal date)
478 if(num_leading_hyphens > 0U)
479 isValidDate = NO( BOOL ) 0;
480 else {
481 day = segment % 1000U;
482 year = segment / 1000U;
483 dateSpecification = dateOnly;
484 if(strict && (day > (365U + is_leap_year(year))))
485 isValidDate = NO( BOOL ) 0;
486 }
487 break;
488
489 case 3: //--DDD (ordinal date, implicit year)
490 //Technically, the standard only allows one hyphen. But it says that two hyphens is the logical implementation, and one was dropped for brevity. So I have chosen to allow the missing hyphen.
491 if((num_leading_hyphens < 1U) || ((num_leading_hyphens > 2U) && !strict))
492 isValidDate = NO( BOOL ) 0;
493 else {
494 day = segment;
495 year = [now yearOfCommonEra];
496 dateSpecification = dateOnly;
497 if(strict && (day > (365U + is_leap_year(year))))
498 isValidDate = NO( BOOL ) 0;
499 }
500 break;
501
502 default:
503 isValidDate = NO( BOOL ) 0;
504 }
505 }
506
507 if (isValidDate) {
508 if (isspace__istype ( ( * ch ) , 0x00004000L )(*ch) || (*ch == 'T')) ++ch;
509
510 if (isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch)) {
511 hour = read_segment_2digits(ch, &ch);
512 if(*ch == timeSep) {
513 ++ch;
514 if((timeSep == ',') || (timeSep == '.')) {
515 //We can't do fractional minutes when '.' is the segment separator.
516 //Only allow whole minutes and whole seconds.
517 minute = read_segment_2digits(ch, &ch);
518 if(*ch == timeSep) {
519 ++ch;
520 second = read_segment_2digits(ch, &ch);
521 }
522 } else {
523 //Allow a fractional minute.
524 //If we don't get a fraction, look for a seconds segment.
525 //Otherwise, the fraction of a minute is the seconds.
526 minute = read_double(ch, &ch);
527 second = modf(minute, &minute);
528 if(second > DBL_EPSILON2.2204460492503131e-16)
529 second *= 60.0; //Convert fraction (e.g. .5) into seconds (e.g. 30).
530 else if(*ch == timeSep) {
531 ++ch;
532 second = read_double(ch, &ch);
533 }
534 }
535 }
536
537 switch(*ch) {
538 case 'Z':
539 timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
540 break;
541
542 case '+':
543 case '-':;
544 BOOL negative = (*ch == '-');
545 if(isdigit__isctype ( ( * ++ ch ) , 0x00000400L )(*++ch)) {
546 //Read hour offset.
547 segment = *ch - '0';
548 if(isdigit__isctype ( ( * ++ ch ) , 0x00000400L )(*++ch)) {
549 segment *= 10U;
550 segment += *(ch++) - '0';
551 }
552 tz_hour = (signed)segment;
553 if(negative) tz_hour = -tz_hour;
554
555 //Optional separator.
556 if(*ch == timeSep) ++ch;
557
558 if(isdigit__isctype ( ( * ch ) , 0x00000400L )(*ch)) {
559 //Read minute offset.
560 segment = *ch - '0';
561 if(isdigit__isctype ( ( * ++ ch ) , 0x00000400L )(*++ch)) {
562 segment *= 10U;
563 segment += *ch - '0';
564 }
565 tz_minute = segment;
566 if(negative) tz_minute = -tz_minute;
567 }
568
569 timeZone = [NSTimeZone timeZoneForSecondsFromGMT:(tz_hour * 3600) + (tz_minute * 60)];
570 }
571 }
572 }
573 }
574
575 if (isValidDate) {
576 switch (dateSpecification) {
577 case monthAndDate:
578 date = [NSCalendarDate dateWithYear:year
579 month:month_or_week
580 day:day
581 hour:hour
582 minute:minute
583 second:second
584 timeZone:timeZone];
585 break;
586
587 case week:;
588 //Adapted from <http://personal.ecu.edu/mccartyr/ISOwdALG.txt>.
589 //This works by converting the week date into an ordinal date, then letting the next case handle it.
590 unsigned prevYear = year - 1U;
591 unsigned YY = prevYear % 100U;
592 unsigned C = prevYear - YY;
593 unsigned G = YY + YY / 4U;
594 unsigned isLeapYear = (((C / 100U) % 4U) * 5U);
595 unsigned Jan1Weekday = (isLeapYear + G) % 7U;
596 enum { monday, tuesday, wednesday, thursday/*, friday, saturday, sunday*/ };
597 day = ((8U - Jan1Weekday) + (7U * (Jan1Weekday > thursday))) + (day - 1U) + (7U * (month_or_week - 2));
598
599 case dateOnly: //An "ordinal date".
600 date = [NSCalendarDate dateWithYear:year
601 month:1
602 day:1
603 hour:hour
604 minute:minute
605 second:second
606 timeZone:timeZone];
607 date = [date dateByAddingYears:0
608 months:0
609 days:(day - 1)
610 hours:0
611 minutes:0
612 seconds:0];
613 break;
614 }
615 }
616 } //if (!(strict && isdigit(ch[0])))
617
618 if(outRange) {
619 if(isValidDate)
620 range.length = ch - start_of_date;
621 else
622 range.location = NSNotFound;
623
624 *outRange = range;
625 }
626
627 return date;
628}
629
630+ (NSCalendarDate *)calendarDateWithString:(NSString *)str {
631 return [self calendarDateWithString:str strictly:NO( BOOL ) 0 getRange:NULL( ( void * ) 0 )];
632}
633+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict {
634 return [self calendarDateWithString:str strictly:strict getRange:NULL( ( void * ) 0 )];
635}
636+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict getRange:(out NSRange *)outRange {
637 return [self calendarDateWithString:str strictly:strict timeSeparator:ISO8601ParserDefaultTimeSeparatorCharacter getRange:NULL( ( void * ) 0 )];
638}
639
640+ (NSCalendarDate *)calendarDateWithString:(NSString *)str timeSeparator:(unichar)timeSep getRange:(out NSRange *)outRange {
641 return [self calendarDateWithString:str strictly:NO( BOOL ) 0 timeSeparator:timeSep getRange:outRange];
642}
643+ (NSCalendarDate *)calendarDateWithString:(NSString *)str timeSeparator:(unichar)timeSep {
644 return [self calendarDateWithString:str strictly:NO( BOOL ) 0 timeSeparator:timeSep getRange:NULL( ( void * ) 0 )];
645}
646+ (NSCalendarDate *)calendarDateWithString:(NSString *)str getRange:(out NSRange *)outRange {
647 return [self calendarDateWithString:str strictly:NO( BOOL ) 0 timeSeparator:ISO8601ParserDefaultTimeSeparatorCharacter getRange:outRange];
648}
649
650@end