-Fixed DateTimeZoneHandling not being used when writing DateTime dictionary keys
-Buffer pooling fixes -Improved date parsing performance
This commit is contained in:
@@ -52,13 +52,10 @@ namespace Newtonsoft.Json.TestConsole
|
||||
//t.DeserializeLargeJson();
|
||||
|
||||
//DeserializeLargeJson();
|
||||
//WriteLargeJson();
|
||||
WriteLargeJson();
|
||||
//DeserializeJson();
|
||||
//ReadLargeJson();
|
||||
//ReadLargeJsonJavaScriptSerializer();
|
||||
DiscriminatedUnionConverterTests t = new DiscriminatedUnionConverterTests();
|
||||
t.SerializePerformance();
|
||||
t.DeserializePerformance();
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Finished");
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -1175,8 +1176,8 @@ third line", jsonTextReader.Value);
|
||||
|
||||
public class FakeBufferPool : IJsonBufferPool<char>
|
||||
{
|
||||
public readonly IList<char[]> FreeBuffers = new List<char[]>();
|
||||
public readonly IList<char[]> UsedBuffers = new List<char[]>();
|
||||
public readonly List<char[]> FreeBuffers = new List<char[]>();
|
||||
public readonly List<char[]> UsedBuffers = new List<char[]>();
|
||||
|
||||
public char[] RentBuffer(int minSize)
|
||||
{
|
||||
@@ -1197,10 +1198,12 @@ third line", jsonTextReader.Value);
|
||||
|
||||
public void ReturnBuffer(ref char[] buffer)
|
||||
{
|
||||
UsedBuffers.Remove(buffer);
|
||||
if (!FreeBuffers.Contains(buffer))
|
||||
if (UsedBuffers.Remove(buffer))
|
||||
{
|
||||
FreeBuffers.Add(buffer);
|
||||
|
||||
// smallest first so the first buffer large enough is rented
|
||||
FreeBuffers.Sort((b1, b2) => Comparer<int>.Default.Compare(b1.Length, b2.Length));
|
||||
}
|
||||
|
||||
buffer = null;
|
||||
@@ -1215,7 +1218,8 @@ third line", jsonTextReader.Value);
|
||||
""Description"": ""Amazing!\nBuy now!"",
|
||||
""Drives"": [
|
||||
""DVD read/writer"",
|
||||
""500 gigabyte hard drive""
|
||||
""500 gigabyte hard drive"",
|
||||
""Amazing Drive" + new string('!', 9000) + @"""
|
||||
]
|
||||
}";
|
||||
|
||||
@@ -1238,7 +1242,8 @@ third line", jsonTextReader.Value);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, bufferPool.FreeBuffers.Count);
|
||||
Assert.AreEqual(0, bufferPool.UsedBuffers.Count);
|
||||
Assert.AreEqual(6, bufferPool.FreeBuffers.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -59,6 +59,50 @@ namespace Newtonsoft.Json.Tests
|
||||
[TestFixture]
|
||||
public class JsonTextWriterTest : TestFixtureBase
|
||||
{
|
||||
[Test]
|
||||
public void BufferTest()
|
||||
{
|
||||
JsonTextReaderTest.FakeBufferPool bufferPool = new JsonTextReaderTest.FakeBufferPool();
|
||||
|
||||
string longString = new string('A', 2000);
|
||||
string longEscapedString = "Hello!" + new string('!', 50) + new string('\n', 1000) + "Good bye!";
|
||||
string longerEscapedString = "Hello!" + new string('!', 2000) + new string('\n', 1000) + "Good bye!";
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
|
||||
|
||||
using (JsonTextWriter writer = new JsonTextWriter(sw))
|
||||
{
|
||||
writer.BufferPool = bufferPool;
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WritePropertyName("Prop1");
|
||||
writer.WriteValue(new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc));
|
||||
|
||||
writer.WritePropertyName("Prop2");
|
||||
writer.WriteValue(longString);
|
||||
|
||||
writer.WritePropertyName("Prop3");
|
||||
writer.WriteValue(longEscapedString);
|
||||
|
||||
writer.WritePropertyName("Prop4");
|
||||
writer.WriteValue(longerEscapedString);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
if ((i + 1) % 100 == 0)
|
||||
{
|
||||
Console.WriteLine("Allocated buffers: " + bufferPool.FreeBuffers.Count);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, bufferPool.UsedBuffers.Count);
|
||||
Assert.AreEqual(3, bufferPool.FreeBuffers.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NewLine()
|
||||
{
|
||||
|
||||
@@ -7487,6 +7487,34 @@ Path '', line 1, position 1.");
|
||||
Assert.AreEqual(2, dic2[new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc)]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DateTimeDictionaryKey_DateTime_Iso_Local()
|
||||
{
|
||||
IDictionary<DateTime, int> dic1 = new Dictionary<DateTime, int>
|
||||
{
|
||||
{ new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc), 1 },
|
||||
{ new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc), 2 }
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(dic1, Formatting.Indented, new JsonSerializerSettings
|
||||
{
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Local
|
||||
});
|
||||
|
||||
JObject o = JObject.Parse(json);
|
||||
Assert.IsFalse(o.Properties().ElementAt(0).Name.Contains("Z"));
|
||||
Assert.IsFalse(o.Properties().ElementAt(1).Name.Contains("Z"));
|
||||
|
||||
IDictionary<DateTime, int> dic2 = JsonConvert.DeserializeObject<IDictionary<DateTime, int>>(json, new JsonSerializerSettings
|
||||
{
|
||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc
|
||||
});
|
||||
|
||||
Assert.AreEqual(2, dic2.Count);
|
||||
Assert.AreEqual(1, dic2[new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc)]);
|
||||
Assert.AreEqual(2, dic2[new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc)]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DateTimeDictionaryKey_DateTime_MS()
|
||||
{
|
||||
|
||||
@@ -53,16 +53,20 @@ namespace Newtonsoft.Json.Tests.Utilities
|
||||
RoundtripDateIso(DateTime.MaxValue);
|
||||
}
|
||||
|
||||
private static StringReference CreateStringReference(string s)
|
||||
{
|
||||
return new StringReference(s.ToCharArray(), 0, s.Length);
|
||||
}
|
||||
|
||||
private static void RoundtripDateIso(DateTime value)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
DateTimeUtils.WriteDateTimeString(sw, value, DateFormatHandling.IsoDateFormat, null, CultureInfo.InvariantCulture);
|
||||
string minDateText = sw.ToString();
|
||||
|
||||
object dt;
|
||||
DateTimeUtils.TryParseDateIso(minDateText, DateParseHandling.DateTime, DateTimeZoneHandling.RoundtripKind, out dt);
|
||||
DateTime parsedDt;
|
||||
DateTimeUtils.TryParseDateTimeIso(CreateStringReference(minDateText), DateTimeZoneHandling.RoundtripKind, out parsedDt);
|
||||
|
||||
DateTime parsedDt = (DateTime)dt;
|
||||
Assert.AreEqual(value, parsedDt);
|
||||
}
|
||||
|
||||
@@ -118,8 +122,12 @@ namespace Newtonsoft.Json.Tests.Utilities
|
||||
oldDate = oldDt;
|
||||
}
|
||||
|
||||
object newDt;
|
||||
DateTimeUtils.TryParseDateIso(text, DateParseHandling.DateTime, DateTimeZoneHandling.RoundtripKind, out newDt);
|
||||
object newDt = null;
|
||||
DateTime temp;
|
||||
if (DateTimeUtils.TryParseDateTimeIso(CreateStringReference(text), DateTimeZoneHandling.RoundtripKind, out temp))
|
||||
{
|
||||
newDt = temp;
|
||||
}
|
||||
|
||||
if (!Equals(oldDate, newDt))
|
||||
{
|
||||
@@ -175,17 +183,26 @@ namespace Newtonsoft.Json.Tests.Utilities
|
||||
private void AssertNewDateTimeOffsetParseEqual(string text)
|
||||
{
|
||||
object oldDt;
|
||||
object newDt;
|
||||
object newDt = null;
|
||||
|
||||
TryParseDateIso(text, DateParseHandling.DateTimeOffset, DateTimeZoneHandling.Unspecified, out oldDt);
|
||||
DateTimeUtils.TryParseDateIso(text, DateParseHandling.DateTimeOffset, DateTimeZoneHandling.Unspecified, out newDt);
|
||||
|
||||
DateTimeOffset temp;
|
||||
if (DateTimeUtils.TryParseDateTimeOffsetIso(CreateStringReference(text), out temp))
|
||||
{
|
||||
newDt = temp;
|
||||
}
|
||||
|
||||
if (!Equals(oldDt, newDt))
|
||||
{
|
||||
long? oldTicks = oldDt != null ? (long?)((DateTime)oldDt).Ticks : null;
|
||||
long? newTicks = newDt != null ? (long?)((DateTime)newDt).Ticks : null;
|
||||
|
||||
Assert.AreEqual(oldDt, newDt, "DateTimeOffset parse not equal. Text: '{0}' Old ticks: {1} New ticks: {2}".FormatWith(
|
||||
CultureInfo.InvariantCulture,
|
||||
text,
|
||||
((DateTime)oldDt).Ticks,
|
||||
((DateTime)newDt).Ticks));
|
||||
oldTicks,
|
||||
newTicks));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace Newtonsoft.Json.Bson
|
||||
/// <summary>
|
||||
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
/// <returns>A <see cref="Nullable{DateTime}"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
public override DateTime? ReadAsDateTime()
|
||||
{
|
||||
return ReadAsDateTimeInternal();
|
||||
|
||||
@@ -422,7 +422,7 @@ namespace Newtonsoft.Json
|
||||
/// <summary>
|
||||
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
/// <returns>A <see cref="Nullable{DateTime}"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
public abstract DateTime? ReadAsDateTime();
|
||||
|
||||
#if !NET20
|
||||
@@ -478,11 +478,9 @@ namespace Newtonsoft.Json
|
||||
return null;
|
||||
}
|
||||
|
||||
object temp;
|
||||
DateTimeOffset dt;
|
||||
if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTimeOffset, DateTimeZoneHandling, _dateFormatString, Culture, out temp))
|
||||
if (DateTimeUtils.TryParseDateTimeOffset(s, _dateFormatString, Culture, out dt))
|
||||
{
|
||||
dt = (DateTimeOffset)temp;
|
||||
SetToken(JsonToken.Date, dt, false);
|
||||
return dt;
|
||||
}
|
||||
@@ -793,10 +791,8 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
|
||||
DateTime dt;
|
||||
object temp;
|
||||
if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTime, DateTimeZoneHandling, _dateFormatString, Culture, out temp))
|
||||
if (DateTimeUtils.TryParseDateTime(s, DateTimeZoneHandling, _dateFormatString, Culture, out dt))
|
||||
{
|
||||
dt = (DateTime)temp;
|
||||
dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
|
||||
SetToken(JsonToken.Date, dt, false);
|
||||
return dt;
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
private const char UnicodeReplacementChar = '\uFFFD';
|
||||
private const int MaximumJavascriptIntegerCharacterLength = 380;
|
||||
|
||||
private readonly TextReader _reader;
|
||||
private char[] _chars;
|
||||
private int _charsUsed;
|
||||
@@ -73,7 +74,9 @@ namespace Newtonsoft.Json
|
||||
public JsonTextReader(TextReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
|
||||
_reader = reader;
|
||||
_lineNumber = 1;
|
||||
@@ -107,7 +110,7 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
if (_stringBuffer.IsEmpty)
|
||||
{
|
||||
_stringBuffer = new StringBuffer(_bufferPool, 1025);
|
||||
_stringBuffer = new StringBuffer(_bufferPool, 1024);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,33 +156,52 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
else
|
||||
{
|
||||
string text = _stringReference.ToString();
|
||||
|
||||
if (_dateParseHandling != DateParseHandling.None)
|
||||
{
|
||||
DateParseHandling dateParseHandling;
|
||||
if (_readType == ReadType.ReadAsDateTime)
|
||||
{
|
||||
dateParseHandling = DateParseHandling.DateTime;
|
||||
}
|
||||
#if !NET20
|
||||
else if (_readType == ReadType.ReadAsDateTimeOffset)
|
||||
{
|
||||
dateParseHandling = DateParseHandling.DateTimeOffset;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
dateParseHandling = _dateParseHandling;
|
||||
|
||||
object dt;
|
||||
if (DateTimeUtils.TryParseDateTime(text, dateParseHandling, DateTimeZoneHandling, DateFormatString, Culture, out dt))
|
||||
{
|
||||
SetToken(JsonToken.Date, dt, false);
|
||||
return;
|
||||
dateParseHandling = _dateParseHandling;
|
||||
}
|
||||
|
||||
if (dateParseHandling == DateParseHandling.DateTime)
|
||||
{
|
||||
DateTime dt;
|
||||
if (DateTimeUtils.TryParseDateTime(_stringReference, DateTimeZoneHandling, DateFormatString, Culture, out dt))
|
||||
{
|
||||
SetToken(JsonToken.Date, dt, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if !NET20
|
||||
else
|
||||
{
|
||||
DateTimeOffset dt;
|
||||
if (DateTimeUtils.TryParseDateTimeOffset(_stringReference, DateFormatString, Culture, out dt))
|
||||
{
|
||||
SetToken(JsonToken.Date, dt, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SetToken(JsonToken.String, text, false);
|
||||
SetToken(JsonToken.String, _stringReference.ToString(), false);
|
||||
_quoteChar = quote;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count)
|
||||
{
|
||||
const int charByteCount = 2;
|
||||
@@ -196,7 +218,9 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
int count = _charsUsed - _charPos;
|
||||
if (count > 0)
|
||||
{
|
||||
BlockCopyChars(_chars, _charPos, _chars, 0, count);
|
||||
}
|
||||
|
||||
_lineStartPos -= _charPos;
|
||||
_charPos = 0;
|
||||
@@ -213,7 +237,9 @@ namespace Newtonsoft.Json
|
||||
private int ReadData(bool append, int charsRequired)
|
||||
{
|
||||
if (_isEndOfFile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// char buffer is full
|
||||
if (_charsUsed + charsRequired >= _chars.Length - 1)
|
||||
@@ -242,7 +268,9 @@ namespace Newtonsoft.Json
|
||||
char[] dst = BufferUtils.RentBuffer(_bufferPool, remainingCharCount + charsRequired + 1);
|
||||
|
||||
if (remainingCharCount > 0)
|
||||
{
|
||||
BlockCopyChars(_chars, _charPos, dst, 0, remainingCharCount);
|
||||
}
|
||||
|
||||
BufferUtils.ReturnBuffer(_bufferPool, ref _chars);
|
||||
|
||||
@@ -252,7 +280,9 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
// copy any remaining data to the beginning of the buffer if needed and reset positions
|
||||
if (remainingCharCount > 0)
|
||||
{
|
||||
BlockCopyChars(_chars, _charPos, _chars, 0, remainingCharCount);
|
||||
}
|
||||
}
|
||||
|
||||
_lineStartPos -= _charPos;
|
||||
@@ -268,7 +298,9 @@ namespace Newtonsoft.Json
|
||||
_charsUsed += charsRead;
|
||||
|
||||
if (charsRead == 0)
|
||||
{
|
||||
_isEndOfFile = true;
|
||||
}
|
||||
|
||||
_chars[_charsUsed] = '\0';
|
||||
return charsRead;
|
||||
@@ -277,7 +309,9 @@ namespace Newtonsoft.Json
|
||||
private bool EnsureChars(int relativePosition, bool append)
|
||||
{
|
||||
if (_charPos + relativePosition >= _charsUsed)
|
||||
{
|
||||
return ReadChars(relativePosition, append);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -285,7 +319,9 @@ namespace Newtonsoft.Json
|
||||
private bool ReadChars(int relativePosition, bool append)
|
||||
{
|
||||
if (_isEndOfFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int charsRequired = _charPos + relativePosition - _charsUsed + 1;
|
||||
|
||||
@@ -299,13 +335,17 @@ namespace Newtonsoft.Json
|
||||
|
||||
// no more content
|
||||
if (charsRead == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
totalCharsRead += charsRead;
|
||||
} while (totalCharsRead < charsRequired);
|
||||
|
||||
if (totalCharsRead < charsRequired)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -369,7 +409,7 @@ namespace Newtonsoft.Json
|
||||
/// <summary>
|
||||
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
/// <returns>A <see cref="Nullable{DateTime}"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
public override DateTime? ReadAsDateTime()
|
||||
{
|
||||
return ReadAsDateTimeInternal();
|
||||
@@ -391,6 +431,7 @@ namespace Newtonsoft.Json
|
||||
if (_chars == null)
|
||||
{
|
||||
_chars = BufferUtils.RentBuffer(_bufferPool, 1024);
|
||||
_chars[0] = '\0';
|
||||
}
|
||||
|
||||
while (true)
|
||||
@@ -658,7 +699,9 @@ namespace Newtonsoft.Json
|
||||
if (_charsUsed == charPos)
|
||||
{
|
||||
if (ReadData(true) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -800,7 +843,9 @@ namespace Newtonsoft.Json
|
||||
if (_charsUsed == _charPos)
|
||||
{
|
||||
if (ReadData(false) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -871,7 +916,9 @@ namespace Newtonsoft.Json
|
||||
|
||||
// no match in name table
|
||||
if (propertyName == null)
|
||||
{
|
||||
propertyName = _stringReference.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -881,7 +928,9 @@ namespace Newtonsoft.Json
|
||||
EatWhitespace(false);
|
||||
|
||||
if (_chars[_charPos] != ':')
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Invalid character after parsing property name. Expected ':' but got: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
|
||||
}
|
||||
|
||||
_charPos++;
|
||||
|
||||
@@ -971,11 +1020,17 @@ namespace Newtonsoft.Json
|
||||
char next = _chars[_charPos + 1];
|
||||
|
||||
if (next == 'u')
|
||||
{
|
||||
ParseNull();
|
||||
}
|
||||
else if (next == 'e')
|
||||
{
|
||||
ParseConstructor();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -990,9 +1045,13 @@ namespace Newtonsoft.Json
|
||||
return true;
|
||||
case '-':
|
||||
if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I')
|
||||
{
|
||||
ParseNumberNegativeInfinity();
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseNumber();
|
||||
}
|
||||
return true;
|
||||
case '/':
|
||||
ParseComment();
|
||||
@@ -1061,7 +1120,9 @@ namespace Newtonsoft.Json
|
||||
_charPos++;
|
||||
|
||||
if (EnsureChars(1, append) && _chars[_charPos] == StringUtils.LineFeed)
|
||||
{
|
||||
_charPos++;
|
||||
}
|
||||
|
||||
OnNewLine(_charPos);
|
||||
}
|
||||
@@ -1080,7 +1141,9 @@ namespace Newtonsoft.Json
|
||||
if (_charsUsed == _charPos)
|
||||
{
|
||||
if (ReadData(false) == 0)
|
||||
{
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1127,7 +1190,9 @@ namespace Newtonsoft.Json
|
||||
if (_charsUsed == _charPos)
|
||||
{
|
||||
if (ReadData(true) == 0)
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Unexpected end while parsing constructor.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1175,7 +1240,9 @@ namespace Newtonsoft.Json
|
||||
EatWhitespace(false);
|
||||
|
||||
if (_chars[_charPos] != '(')
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
|
||||
}
|
||||
|
||||
_charPos++;
|
||||
|
||||
@@ -1241,11 +1308,17 @@ namespace Newtonsoft.Json
|
||||
int value;
|
||||
ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value);
|
||||
if (parseResult == ParseResult.Success)
|
||||
{
|
||||
numberValue = value;
|
||||
}
|
||||
else if (parseResult == ParseResult.Overflow)
|
||||
{
|
||||
throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Integer;
|
||||
@@ -1281,9 +1354,13 @@ namespace Newtonsoft.Json
|
||||
|
||||
decimal value;
|
||||
if (decimal.TryParse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value))
|
||||
{
|
||||
numberValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Float;
|
||||
@@ -1328,7 +1405,9 @@ namespace Newtonsoft.Json
|
||||
string number = _stringReference.ToString();
|
||||
|
||||
if (number.Length > MaximumJavascriptIntegerCharacterLength)
|
||||
{
|
||||
throw JsonReaderException.Create(this, "JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
|
||||
}
|
||||
|
||||
numberValue = BigIntegerParse(number, CultureInfo.InvariantCulture);
|
||||
numberType = JsonToken.Integer;
|
||||
@@ -1344,17 +1423,25 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
decimal d;
|
||||
if (decimal.TryParse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
|
||||
{
|
||||
numberValue = d;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double d;
|
||||
if (double.TryParse(number, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d))
|
||||
{
|
||||
numberValue = d;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number));
|
||||
}
|
||||
}
|
||||
|
||||
numberType = JsonToken.Float;
|
||||
@@ -1391,11 +1478,17 @@ namespace Newtonsoft.Json
|
||||
bool singlelineComment;
|
||||
|
||||
if (_chars[_charPos] == '*')
|
||||
{
|
||||
singlelineComment = false;
|
||||
}
|
||||
else if (_chars[_charPos] == '/')
|
||||
{
|
||||
singlelineComment = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
|
||||
}
|
||||
|
||||
_charPos++;
|
||||
|
||||
@@ -1413,7 +1506,9 @@ namespace Newtonsoft.Json
|
||||
if (ReadData(true) == 0)
|
||||
{
|
||||
if (!singlelineComment)
|
||||
{
|
||||
throw JsonReaderException.Create(this, "Unexpected end while parsing comment.");
|
||||
}
|
||||
|
||||
_stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
|
||||
commentFinished = true;
|
||||
@@ -1471,7 +1566,9 @@ namespace Newtonsoft.Json
|
||||
private bool MatchValue(string value)
|
||||
{
|
||||
if (!EnsureChars(value.Length - 1, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
@@ -1492,10 +1589,14 @@ namespace Newtonsoft.Json
|
||||
bool match = MatchValue(value);
|
||||
|
||||
if (!match)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnsureChars(0, false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return IsSeparator(_chars[_charPos]) || _chars[_charPos] == '\0';
|
||||
}
|
||||
@@ -1511,14 +1612,18 @@ namespace Newtonsoft.Json
|
||||
case '/':
|
||||
// check next character to see if start of a comment
|
||||
if (!EnsureChars(1, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var nextChart = _chars[_charPos + 1];
|
||||
|
||||
return (nextChart == '*' || nextChart == '/');
|
||||
case ')':
|
||||
if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
case StringUtils.Tab:
|
||||
@@ -1527,7 +1632,9 @@ namespace Newtonsoft.Json
|
||||
return true;
|
||||
default:
|
||||
if (char.IsWhiteSpace(c))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1590,7 +1697,9 @@ namespace Newtonsoft.Json
|
||||
if (MatchValueWithTrailingSeparator(JsonConvert.NegativeInfinity))
|
||||
{
|
||||
if (_floatParseHandling == FloatParseHandling.Decimal)
|
||||
{
|
||||
throw new JsonReaderException("Cannot read -Infinity as a decimal.");
|
||||
}
|
||||
|
||||
SetToken(JsonToken.Float, double.NegativeInfinity);
|
||||
}
|
||||
@@ -1605,7 +1714,9 @@ namespace Newtonsoft.Json
|
||||
if (MatchValueWithTrailingSeparator(JsonConvert.PositiveInfinity))
|
||||
{
|
||||
if (_floatParseHandling == FloatParseHandling.Decimal)
|
||||
{
|
||||
throw new JsonReaderException("Cannot read Infinity as a decimal.");
|
||||
}
|
||||
|
||||
SetToken(JsonToken.Float, double.PositiveInfinity);
|
||||
}
|
||||
@@ -1620,7 +1731,9 @@ namespace Newtonsoft.Json
|
||||
if (MatchValueWithTrailingSeparator(JsonConvert.NaN))
|
||||
{
|
||||
if (_floatParseHandling == FloatParseHandling.Decimal)
|
||||
{
|
||||
throw new JsonReaderException("Cannot read NaN as a decimal.");
|
||||
}
|
||||
|
||||
SetToken(JsonToken.Float, double.NaN);
|
||||
}
|
||||
@@ -1676,7 +1789,9 @@ namespace Newtonsoft.Json
|
||||
get
|
||||
{
|
||||
if (CurrentState == State.Start && LinePosition == 0 && TokenType != JsonToken.Comment)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _lineNumber;
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ namespace Newtonsoft.Json
|
||||
/// <summary>
|
||||
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
/// <returns>A <see cref="Nullable{DateTime}"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
public override DateTime? ReadAsDateTime()
|
||||
{
|
||||
DateTime? dateTime = _reader.ReadAsDateTime();
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace Newtonsoft.Json.Linq
|
||||
/// <summary>
|
||||
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
/// <returns>A <see cref="Nullable{DateTime}"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
public override DateTime? ReadAsDateTime()
|
||||
{
|
||||
return ReadAsDateTimeInternal();
|
||||
|
||||
@@ -1180,7 +1180,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
contract.ItemContract = GetContractSafe(contract.DictionaryValueType);
|
||||
|
||||
JsonConverter dictionaryValueConverter = contract.ItemConverter ?? GetConverter(contract.ItemContract, null, contract, containerProperty);
|
||||
PrimitiveTypeCode keyTypeCode = (contract.KeyContract is JsonPrimitiveContract) ? ((JsonPrimitiveContract)contract.KeyContract).TypeCode : PrimitiveTypeCode.Empty;
|
||||
PrimitiveTypeCode keyTypeCode = (contract.KeyContract is JsonPrimitiveContract) ? ((JsonPrimitiveContract) contract.KeyContract).TypeCode : PrimitiveTypeCode.Empty;
|
||||
|
||||
bool finished = false;
|
||||
do
|
||||
@@ -1196,30 +1196,43 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
try
|
||||
{
|
||||
DateParseHandling dateParseHandling;
|
||||
// this is for correctly reading ISO and MS formatted dictionary keys
|
||||
switch (keyTypeCode)
|
||||
{
|
||||
case PrimitiveTypeCode.DateTime:
|
||||
case PrimitiveTypeCode.DateTimeNullable:
|
||||
dateParseHandling = DateParseHandling.DateTime;
|
||||
{
|
||||
DateTime dt;
|
||||
if (DateTimeUtils.TryParseDateTime(keyValue.ToString(), reader.DateTimeZoneHandling, reader.DateFormatString, reader.Culture, out dt))
|
||||
{
|
||||
keyValue = dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if !NET20
|
||||
case PrimitiveTypeCode.DateTimeOffset:
|
||||
case PrimitiveTypeCode.DateTimeOffsetNullable:
|
||||
dateParseHandling = DateParseHandling.DateTimeOffset;
|
||||
{
|
||||
DateTimeOffset dt;
|
||||
if (DateTimeUtils.TryParseDateTimeOffset(keyValue.ToString(), reader.DateFormatString, reader.Culture, out dt))
|
||||
{
|
||||
keyValue = dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
dateParseHandling = DateParseHandling.None;
|
||||
keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType);
|
||||
break;
|
||||
}
|
||||
|
||||
// this is for correctly reading ISO and MS formatted dictionary keys
|
||||
object dt;
|
||||
if (dateParseHandling != DateParseHandling.None && DateTimeUtils.TryParseDateTime(keyValue.ToString(), dateParseHandling, reader.DateTimeZoneHandling, reader.DateFormatString, reader.Culture, out dt))
|
||||
keyValue = dt;
|
||||
else
|
||||
keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1227,7 +1240,9 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
|
||||
if (!ReadForType(reader, contract.ItemContract, dictionaryValueConverter != null))
|
||||
{
|
||||
throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object.");
|
||||
}
|
||||
|
||||
object itemValue;
|
||||
if (dictionaryValueConverter != null && dictionaryValueConverter.CanRead)
|
||||
|
||||
@@ -980,9 +980,11 @@ namespace Newtonsoft.Json.Serialization
|
||||
JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract;
|
||||
if (primitiveContract.TypeCode == PrimitiveTypeCode.DateTime || primitiveContract.TypeCode == PrimitiveTypeCode.DateTimeNullable)
|
||||
{
|
||||
DateTime dt = DateTimeUtils.EnsureDateTime((DateTime)name, writer.DateTimeZoneHandling);
|
||||
|
||||
escape = false;
|
||||
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
|
||||
DateTimeUtils.WriteDateTimeString(sw, (DateTime)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture);
|
||||
DateTimeUtils.WriteDateTimeString(sw, dt, writer.DateFormatHandling, writer.DateFormatString, writer.Culture);
|
||||
return sw.ToString();
|
||||
}
|
||||
#if !NET20
|
||||
|
||||
@@ -67,8 +67,8 @@ namespace Newtonsoft.Json.Utilities
|
||||
public int ZoneMinute;
|
||||
public ParserTimeZone Zone;
|
||||
|
||||
private string _text;
|
||||
private int _length;
|
||||
private char[] _text;
|
||||
private int _end;
|
||||
|
||||
private static readonly int[] Power10;
|
||||
|
||||
@@ -88,13 +88,15 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
private const short MaxFractionDigits = 7;
|
||||
|
||||
public bool Parse(string text)
|
||||
public bool Parse(char[] text, int startIndex, int length)
|
||||
{
|
||||
_text = text;
|
||||
_length = text.Length;
|
||||
_end = startIndex + length;
|
||||
|
||||
if (ParseDate(0) && ParseChar(Lzyyyy_MM_dd, 'T') && ParseTimeAndZoneAndWhitespace(Lzyyyy_MM_ddT))
|
||||
if (ParseDate(startIndex) && ParseChar(Lzyyyy_MM_dd + startIndex, 'T') && ParseTimeAndZoneAndWhitespace(Lzyyyy_MM_ddT + startIndex))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -138,11 +140,13 @@ namespace Newtonsoft.Json.Utilities
|
||||
Fraction = 0;
|
||||
int numberOfDigits = 0;
|
||||
|
||||
while (++start < _length && numberOfDigits < MaxFractionDigits)
|
||||
while (++start < _end && numberOfDigits < MaxFractionDigits)
|
||||
{
|
||||
int digit = _text[start] - '0';
|
||||
if (digit < 0 || digit > 9)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Fraction = (Fraction * 10) + digit;
|
||||
|
||||
@@ -152,7 +156,9 @@ namespace Newtonsoft.Json.Utilities
|
||||
if (numberOfDigits < MaxFractionDigits)
|
||||
{
|
||||
if (numberOfDigits == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Fraction *= Power10[MaxFractionDigits - numberOfDigits];
|
||||
}
|
||||
@@ -162,7 +168,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
private bool ParseZone(int start)
|
||||
{
|
||||
if (start < _length)
|
||||
if (start < _end)
|
||||
{
|
||||
char ch = _text[start];
|
||||
if (ch == 'Z' || ch == 'z')
|
||||
@@ -172,7 +178,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
else
|
||||
{
|
||||
if (start + 2 < _length
|
||||
if (start + 2 < _end
|
||||
&& Parse2Digit(start + Lz_, out ZoneHour)
|
||||
&& ZoneHour <= 99)
|
||||
{
|
||||
@@ -190,13 +196,13 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
if (start < _length)
|
||||
if (start < _end)
|
||||
{
|
||||
if (ParseChar(start, ':'))
|
||||
{
|
||||
start += 1;
|
||||
|
||||
if (start + 1 < _length
|
||||
if (start + 1 < _end
|
||||
&& Parse2Digit(start, out ZoneMinute)
|
||||
&& ZoneMinute <= 99)
|
||||
{
|
||||
@@ -205,7 +211,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
else
|
||||
{
|
||||
if (start + 1 < _length
|
||||
if (start + 1 < _end
|
||||
&& Parse2Digit(start, out ZoneMinute)
|
||||
&& ZoneMinute <= 99)
|
||||
{
|
||||
@@ -216,12 +222,12 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
return (start == _length);
|
||||
return (start == _end);
|
||||
}
|
||||
|
||||
private bool Parse4Digit(int start, out int num)
|
||||
{
|
||||
if (start + 3 < _length)
|
||||
if (start + 3 < _end)
|
||||
{
|
||||
int digit1 = _text[start] - '0';
|
||||
int digit2 = _text[start + 1] - '0';
|
||||
@@ -242,7 +248,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
private bool Parse2Digit(int start, out int num)
|
||||
{
|
||||
if (start + 1 < _length)
|
||||
if (start + 1 < _end)
|
||||
{
|
||||
int digit1 = _text[start] - '0';
|
||||
int digit2 = _text[start + 1] - '0';
|
||||
@@ -259,7 +265,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
private bool ParseChar(int start, char ch)
|
||||
{
|
||||
return (start < _length && _text[start] == ch);
|
||||
return (start < _end && _text[start] == ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
internal static class DateTimeUtils
|
||||
{
|
||||
internal static readonly long InitialJavaScriptDateTicks = 621355968000000000;
|
||||
private const string IsoDateFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK";
|
||||
|
||||
private const int DaysPer100Years = 36524;
|
||||
private const int DaysPer400Years = 146097;
|
||||
@@ -131,7 +132,9 @@ namespace Newtonsoft.Json.Utilities
|
||||
private static long ToUniversalTicks(DateTime dateTime)
|
||||
{
|
||||
if (dateTime.Kind == DateTimeKind.Utc)
|
||||
{
|
||||
return dateTime.Ticks;
|
||||
}
|
||||
|
||||
return ToUniversalTicks(dateTime, dateTime.GetUtcOffset());
|
||||
}
|
||||
@@ -141,14 +144,20 @@ namespace Newtonsoft.Json.Utilities
|
||||
// special case min and max value
|
||||
// they never have a timezone appended to avoid issues
|
||||
if (dateTime.Kind == DateTimeKind.Utc || dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue)
|
||||
{
|
||||
return dateTime.Ticks;
|
||||
}
|
||||
|
||||
long ticks = dateTime.Ticks - offset.Ticks;
|
||||
if (ticks > 3155378975999999999L)
|
||||
{
|
||||
return 3155378975999999999L;
|
||||
}
|
||||
|
||||
if (ticks < 0L)
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
@@ -187,235 +196,393 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
|
||||
#region Parse
|
||||
internal static bool TryParseDateIso(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt)
|
||||
|
||||
internal static bool TryParseDateTimeIso(StringReference text, DateTimeZoneHandling dateTimeZoneHandling, out DateTime dt)
|
||||
{
|
||||
DateTimeParser dateTimeParser = new DateTimeParser();
|
||||
if (!dateTimeParser.Parse(text))
|
||||
if (!dateTimeParser.Parse(text.Chars, text.StartIndex, text.Length))
|
||||
{
|
||||
dt = null;
|
||||
dt = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime d = new DateTime(dateTimeParser.Year, dateTimeParser.Month, dateTimeParser.Day, dateTimeParser.Hour, dateTimeParser.Minute, dateTimeParser.Second);
|
||||
d = d.AddTicks(dateTimeParser.Fraction);
|
||||
|
||||
#if !NET20
|
||||
if (dateParseHandling == DateParseHandling.DateTimeOffset)
|
||||
long ticks;
|
||||
|
||||
switch (dateTimeParser.Zone)
|
||||
{
|
||||
TimeSpan offset;
|
||||
case ParserTimeZone.Utc:
|
||||
d = new DateTime(d.Ticks, DateTimeKind.Utc);
|
||||
break;
|
||||
|
||||
switch (dateTimeParser.Zone)
|
||||
case ParserTimeZone.LocalWestOfUtc:
|
||||
{
|
||||
case ParserTimeZone.Utc:
|
||||
offset = new TimeSpan(0L);
|
||||
break;
|
||||
case ParserTimeZone.LocalWestOfUtc:
|
||||
offset = new TimeSpan(-dateTimeParser.ZoneHour, -dateTimeParser.ZoneMinute, 0);
|
||||
break;
|
||||
case ParserTimeZone.LocalEastOfUtc:
|
||||
offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
|
||||
break;
|
||||
default:
|
||||
offset = TimeZoneInfo.Local.GetUtcOffset(d);
|
||||
break;
|
||||
}
|
||||
|
||||
long ticks = d.Ticks - offset.Ticks;
|
||||
if (ticks < 0 || ticks > 3155378975999999999)
|
||||
{
|
||||
dt = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
dt = new DateTimeOffset(d, offset);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
long ticks;
|
||||
|
||||
switch (dateTimeParser.Zone)
|
||||
{
|
||||
case ParserTimeZone.Utc:
|
||||
d = new DateTime(d.Ticks, DateTimeKind.Utc);
|
||||
break;
|
||||
|
||||
case ParserTimeZone.LocalWestOfUtc:
|
||||
TimeSpan offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
|
||||
ticks = d.Ticks + offset.Ticks;
|
||||
if (ticks <= DateTime.MaxValue.Ticks)
|
||||
{
|
||||
TimeSpan offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
|
||||
ticks = d.Ticks + offset.Ticks;
|
||||
if (ticks <= DateTime.MaxValue.Ticks)
|
||||
{
|
||||
d = new DateTime(ticks, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
ticks += d.GetUtcOffset().Ticks;
|
||||
if (ticks > DateTime.MaxValue.Ticks)
|
||||
ticks = DateTime.MaxValue.Ticks;
|
||||
|
||||
d = new DateTime(ticks, DateTimeKind.Local);
|
||||
}
|
||||
break;
|
||||
d = new DateTime(ticks, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
case ParserTimeZone.LocalEastOfUtc:
|
||||
else
|
||||
{
|
||||
TimeSpan offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
|
||||
ticks = d.Ticks - offset.Ticks;
|
||||
if (ticks >= DateTime.MinValue.Ticks)
|
||||
{
|
||||
d = new DateTime(ticks, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
ticks += d.GetUtcOffset().Ticks;
|
||||
if (ticks < DateTime.MinValue.Ticks)
|
||||
ticks = DateTime.MinValue.Ticks;
|
||||
ticks += d.GetUtcOffset().Ticks;
|
||||
if (ticks > DateTime.MaxValue.Ticks)
|
||||
ticks = DateTime.MaxValue.Ticks;
|
||||
|
||||
d = new DateTime(ticks, DateTimeKind.Local);
|
||||
}
|
||||
break;
|
||||
d = new DateTime(ticks, DateTimeKind.Local);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ParserTimeZone.LocalEastOfUtc:
|
||||
{
|
||||
TimeSpan offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
|
||||
ticks = d.Ticks - offset.Ticks;
|
||||
if (ticks >= DateTime.MinValue.Ticks)
|
||||
{
|
||||
d = new DateTime(ticks, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
ticks += d.GetUtcOffset().Ticks;
|
||||
if (ticks < DateTime.MinValue.Ticks)
|
||||
ticks = DateTime.MinValue.Ticks;
|
||||
|
||||
dt = EnsureDateTime(d, dateTimeZoneHandling);
|
||||
return true;
|
||||
d = new DateTime(ticks, DateTimeKind.Local);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dt = EnsureDateTime(d, dateTimeZoneHandling);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool TryParseDateTime(string s, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out object dt)
|
||||
#if !NET20
|
||||
internal static bool TryParseDateTimeOffsetIso(StringReference text, out DateTimeOffset dt)
|
||||
{
|
||||
DateTimeParser dateTimeParser = new DateTimeParser();
|
||||
if (!dateTimeParser.Parse(text.Chars, text.StartIndex, text.Length))
|
||||
{
|
||||
dt = default(DateTimeOffset);
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime d = new DateTime(dateTimeParser.Year, dateTimeParser.Month, dateTimeParser.Day, dateTimeParser.Hour, dateTimeParser.Minute, dateTimeParser.Second);
|
||||
d = d.AddTicks(dateTimeParser.Fraction);
|
||||
|
||||
TimeSpan offset;
|
||||
|
||||
switch (dateTimeParser.Zone)
|
||||
{
|
||||
case ParserTimeZone.Utc:
|
||||
offset = new TimeSpan(0L);
|
||||
break;
|
||||
case ParserTimeZone.LocalWestOfUtc:
|
||||
offset = new TimeSpan(-dateTimeParser.ZoneHour, -dateTimeParser.ZoneMinute, 0);
|
||||
break;
|
||||
case ParserTimeZone.LocalEastOfUtc:
|
||||
offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
|
||||
break;
|
||||
default:
|
||||
offset = TimeZoneInfo.Local.GetUtcOffset(d);
|
||||
break;
|
||||
}
|
||||
|
||||
long ticks = d.Ticks - offset.Ticks;
|
||||
if (ticks < 0 || ticks > 3155378975999999999)
|
||||
{
|
||||
dt = default(DateTimeOffset);
|
||||
return false;
|
||||
}
|
||||
|
||||
dt = new DateTimeOffset(d, offset);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static bool TryParseDateTime(StringReference s, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out DateTime dt)
|
||||
{
|
||||
if (s.Length > 0)
|
||||
{
|
||||
int i = s.StartIndex;
|
||||
if (s[i] == '/')
|
||||
{
|
||||
if (s.Length >= 9 && s.StartsWith("/Date(") && s.EndsWith(")/"))
|
||||
{
|
||||
if (TryParseDateTimeMicrosoft(s, dateTimeZoneHandling, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[i]) && s[i + 10] == 'T')
|
||||
{
|
||||
if (TryParseDateTimeIso(s, dateTimeZoneHandling, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(dateFormatString))
|
||||
{
|
||||
if (TryParseDateTimeExact(s.ToString(), dateTimeZoneHandling, dateFormatString, culture, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dt = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryParseDateTime(string s, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out DateTime dt)
|
||||
{
|
||||
if (s.Length > 0)
|
||||
{
|
||||
if (s[0] == '/')
|
||||
{
|
||||
if (s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal))
|
||||
if (s.Length >= 9 && s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal))
|
||||
{
|
||||
if (TryParseDateMicrosoft(s, dateParseHandling, dateTimeZoneHandling, out dt))
|
||||
if (TryParseDateTimeMicrosoft(new StringReference(s.ToCharArray(), 0, s.Length), dateTimeZoneHandling, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[0]) && s[10] == 'T')
|
||||
{
|
||||
if (TryParseDateIso(s, dateParseHandling, dateTimeZoneHandling, out dt))
|
||||
if (DateTime.TryParseExact(s, IsoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt))
|
||||
{
|
||||
dt = EnsureDateTime(dt, dateTimeZoneHandling);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(dateFormatString))
|
||||
{
|
||||
if (TryParseDateExact(s, dateParseHandling, dateTimeZoneHandling, dateFormatString, culture, out dt))
|
||||
if (TryParseDateTimeExact(s, dateTimeZoneHandling, dateFormatString, culture, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dt = null;
|
||||
dt = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryParseDateMicrosoft(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt)
|
||||
#if !NET20
|
||||
internal static bool TryParseDateTimeOffset(StringReference s, string dateFormatString, CultureInfo culture, out DateTimeOffset dt)
|
||||
{
|
||||
string value = text.Substring(6, text.Length - 8);
|
||||
DateTimeKind kind = DateTimeKind.Utc;
|
||||
if (s.Length > 0)
|
||||
{
|
||||
int i = s.StartIndex;
|
||||
if (s[i] == '/')
|
||||
{
|
||||
if (s.Length >= 9 && s.StartsWith("/Date(") && s.EndsWith(")/"))
|
||||
{
|
||||
if (TryParseDateTimeOffsetMicrosoft(s, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[i]) && s[i + 10] == 'T')
|
||||
{
|
||||
if (TryParseDateTimeOffsetIso(s, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int index = value.IndexOf('+', 1);
|
||||
if (!string.IsNullOrEmpty(dateFormatString))
|
||||
{
|
||||
if (TryParseDateTimeOffsetExact(s.ToString(), dateFormatString, culture, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dt = default(DateTimeOffset);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryParseDateTimeOffset(string s, string dateFormatString, CultureInfo culture, out DateTimeOffset dt)
|
||||
{
|
||||
if (s.Length > 0)
|
||||
{
|
||||
if (s[0] == '/')
|
||||
{
|
||||
if (s.Length >= 9 && s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal))
|
||||
{
|
||||
if (TryParseDateTimeOffsetMicrosoft(new StringReference(s.ToCharArray(), 0, s.Length), out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[0]) && s[10] == 'T')
|
||||
{
|
||||
if (DateTimeOffset.TryParseExact(s, IsoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt))
|
||||
if (TryParseDateTimeOffsetIso(new StringReference(s.ToCharArray(), 0, s.Length), out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(dateFormatString))
|
||||
{
|
||||
if (TryParseDateTimeOffsetExact(s, dateFormatString, culture, out dt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dt = default(DateTimeOffset);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static bool TryParseMicrosoftDate(StringReference text, out long ticks, out TimeSpan offset, out DateTimeKind kind)
|
||||
{
|
||||
kind = DateTimeKind.Utc;
|
||||
|
||||
int index = text.IndexOf('+', 7, text.Length - 8);
|
||||
|
||||
if (index == -1)
|
||||
index = value.IndexOf('-', 1);
|
||||
|
||||
#if !NET20
|
||||
TimeSpan offset = TimeSpan.Zero;
|
||||
#endif
|
||||
index = text.IndexOf('-', 7, text.Length - 8);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
kind = DateTimeKind.Local;
|
||||
#if !NET20
|
||||
offset = ReadOffset(value.Substring(index));
|
||||
#endif
|
||||
value = value.Substring(0, index);
|
||||
}
|
||||
|
||||
long javaScriptTicks;
|
||||
if (!long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out javaScriptTicks))
|
||||
{
|
||||
dt = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime utcDateTime = ConvertJavaScriptTicksToDateTime(javaScriptTicks);
|
||||
|
||||
#if !NET20
|
||||
if (dateParseHandling == DateParseHandling.DateTimeOffset)
|
||||
{
|
||||
dt = new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
DateTime dateTime;
|
||||
switch (kind)
|
||||
{
|
||||
case DateTimeKind.Unspecified:
|
||||
dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
|
||||
break;
|
||||
case DateTimeKind.Local:
|
||||
dateTime = utcDateTime.ToLocalTime();
|
||||
break;
|
||||
default:
|
||||
dateTime = utcDateTime;
|
||||
break;
|
||||
}
|
||||
|
||||
dt = EnsureDateTime(dateTime, dateTimeZoneHandling);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseDateExact(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out object dt)
|
||||
{
|
||||
#if !NET20
|
||||
if (dateParseHandling == DateParseHandling.DateTimeOffset)
|
||||
{
|
||||
DateTimeOffset temp;
|
||||
if (DateTimeOffset.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out temp))
|
||||
if (!TryReadOffset(text, index, out offset))
|
||||
{
|
||||
dt = temp;
|
||||
return true;
|
||||
ticks = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
DateTime temp;
|
||||
if (DateTime.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out temp))
|
||||
{
|
||||
temp = EnsureDateTime(temp, dateTimeZoneHandling);
|
||||
dt = temp;
|
||||
return true;
|
||||
}
|
||||
offset = TimeSpan.Zero;
|
||||
index = text.Length - 2;
|
||||
}
|
||||
|
||||
dt = null;
|
||||
return (ConvertUtils.Int64TryParse(text.Chars, 6, index - 6, out ticks) == ParseResult.Success);
|
||||
}
|
||||
|
||||
private static bool TryParseDateTimeMicrosoft(StringReference text, DateTimeZoneHandling dateTimeZoneHandling, out DateTime dt)
|
||||
{
|
||||
long ticks;
|
||||
TimeSpan offset;
|
||||
DateTimeKind kind;
|
||||
|
||||
if (!TryParseMicrosoftDate(text, out ticks, out offset, out kind))
|
||||
{
|
||||
dt = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime utcDateTime = ConvertJavaScriptTicksToDateTime(ticks);
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case DateTimeKind.Unspecified:
|
||||
dt = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
|
||||
break;
|
||||
case DateTimeKind.Local:
|
||||
dt = utcDateTime.ToLocalTime();
|
||||
break;
|
||||
default:
|
||||
dt = utcDateTime;
|
||||
break;
|
||||
}
|
||||
|
||||
dt = EnsureDateTime(dt, dateTimeZoneHandling);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseDateTimeExact(string text, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out DateTime dt)
|
||||
{
|
||||
DateTime temp;
|
||||
if (DateTime.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out temp))
|
||||
{
|
||||
temp = EnsureDateTime(temp, dateTimeZoneHandling);
|
||||
dt = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
dt = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
private static TimeSpan ReadOffset(string offsetText)
|
||||
private static bool TryParseDateTimeOffsetMicrosoft(StringReference text, out DateTimeOffset dt)
|
||||
{
|
||||
bool negative = (offsetText[0] == '-');
|
||||
long ticks;
|
||||
TimeSpan offset;
|
||||
DateTimeKind kind;
|
||||
|
||||
int hours = int.Parse(offsetText.Substring(1, 2), NumberStyles.Integer, CultureInfo.InvariantCulture);
|
||||
int minutes = 0;
|
||||
if (offsetText.Length >= 5)
|
||||
minutes = int.Parse(offsetText.Substring(3, 2), NumberStyles.Integer, CultureInfo.InvariantCulture);
|
||||
if (!TryParseMicrosoftDate(text, out ticks, out offset, out kind))
|
||||
{
|
||||
dt = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeSpan offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes);
|
||||
if (negative)
|
||||
offset = offset.Negate();
|
||||
DateTime utcDateTime = ConvertJavaScriptTicksToDateTime(ticks);
|
||||
|
||||
return offset;
|
||||
dt = new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseDateTimeOffsetExact(string text, string dateFormatString, CultureInfo culture, out DateTimeOffset dt)
|
||||
{
|
||||
DateTimeOffset temp;
|
||||
if (DateTimeOffset.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out temp))
|
||||
{
|
||||
dt = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
dt = default(DateTimeOffset);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static bool TryReadOffset(StringReference offsetText, int startIndex, out TimeSpan offset)
|
||||
{
|
||||
bool negative = (offsetText[startIndex] == '-');
|
||||
|
||||
int hours;
|
||||
if (ConvertUtils.Int32TryParse(offsetText.Chars, startIndex + 1, 2, out hours) != ParseResult.Success)
|
||||
{
|
||||
offset = default(TimeSpan);
|
||||
return false;
|
||||
}
|
||||
|
||||
int minutes = 0;
|
||||
if (offsetText.Length - startIndex > 5)
|
||||
{
|
||||
if (ConvertUtils.Int32TryParse(offsetText.Chars, startIndex + 3, 2, out minutes) != ParseResult.Success)
|
||||
{
|
||||
offset = default(TimeSpan);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes);
|
||||
if (negative)
|
||||
{
|
||||
offset = offset.Negate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Write
|
||||
|
||||
@@ -48,7 +48,8 @@ namespace Newtonsoft.Json.Utilities
|
||||
return new char[minSize];
|
||||
}
|
||||
|
||||
return bufferPool.RentBuffer(minSize);
|
||||
char[] buffer = bufferPool.RentBuffer(minSize);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static void ReturnBuffer(IJsonBufferPool<char> bufferPool, ref char[] buffer)
|
||||
@@ -236,7 +237,9 @@ namespace Newtonsoft.Json.Utilities
|
||||
// the unicode text is already in the buffer
|
||||
// copy it over when creating new buffer
|
||||
if (isEscapedUnicodeText)
|
||||
{
|
||||
Array.Copy(writeBuffer, newBuffer, UnicodeTextLength);
|
||||
}
|
||||
|
||||
BufferUtils.ReturnBuffer(bufferPool, ref writeBuffer);
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
internal struct StringReference
|
||||
@@ -31,6 +33,11 @@ namespace Newtonsoft.Json.Utilities
|
||||
private readonly int _startIndex;
|
||||
private readonly int _length;
|
||||
|
||||
public char this[int i]
|
||||
{
|
||||
get { return _chars[i]; }
|
||||
}
|
||||
|
||||
public char[] Chars
|
||||
{
|
||||
get { return _chars; }
|
||||
@@ -58,4 +65,64 @@ namespace Newtonsoft.Json.Utilities
|
||||
return new string(_chars, _startIndex, _length);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class StringReferenceExtensions
|
||||
{
|
||||
public static int IndexOf(this StringReference s, char c)
|
||||
{
|
||||
return IndexOf(s, c, 0, 0);
|
||||
}
|
||||
|
||||
public static int IndexOf(this StringReference s, char c, int startIndex, int length)
|
||||
{
|
||||
int index = Array.IndexOf(s.Chars, c, s.StartIndex + startIndex, length);
|
||||
if (index == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return index - s.StartIndex;
|
||||
}
|
||||
|
||||
public static bool StartsWith(this StringReference s, string text)
|
||||
{
|
||||
if (text.Length > s.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char[] chars = s.Chars;
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] != chars[i + s.StartIndex])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool EndsWith(this StringReference s, string text)
|
||||
{
|
||||
if (text.Length > s.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char[] chars = s.Chars;
|
||||
|
||||
int start = s.StartIndex + s.Length - text.Length;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] != chars[i + start])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user