private static bool TryParseDateTime(byte[] chars, int offset, int count, out DateTime result)
{
int offsetMax = offset + count;
result = DateTime.MaxValue;
if (count < 19)
return false;
// 1 2 3
// 012345678901234567890123456789012
// "yyyy-MM-ddTHH:mm:ss"
// "yyyy-MM-ddTHH:mm:ss.fffffff"
// "yyyy-MM-ddTHH:mm:ss.fffffffZ"
// "yyyy-MM-ddTHH:mm:ss.fffffff+xx:yy"
// "yyyy-MM-ddTHH:mm:ss.fffffff-xx:yy"
if (chars[offset + 4] != '-' || chars[offset + 7] != '-' || chars[offset + 10] != 'T' ||
chars[offset + 13] != ':' || chars[offset + 16] != ':')
return false;
int year = ToInt32D4(chars, offset + 0, 4);
int month = ToInt32D2(chars, offset + 5);
int day = ToInt32D2(chars, offset + 8);
int hour = ToInt32D2(chars, offset + 11);
int minute = ToInt32D2(chars, offset + 14);
int second = ToInt32D2(chars, offset + 17);
if ((year | month | day | hour | minute | second) < 0)
return false;
DateTimeKind kind = DateTimeKind.Unspecified;
offset += 19;
int ticks = 0;
if (offset < offsetMax && chars[offset] == '.')
{
offset++;
int digitOffset = offset;
while (offset < offsetMax)
{
byte ch = chars[offset];
if (ch < '0' || ch > '9')
break;
offset++;
}
int digitCount = offset - digitOffset;
if (digitCount < 1 || digitCount > 7)
return false;
ticks = ToInt32D7(chars, digitOffset, digitCount);
if (ticks < 0)
return false;
for (int i = digitCount; i < 7; ++i)
ticks *= 10;
}
bool isLocal = false;
int hourDelta = 0;
int minuteDelta = 0;
if (offset < offsetMax)
{
byte ch = chars[offset];
if (ch == 'Z')
{
offset++;
kind = DateTimeKind.Utc;
}
else if (ch == '+' || ch == '-')
{
offset++;
if (offset + 5 > offsetMax || chars[offset + 2] != ':')
return false;
kind = DateTimeKind.Utc;
isLocal = true;
hourDelta = ToInt32D2(chars, offset);
minuteDelta = ToInt32D2(chars, offset + 3);
if ((hourDelta | minuteDelta) < 0)
return false;
if (ch == '+')
{
hourDelta = -hourDelta;
minuteDelta = -minuteDelta;
}
offset += 5;
}
}
if (offset < offsetMax)
return false;
DateTime value;
try
{
value = new DateTime(year, month, day, hour, minute, second, kind);
}
catch (ArgumentException)
{
return false;
}
if (ticks > 0)
{
value = value.AddTicks(ticks);
}
if (isLocal)
{
try
{
TimeSpan ts = new TimeSpan(hourDelta, minuteDelta, 0);
if (hourDelta >= 0 && (value < DateTime.MaxValue - ts) ||
hourDelta < 0 && (value > DateTime.MinValue - ts))
{
value = value.Add(ts).ToLocalTime();
}
else
{
value = value.ToLocalTime().Add(ts);
}
}
catch (ArgumentOutOfRangeException) // Overflow
{
return false;
}
}
result = value;
return true;
}