diff --git a/CryptoExchange.Net.UnitTests/ConverterTests.cs b/CryptoExchange.Net.UnitTests/ConverterTests.cs index 83c7fab..b2022c8 100644 --- a/CryptoExchange.Net.UnitTests/ConverterTests.cs +++ b/CryptoExchange.Net.UnitTests/ConverterTests.cs @@ -18,10 +18,12 @@ namespace CryptoExchange.Net.UnitTests [TestCase("1620777600.000")] [TestCase("1620777600000")] [TestCase("2021-05-12T00:00:00.000Z")] - public void TestDateTimeConverterString(string input) + [TestCase("", true)] + [TestCase(" ", true)] + public void TestDateTimeConverterString(string input, bool expectNull = false) { var output = JsonConvert.DeserializeObject($"{{ \"time\": \"{input}\" }}"); - Assert.AreEqual(output.Time, new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc)); + Assert.AreEqual(output.Time, expectNull ? null: new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc)); } [TestCase(1620777600.000)] @@ -36,13 +38,14 @@ namespace CryptoExchange.Net.UnitTests [TestCase(1620777600000)] [TestCase(1620777600000000)] [TestCase(1620777600000000000)] - public void TestDateTimeConverterLong(long input) + [TestCase(0, true)] + public void TestDateTimeConverterLong(long input, bool expectNull = false) { var output = JsonConvert.DeserializeObject($"{{ \"time\": {input} }}"); - Assert.AreEqual(output.Time, new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc)); + Assert.AreEqual(output.Time, expectNull ? null : new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc)); } - [Test] + [TestCase()] public void TestDateTimeConverterNull() { var output = JsonConvert.DeserializeObject($"{{ \"time\": null }}"); diff --git a/CryptoExchange.Net/Converters/DateTimeConverter.cs b/CryptoExchange.Net/Converters/DateTimeConverter.cs index 0e2b25d..f2edcb6 100644 --- a/CryptoExchange.Net/Converters/DateTimeConverter.cs +++ b/CryptoExchange.Net/Converters/DateTimeConverter.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using System; using System.Diagnostics; +using System.Globalization; namespace CryptoExchange.Net.Converters { @@ -28,6 +29,8 @@ namespace CryptoExchange.Net.Converters if(reader.TokenType is JsonToken.Integer) { var longValue = (long)reader.Value; + if (longValue == 0) + return null; if (longValue < 1999999999) return ConvertFromSeconds(longValue); if (longValue < 1999999999999) @@ -48,6 +51,9 @@ namespace CryptoExchange.Net.Converters else if(reader.TokenType is JsonToken.String) { var stringValue = (string)reader.Value; + if (string.IsNullOrWhiteSpace(stringValue)) + return null; + if (stringValue.Length == 8) { // Parse 20211103 format @@ -74,7 +80,7 @@ namespace CryptoExchange.Net.Converters return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc); } - if (double.TryParse(stringValue, out var doubleValue)) + if (double.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleValue)) { // Parse 1637745563.000 format if (doubleValue < 1999999999) @@ -110,20 +116,63 @@ namespace CryptoExchange.Net.Converters } } + /// + /// Convert a seconds since epoch (01-01-1970) value to DateTime + /// + /// + /// public static DateTime ConvertFromSeconds(double seconds) => _epoch.AddSeconds(seconds); - public static DateTime ConvertFromMilliseconds(double milliseconds) => _epoch.AddMilliseconds(milliseconds); + /// + /// Convert a milliseconds since epoch (01-01-1970) value to DateTime + /// + /// + /// + public static DateTime ConvertFromMilliseconds(double milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond)); + /// + /// Convert a microseconds since epoch (01-01-1970) value to DateTime + /// + /// + /// public static DateTime ConvertFromMicroseconds(long microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * ticksPerMicrosecond)); + /// + /// Convert a nanoseconds since epoch (01-01-1970) value to DateTime + /// + /// + /// public static DateTime ConvertFromNanoseconds(long nanoseconds) => _epoch.AddTicks((long)Math.Round(nanoseconds * ticksPerNanosecond)); - public static long ConvertToSeconds(DateTime time) => (long)Math.Round((time - _epoch).TotalSeconds); - public static long ConvertToMilliseconds(DateTime time) => (long)Math.Round((time - _epoch).TotalMilliseconds); - public static long ConvertToMicroseconds(DateTime time) => (long)Math.Round((time - _epoch).Ticks / ticksPerMicrosecond); - public static long ConvertToNanoseconds(DateTime time) => (long)Math.Round((time - _epoch).Ticks / ticksPerNanosecond); + /// + /// Convert a DateTime value to seconds since epoch (01-01-1970) value + /// + /// + /// + public static long? ConvertToSeconds(DateTime? time) => time == null ? null: (long)Math.Round((time.Value - _epoch).TotalSeconds); + /// + /// Convert a DateTime value to milliseconds since epoch (01-01-1970) value + /// + /// + /// + public static long? ConvertToMilliseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalMilliseconds); + /// + /// Convert a DateTime value to microseconds since epoch (01-01-1970) value + /// + /// + /// + public static long? ConvertToMicroseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / ticksPerMicrosecond); + /// + /// Convert a DateTime value to nanoseconds since epoch (01-01-1970) value + /// + /// + /// + public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / ticksPerNanosecond); /// public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - if (value == null) + var datetimeValue = (DateTime?)value; + if (datetimeValue == null) + writer.WriteValue((DateTime?)null); + if(datetimeValue == default(DateTime)) writer.WriteValue((DateTime?)null); else writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalMilliseconds)); diff --git a/CryptoExchange.Net/Converters/TimestampConverter.cs b/CryptoExchange.Net/Converters/TimestampConverter.cs deleted file mode 100644 index b070771..0000000 --- a/CryptoExchange.Net/Converters/TimestampConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace CryptoExchange.Net.Converters -{ - /// - /// Converter for milliseconds to datetime - /// - public class TimestampConverter : JsonConverter - { - /// - public override bool CanConvert(Type objectType) - { - return objectType == typeof(DateTime); - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return null; - - if (reader.Value is double d) - return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(d); - - var t = long.Parse(reader.Value.ToString()); - return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(t); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if(value == null) - writer.WriteValue((DateTime?)null); - else - writer.WriteValue((long)Math.Round(((DateTime)value - new DateTime(1970, 1, 1)).TotalMilliseconds)); - } - } -} diff --git a/CryptoExchange.Net/Converters/TimestampMicroSecondsConverter.cs b/CryptoExchange.Net/Converters/TimestampMicroSecondsConverter.cs deleted file mode 100644 index 033e25f..0000000 --- a/CryptoExchange.Net/Converters/TimestampMicroSecondsConverter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace CryptoExchange.Net.Converters -{ - /// - /// Converter for nanoseconds to datetime - /// - public class TimestampMicroSecondsConverter : JsonConverter - { - private const decimal ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000; - - /// - public override bool CanConvert(Type objectType) - { - return objectType == typeof(DateTime); - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return null; - - var nanoSeconds = long.Parse(reader.Value.ToString()); - return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks((long)Math.Round(nanoSeconds * ticksPerMicrosecond)); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer.WriteValue((long)Math.Round(((DateTime)value! - new DateTime(1970, 1, 1)).Ticks / ticksPerMicrosecond)); - } - } -} diff --git a/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs b/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs deleted file mode 100644 index 90b023c..0000000 --- a/CryptoExchange.Net/Converters/TimestampNanoSecondsConverter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace CryptoExchange.Net.Converters -{ - /// - /// Converter for nanoseconds to datetime - /// - public class TimestampNanoSecondsConverter : JsonConverter - { - private const decimal ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000m / 1000; - - /// - public override bool CanConvert(Type objectType) - { - return objectType == typeof(DateTime); - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return null; - - var nanoSeconds = long.Parse(reader.Value.ToString()); - return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks((long)Math.Round(nanoSeconds * ticksPerNanosecond)); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer.WriteValue((long)Math.Round(((DateTime)value! - new DateTime(1970, 1, 1)).Ticks / ticksPerNanosecond)); - } - } -} diff --git a/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs b/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs deleted file mode 100644 index aabd462..0000000 --- a/CryptoExchange.Net/Converters/TimestampSecondsConverter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Globalization; -using Newtonsoft.Json; - -namespace CryptoExchange.Net.Converters -{ - /// - /// Converter for seconds to datetime - /// - public class TimestampSecondsConverter : JsonConverter - { - /// - public override bool CanConvert(Type objectType) - { - return objectType == typeof(DateTime); - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return null; - - if (reader.Value is double d) - return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(d); - - var t = double.Parse(reader.Value.ToString(), CultureInfo.InvariantCulture); - // Set ticks instead of seconds or milliseconds, because AddSeconds/AddMilliseconds rounds to nearest millisecond - return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks((long)(t * TimeSpan.TicksPerSecond)); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value == null) - writer.WriteValue((DateTime?)null); - else - writer.WriteValue((long)Math.Round(((DateTime)value! - new DateTime(1970, 1, 1)).TotalSeconds)); - } - } -} diff --git a/CryptoExchange.Net/Converters/TimestampStringConverter.cs b/CryptoExchange.Net/Converters/TimestampStringConverter.cs deleted file mode 100644 index 9f97040..0000000 --- a/CryptoExchange.Net/Converters/TimestampStringConverter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace CryptoExchange.Net.Converters -{ - /// - /// converter for datetime string (yyyymmdd) to datetime - /// - public class TimestampStringConverter : JsonConverter - { - /// - public override bool CanConvert(Type objectType) - { - return objectType == typeof(DateTime); - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return null; - - var value = reader.Value.ToString(); - if (value.Length == 8) - return new DateTime(int.Parse(value.Substring(0, 4)), int.Parse(value.Substring(4, 2)), int.Parse(value.Substring(6, 2)), 0, 0, 0, DateTimeKind.Utc); - else if(value.Length == 6) - return new DateTime(int.Parse(value.Substring(0, 2)), int.Parse(value.Substring(2, 2)), int.Parse(value.Substring(4, 2)), 0, 0, 0, DateTimeKind.Utc); - - throw new Exception("Unknown datetime value: " + value); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value == null) - writer.WriteValue((DateTime?)null); - else - { - var dateTimeValue = (DateTime)value; - writer.WriteValue(int.Parse($"{dateTimeValue.Year}{dateTimeValue.Month}{dateTimeValue.Day}")); - } - } - } -} diff --git a/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs b/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs deleted file mode 100644 index efd84f8..0000000 --- a/CryptoExchange.Net/Converters/UTCDateTimeConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace CryptoExchange.Net.Converters -{ - /// - /// Converter for utc datetime - /// - public class UTCDateTimeConverter: JsonConverter - { - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer.WriteValue(JsonConvert.SerializeObject(value)); - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - if (reader.Value == null) - return null; - - DateTime value; - if (reader.Value is string s) - value = (DateTime)JsonConvert.DeserializeObject(s)!; - else - value = (DateTime) reader.Value; - - return DateTime.SpecifyKind(value, DateTimeKind.Utc); - } - - /// - public override bool CanConvert(Type objectType) - { - return objectType == typeof(DateTime) || objectType == typeof(DateTime?); - } - } -} diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index ed0cf96..4ab0885 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -17,7 +17,7 @@ true 4.2.8 - Fixed deadlock in socket receive, Fixed issue in reconnection handling when the client is disconnected again during resubscribing, Added some additional checking of socket state to prevent sending/expecting data when socket is not connected enable - 8.0 + 9.0 MIT