private void parse(string s)
{
this.str = s;
this.pos = 0;
int len = pattern.Length;
bool skippedLast = false;
for (int i=0; i<len; ++i)
{
// character
int c = pattern[i];
// character count
int n = 1;
while (i+1<len && pattern[i+1] == c) { ++i; ++n; }
// switch
switch (c)
{
case 'Y':
year = parseInt(n);
if (year < 30) year += 2000;
else if (year < 100) year += 1900;
break;
case 'M':
switch (n)
{
case 4: mon = parseMon(); break;
case 3: mon = parseMon(); break;
default: mon = Month.array[parseInt(n)-1]; break;
}
break;
case 'D':
if (n != 3) day = parseInt(n);
else
{
// suffix like st, nd, th
day = parseInt(1);
skipWord();
}
break;
case 'h':
case 'k':
hour = parseInt(n);
break;
case 'm':
min = parseInt(n);
break;
case 's':
sec = parseInt(n);
break;
case 'S':
if (!skippedLast) sec = parseInt(n);
break;
case 'a':
case 'A':
int amPm = str[pos]; pos += n;
if (amPm == 'P' || amPm == 'p')
{
if (hour < 12) hour += 12;
}
else
{
if (hour == 12) hour = 0;
}
break;
case 'W':
skipWord();
break;
case 'f':
case 'F':
if (c == 'F' && skippedLast) break;
ns = 0;
int tenth = 100000000;
while (true)
{
int digit = parseOptDigit();
if (digit < 0) break;
ns += tenth * digit;
tenth /= 10;
}
break;
case 'z':
switch (n)
{
case 1: parseTzOffset(); break;
default: parseTzName(); break;
}
break;
case '\'':
while (true)
{
int expected = pattern[++i];
if (expected == '\'') break;
int actual = str[pos++];
if (actual != expected) throw new Exception();
}
break;
default:
int match = pos+1 < str.Length ? str[pos++] : 0;
// handle skipped symbols
if (i+1 < pattern.Length)
{
int next = pattern[i+1];
if (next == 'F' || next == 'S')
{
if (match != c) { skippedLast = true; --pos; break; }
}
}
skippedLast = false;
if (match != c) throw new Exception();
break;
}
}
}