DateTimeParser
struct DateTimeParser
using System;
namespace Newtonsoft.Json.Utilities
{
internal struct DateTimeParser
{
public int Year;
public int Month;
public int Day;
public int Hour;
public int Minute;
public int Second;
public int Fraction;
public int ZoneHour;
public int ZoneMinute;
public ParserTimeZone Zone;
private char[] _text;
private int _end;
private static readonly int[] Power10;
private static readonly int Lzyyyy;
private static readonly int Lzyyyy_;
private static readonly int Lzyyyy_MM;
private static readonly int Lzyyyy_MM_;
private static readonly int Lzyyyy_MM_dd;
private static readonly int Lzyyyy_MM_ddT;
private static readonly int LzHH;
private static readonly int LzHH_;
private static readonly int LzHH_mm;
private static readonly int LzHH_mm_;
private static readonly int LzHH_mm_ss;
private static readonly int Lz_;
private static readonly int Lz_zz;
private const short MaxFractionDigits = 7;
static DateTimeParser()
{
Power10 = new int[7] {
-1,
10,
100,
1000,
10000,
100000,
1000000
};
Lzyyyy = "yyyy".Length;
Lzyyyy_ = "yyyy-".Length;
Lzyyyy_MM = "yyyy-MM".Length;
Lzyyyy_MM_ = "yyyy-MM-".Length;
Lzyyyy_MM_dd = "yyyy-MM-dd".Length;
Lzyyyy_MM_ddT = "yyyy-MM-ddT".Length;
LzHH = "HH".Length;
LzHH_ = "HH:".Length;
LzHH_mm = "HH:mm".Length;
LzHH_mm_ = "HH:mm:".Length;
LzHH_mm_ss = "HH:mm:ss".Length;
Lz_ = "-".Length;
Lz_zz = "-zz".Length;
}
public bool Parse(char[] text, int startIndex, int length)
{
_text = text;
_end = startIndex + length;
if (ParseDate(startIndex) && ParseChar(Lzyyyy_MM_dd + startIndex, 'T') && ParseTimeAndZoneAndWhitespace(Lzyyyy_MM_ddT + startIndex))
return true;
return false;
}
private bool ParseDate(int start)
{
if (Parse4Digit(start, out Year) && 1 <= Year && ParseChar(start + Lzyyyy, '-') && Parse2Digit(start + Lzyyyy_, out Month) && 1 <= Month && Month <= 12 && ParseChar(start + Lzyyyy_MM, '-') && Parse2Digit(start + Lzyyyy_MM_, out Day) && 1 <= Day)
return Day <= DateTime.DaysInMonth(Year, Month);
return false;
}
private bool ParseTimeAndZoneAndWhitespace(int start)
{
if (ParseTime(ref start))
return ParseZone(start);
return false;
}
private bool ParseTime(ref int start)
{
if (!Parse2Digit(start, out Hour) || Hour > 24 || !ParseChar(start + LzHH, ':') || !Parse2Digit(start + LzHH_, out Minute) || Minute >= 60 || !ParseChar(start + LzHH_mm, ':') || !Parse2Digit(start + LzHH_mm_, out Second) || Second >= 60 || (Hour == 24 && (Minute != 0 || Second != 0)))
return false;
start += LzHH_mm_ss;
if (ParseChar(start, '.')) {
Fraction = 0;
int num = 0;
while (++start < _end && num < 7) {
int num2 = _text[start] - 48;
if (num2 < 0 || num2 > 9)
break;
Fraction = Fraction * 10 + num2;
num++;
}
if (num < 7) {
if (num == 0)
return false;
Fraction *= Power10[7 - num];
}
if (Hour == 24 && Fraction != 0)
return false;
}
return true;
}
private bool ParseZone(int start)
{
if (start < _end) {
char c = _text[start];
if (c == 'Z' || c == 'z') {
Zone = ParserTimeZone.Utc;
start++;
} else {
if (start + 2 < _end && Parse2Digit(start + Lz_, out ZoneHour) && ZoneHour <= 99) {
switch (c) {
case '-':
Zone = ParserTimeZone.LocalWestOfUtc;
start += Lz_zz;
break;
case '+':
Zone = ParserTimeZone.LocalEastOfUtc;
start += Lz_zz;
break;
}
}
if (start < _end) {
if (ParseChar(start, ':')) {
start++;
if (start + 1 < _end && Parse2Digit(start, out ZoneMinute) && ZoneMinute <= 99)
start += 2;
} else if (start + 1 < _end && Parse2Digit(start, out ZoneMinute) && ZoneMinute <= 99) {
start += 2;
}
}
}
}
return start == _end;
}
private bool Parse4Digit(int start, out int num)
{
if (start + 3 < _end) {
int num2 = _text[start] - 48;
int num3 = _text[start + 1] - 48;
int num4 = _text[start + 2] - 48;
int num5 = _text[start + 3] - 48;
if (0 <= num2 && num2 < 10 && 0 <= num3 && num3 < 10 && 0 <= num4 && num4 < 10 && 0 <= num5 && num5 < 10) {
num = ((num2 * 10 + num3) * 10 + num4) * 10 + num5;
return true;
}
}
num = 0;
return false;
}
private bool Parse2Digit(int start, out int num)
{
if (start + 1 < _end) {
int num2 = _text[start] - 48;
int num3 = _text[start + 1] - 48;
if (0 <= num2 && num2 < 10 && 0 <= num3 && num3 < 10) {
num = num2 * 10 + num3;
return true;
}
}
num = 0;
return false;
}
private bool ParseChar(int start, char ch)
{
if (start < _end)
return _text[start] == ch;
return false;
}
}
}