NSLog(@"(%@) parsing month; segment is %u and ch is %s", string, segment, ch);
switch(num_digits) {
case 4: //YY-MMDD
day = segment % 100U;
month_or_week = segment / 100U;
break;
case 1: //YY-M; YY-M-DD (extension)
if (strict) {
isValidDate = NO;
break;
}
case 2: //YY-MM; YY-MM-DD
month_or_week = segment;
if (*ch == '-') {
if (isdigit(*++ch))
day = read_segment_2digits(ch, &ch);
else
day = 1U;
} else
day = 1U;
break;
case 3: //Ordinal date.
day = segment;
dateSpecification = dateOnly;
break;
}
}
} else if (*ch == 'W') {
year = nowComponents.year;
year -= (year % 100U);
year += segment;
parseWeekAndDay: //*ch should be 'W' here.
if (!isdigit(*++ch)) {
//Not really a week-based date; just a year followed by '-W'.
if (strict)
isValidDate = NO;
else
month_or_week = day = 1U;
} else {
month_or_week = read_segment_2digits(ch, &ch);
if (*ch == '-') ++ch;
parseDayAfterWeek:
day = isdigit(*ch) ? read_segment_2digits(ch, &ch) : 1U;
dateSpecification = week;
}
} else {
//Century only. Assume current year.
centuryOnly:
year = segment * 100U + nowComponents.year % 100U;
month_or_week = day = 1U;
}
break;
case 1:; //-YY; -YY-MM (implicit century)
NSLog(@"(%@) found %u digits and one hyphen, so this is either -YY or -YY-MM; segment (year) is %u", string, num_digits, segment);
unsigned current_year = nowComponents.year;
unsigned century = (current_year % 100U);
year = segment + (current_year - century);
if (num_digits == 1U) //implied decade
year += century - (current_year % 10U);
if (*ch == '-') {
++ch;
month_or_week = read_segment_2digits(ch, &ch);
NSLog(@"(%@) month is %u", string, month_or_week);
}
day = 1U;
break;
case 2: //--MM; --MM-DD
year = nowComponents.year;
month_or_week = segment;
if (*ch == '-') {
++ch;
day = read_segment_2digits(ch, &ch);
}
break;
case 3: //---DD
year = nowComponents.year;
month_or_week = nowComponents.month;
day = segment;
break;
default:
isValidDate = NO;
} //switch(num_leading_hyphens) (2 digits)
break;
case 7: //YYYY DDD (ordinal date)
if (num_leading_hyphens > 0U)
isValidDate = NO;
else {
day = segment % 1000U;
year = segment / 1000U;
dateSpecification = dateOnly;
if (strict && (day > (365U + is_leap_year(year))))
isValidDate = NO;
}
break;
case 3: //--DDD (ordinal date, implicit year)
//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.
if ((num_leading_hyphens < 1U) || ((num_leading_hyphens > 2U) && !strict))
isValidDate = NO;
else {
day = segment;
year = nowComponents.year;
dateSpecification = dateOnly;
if (strict && (day > (365U + is_leap_year(year))))
isValidDate = NO;
}
break;
default:
isValidDate = NO;
}
}
if (isValidDate) {
if (isspace(*ch) || (*ch == 'T')) ++ch;
if (isdigit(*ch)) {
hour = read_segment_2digits(ch, &ch);
if (*ch == timeSep) {
++ch;
if ((timeSep == ',') || (timeSep == '.')) {
//We can't do fractional minutes when '.' is the segment separator.
//Only allow whole minutes and whole seconds.
minute = read_segment_2digits(ch, &ch);
if (*ch == timeSep) {
++ch;
second = read_segment_2digits(ch, &ch);
}
} else {
//Allow a fractional minute.
//If we don't get a fraction, look for a seconds segment.
//Otherwise, the fraction of a minute is the seconds.
minute = read_double(ch, &ch);
second = modf(minute, &minute);
if (second > DBL_EPSILON)
second *= 60.0; //Convert fraction (e.g. .5) into seconds (e.g. 30).