-Fixed DateTimeZoneHandling not being used when writing DateTime dictionary keys

-Buffer pooling fixes
-Improved date parsing performance
This commit is contained in:
James Newton-King
2015-11-14 15:22:59 +13:00
parent f3822513df
commit a3eb2d596c
16 changed files with 689 additions and 227 deletions
+1 -4
View File
@@ -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
+1 -1
View File
@@ -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();
+3 -7
View File
@@ -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;
+126 -11
View File
@@ -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;
}
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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
+21 -15
View File
@@ -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);
}
}
}
+325 -158
View File
@@ -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;
}
}
}