internal static double ParseDate(String str){
long Nil = 0x80000000;
int AmPm = 0;
int BcAd = 0;
Ps ps = Ps.Initial;
long Year = Nil;
long Month = Nil;
long Date = Nil;
long Time = Nil;
long Zone = Nil;
long Offset = Nil;
str = str.ToLowerInvariant();
int i = 0, n = str.Length;
while (i < n){
char ch = str[i++];
if (ch <= ' ')
continue;
switch (ch){
case '(':
// skip stuff in parens
for (int depth = 1; i < n; ){
ch = str[i++];
if (ch == '(')
depth++;
else if (ch == ')' && --depth <= 0)
break;
}
continue;
case ',':
continue;
case ':':
continue;
case '/':
continue;
case '+':
if (Nil != Year)
ps = Ps.AddOffset;
continue;
case '-':
if (Nil != Year)
ps = Ps.SubOffset;
continue;
default:
break;
}
if (isalpha(ch)){ //scan an alpha token
int j = i-1; //remember start of token
while (i < n){
ch = str[i++];
if (isalpha(ch) || '.' == ch) continue;
break;
}
int cch = i - j - (i < n ? 1 : 0);
if ('.' == str[i-(i < n ? 2 : 1)]) //Exclude a trailing dot from the token
cch--;
// skip over any spaces
while (ch == ' ' && i < n) ch = str[i++];
// have an alphabetic token - look it up
if (1 == cch){
// military version of time zone
// z = GMT
// j isn't used
// a to m are -1 to -12
// n to y are 1 to 12
if (Nil != Zone)
return Double.NaN;
char chj = str[j];
if (chj <= 'm'){
if (chj == 'j' || chj < 'a')
return Double.NaN;
Zone = -(long)(chj - 'a' + (chj < 'j'?1:0)) * 60;
}else if (chj <= 'y')
Zone = (long)(chj - 'm') * 60;
else if (chj == 'z')
Zone = 0;
else
return Double.NaN;
// look for a time zone offset
if ('+' == ch){
ps = Ps.AddOffset;
}else if ('-' == ch){
ps = Ps.SubOffset;
}else
ps = Ps.Initial;
continue;
}
for (int kk = Strings.Length-1; kk >= 0; kk--){
String tkstr = Strings[kk];
if (tkstr.Length < cch) continue;
if (0 != String.CompareOrdinal(str, j, tkstr, 0, cch)){
if (kk == 0) return Double.NaN; //The current token does not match anything. Date string cannot be parsed by this routine.
}else{
switch (Tokens[kk]){
case Tk.BcAd :
if (0 != BcAd) return Double.NaN;
BcAd = Values[kk];
break;
case Tk.AmPm :
if (0 != AmPm) return Double.NaN;
AmPm = Values[kk];
break;
case Tk.Month :
if (Nil != Month) return Double.NaN;
Month = Values[kk];
break;
case Tk.Zone :
if (Nil != Zone) return Double.NaN;
Zone = Values[kk];
// look for a time zone offset
if ('+' == ch){
ps = Ps.AddOffset; i++;
}else if ('-' == ch){
ps = Ps.SubOffset; i++;
}else
ps = Ps.Initial;
break;
}
break; //out of the loop matching the current token with known tokens
}
}
if (i < n) i--;
continue; //with overall loop, looking for next bit of date
}
if (!isdigit(ch))
return Double.NaN;
int T = 0, k = i;
do{
T = T * 10 + ch - '0';
if (i >= n) break;
ch = str[i++];
}while (isdigit(ch));
// to avoid overflow
if (i - k > 6)
return Double.NaN;
// skip over any spaces
while (ch == ' ' && i < n) ch = str[i++];
switch (ps){
case Ps.AddOffset:
if (Nil != Offset)
return Double.NaN;
Offset = T < 24 ? T * 60 : (T % 100) + (T / 100) * 60; //> 24 implies hhmm
ps = Ps.Initial;
if (i < n) i--;
break;
case Ps.SubOffset:
if (Nil != Offset)
return Double.NaN;
Offset = T < 24 ? -T * 60 : -((T % 100) + (T / 100) * 60); //> 24 implies hhmm
ps = Ps.Initial;
if (i < n) i--;
break;
case Ps.Minutes:
if (T >= 60)
return Double.NaN;
Time += T * 60;
if (ch == ':'){
ps = Ps.Seconds;
}else{
ps = Ps.Initial;
if (i < n) i--;
}
break;
case Ps.Seconds:
if (T >= 60)
return Double.NaN;
Time += T;
ps = Ps.Initial;
if (i < n) i--;
break;
case Ps.Date:
if (Nil != Date)
return Double.NaN;
Date = T;
if ('/' == ch || '-' == ch){
ps = Ps.Year;
}else{
ps = Ps.Initial;
if (i < n) i--;
}
break;
case Ps.Year:
if (Nil != Year)
return Double.NaN;
Year = T;
ps = Ps.Initial;
if (i < n) i--;
break;
default:
//Assert(ps == Ps.Initial);
if (T >= 70){
// assume it's a year
if (Nil != Year)
return Double.NaN;
Year = T;
if (i < n) i--;
break;
}
switch (ch){
case ':':
// hour
if (Nil != Time)
return Double.NaN;
if (T >= 24)
return Double.NaN;
Time = T * 3600;
ps = Ps.Minutes;
break;
case '/': goto case '-';
case '-':
// month
if (Nil != Month)
return Double.NaN;
Month = T - 1;
ps = Ps.Date;
break;
default:
// date
if (Nil != Date)
return Double.NaN;
Date = T;
if (i < n) i--;
break;
}
break;
}
}
if (Nil == Year || Nil == Month || Nil == Date)
return Double.NaN;
if (0 != BcAd){
if (BcAd < 0)
// BC. Note that 1 BC is year 0 and 2 BC is year -1.
Year = -Year + 1;
}else if (Year < 100)
Year += 1900;
if (0 != AmPm){
if (Nil == Time)
return Double.NaN;
if (Time >= 12 * 3600 && Time < 13 * 3600){
// In the 12:00 hour. AM means subtract 12 hours and PM means
// do nothing.
if (AmPm < 0)
Time -= 12 * 3600;
}else{
// Not in the 12:00 hour. AM means do nothing and PM means
// add 12 hours.
if (AmPm > 0){
if (Time >= 12 * 3600)
return Double.NaN;
Time += 12 * 3600;
}
}
}else if (Nil == Time)
Time = 0;
bool Utc = false;
if (Nil != Zone){
Time -= Zone * 60;
Utc = true;
}
if (Nil != Offset)
Time -= Offset * 60;
// Rebuild time.
double result = MakeDate(MakeDay(Year, Month, Date), Time*1000);
if (!Utc)
result = UTC(result);
return result;
}