277 lines
8.9 KiB
C#
277 lines
8.9 KiB
C#
#region License
|
|
// Copyright (c) 2007 James Newton-King
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person
|
|
// obtaining a copy of this software and associated documentation
|
|
// files (the "Software"), to deal in the Software without
|
|
// restriction, including without limitation the rights to use,
|
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following
|
|
// conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
// OTHER DEALINGS IN THE SOFTWARE.
|
|
#endregion
|
|
|
|
using System;
|
|
|
|
namespace Newtonsoft.Json.Utilities
|
|
{
|
|
internal enum ParserTimeZone
|
|
{
|
|
Unspecified = 0,
|
|
Utc = 1,
|
|
LocalWestOfUtc = 2,
|
|
LocalEastOfUtc = 3
|
|
}
|
|
|
|
internal struct DateTimeParser
|
|
{
|
|
static DateTimeParser()
|
|
{
|
|
Power10 = new[] { -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 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;
|
|
|
|
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)
|
|
{
|
|
return (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
|
|
&& Day <= DateTime.DaysInMonth(Year, Month));
|
|
}
|
|
|
|
private bool ParseTimeAndZoneAndWhitespace(int start)
|
|
{
|
|
return (ParseTime(ref start) && ParseZone(start));
|
|
}
|
|
|
|
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)))) // hour can be 24 if minute/second is zero)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
start += LzHH_mm_ss;
|
|
if (ParseChar(start, '.'))
|
|
{
|
|
Fraction = 0;
|
|
int numberOfDigits = 0;
|
|
|
|
while (++start < _end && numberOfDigits < MaxFractionDigits)
|
|
{
|
|
int digit = _text[start] - '0';
|
|
if (digit < 0 || digit > 9)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Fraction = (Fraction * 10) + digit;
|
|
|
|
numberOfDigits++;
|
|
}
|
|
|
|
if (numberOfDigits < MaxFractionDigits)
|
|
{
|
|
if (numberOfDigits == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Fraction *= Power10[MaxFractionDigits - numberOfDigits];
|
|
}
|
|
|
|
if (Hour == 24 && Fraction != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool ParseZone(int start)
|
|
{
|
|
if (start < _end)
|
|
{
|
|
char ch = _text[start];
|
|
if (ch == 'Z' || ch == 'z')
|
|
{
|
|
Zone = ParserTimeZone.Utc;
|
|
start++;
|
|
}
|
|
else
|
|
{
|
|
if (start + 2 < _end
|
|
&& Parse2Digit(start + Lz_, out ZoneHour)
|
|
&& ZoneHour <= 99)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case '-':
|
|
Zone = ParserTimeZone.LocalWestOfUtc;
|
|
start += Lz_zz;
|
|
break;
|
|
|
|
case '+':
|
|
Zone = ParserTimeZone.LocalEastOfUtc;
|
|
start += Lz_zz;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (start < _end)
|
|
{
|
|
if (ParseChar(start, ':'))
|
|
{
|
|
start += 1;
|
|
|
|
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 digit1 = _text[start] - '0';
|
|
int digit2 = _text[start + 1] - '0';
|
|
int digit3 = _text[start + 2] - '0';
|
|
int digit4 = _text[start + 3] - '0';
|
|
if (0 <= digit1 && digit1 < 10
|
|
&& 0 <= digit2 && digit2 < 10
|
|
&& 0 <= digit3 && digit3 < 10
|
|
&& 0 <= digit4 && digit4 < 10)
|
|
{
|
|
num = (((((digit1 * 10) + digit2) * 10) + digit3) * 10) + digit4;
|
|
return true;
|
|
}
|
|
}
|
|
num = 0;
|
|
return false;
|
|
}
|
|
|
|
private bool Parse2Digit(int start, out int num)
|
|
{
|
|
if (start + 1 < _end)
|
|
{
|
|
int digit1 = _text[start] - '0';
|
|
int digit2 = _text[start + 1] - '0';
|
|
if (0 <= digit1 && digit1 < 10
|
|
&& 0 <= digit2 && digit2 < 10)
|
|
{
|
|
num = (digit1 * 10) + digit2;
|
|
return true;
|
|
}
|
|
}
|
|
num = 0;
|
|
return false;
|
|
}
|
|
|
|
private bool ParseChar(int start, char ch)
|
|
{
|
|
return (start < _end && _text[start] == ch);
|
|
}
|
|
}
|
|
} |