1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-08 08:26:20 +00:00

Added DateTimeConverter as replacement for individual converters, fix for not closing socket when auth fails

This commit is contained in:
Jkorf 2021-11-24 16:39:14 +01:00
parent 7ac7a11dfe
commit 8b619e82f2
5 changed files with 235 additions and 4 deletions

View File

@ -0,0 +1,58 @@
using CryptoExchange.Net.Converters;
using Newtonsoft.Json;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CryptoExchange.Net.UnitTests
{
[TestFixture()]
public class ConverterTests
{
[TestCase("2021-05-12")]
[TestCase("20210512")]
[TestCase("210512")]
[TestCase("1620777600.000")]
[TestCase("1620777600000")]
[TestCase("2021-05-12T00:00:00.000Z")]
public void TestDateTimeConverterString(string input)
{
var output = JsonConvert.DeserializeObject<TimeObject>($"{{ \"time\": \"{input}\" }}");
Assert.AreEqual(output.Time, new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc));
}
[TestCase(1620777600.000)]
[TestCase(1620777600000d)]
public void TestDateTimeConverterDouble(double input)
{
var output = JsonConvert.DeserializeObject<TimeObject>($"{{ \"time\": {input} }}");
Assert.AreEqual(output.Time, new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc));
}
[TestCase(1620777600)]
[TestCase(1620777600000)]
[TestCase(1620777600000000)]
[TestCase(1620777600000000000)]
public void TestDateTimeConverterLong(long input)
{
var output = JsonConvert.DeserializeObject<TimeObject>($"{{ \"time\": {input} }}");
Assert.AreEqual(output.Time, new DateTime(2021, 05, 12, 0, 0, 0, DateTimeKind.Utc));
}
[Test]
public void TestDateTimeConverterNull()
{
var output = JsonConvert.DeserializeObject<TimeObject>($"{{ \"time\": null }}");
Assert.AreEqual(output.Time, null);
}
}
public class TimeObject
{
[JsonConverter(typeof(DateTimeConverter))]
public DateTime? Time { get; set; }
}
}

View File

@ -0,0 +1,132 @@
using Newtonsoft.Json;
using System;
using System.Diagnostics;
namespace CryptoExchange.Net.Converters
{
/// <summary>
/// Datetime converter
/// </summary>
public class DateTimeConverter: JsonConverter
{
private static DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private const decimal ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;
private const decimal ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000m / 1000;
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
}
/// <inheritdoc />
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.Value == null)
return null;
if(reader.TokenType is JsonToken.Integer)
{
var longValue = (long)reader.Value;
if (longValue < 1999999999)
return ConvertFromSeconds(longValue);
if (longValue < 1999999999999)
return ConvertFromMilliseconds(longValue);
if (longValue < 1999999999999999)
return ConvertFromMicroseconds(longValue);
return ConvertFromNanoseconds(longValue);
}
else if (reader.TokenType is JsonToken.Float)
{
var doubleValue = (double)reader.Value;
if (doubleValue < 1999999999)
return ConvertFromSeconds(doubleValue);
return ConvertFromMilliseconds(doubleValue);
}
else if(reader.TokenType is JsonToken.String)
{
var stringValue = (string)reader.Value;
if (stringValue.Length == 8)
{
// Parse 20211103 format
if (!int.TryParse(stringValue.Substring(0, 4), out var year)
|| !int.TryParse(stringValue.Substring(4, 2), out var month)
|| !int.TryParse(stringValue.Substring(6, 2), out var day))
{
Debug.WriteLine("Unknown DateTime format: " + reader.Value);
return default;
}
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
}
if (stringValue.Length == 6)
{
// Parse 211103 format
if (!int.TryParse(stringValue.Substring(0, 2), out var year)
|| !int.TryParse(stringValue.Substring(2, 2), out var month)
|| !int.TryParse(stringValue.Substring(4, 2), out var day))
{
Debug.WriteLine("Unknown DateTime format: " + reader.Value);
return default;
}
return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc);
}
if (double.TryParse(stringValue, out var doubleValue))
{
// Parse 1637745563.000 format
if (doubleValue < 1999999999)
return ConvertFromSeconds(doubleValue);
return ConvertFromMilliseconds(doubleValue);
}
if(stringValue.Length == 10)
{
// Parse 2021-11-03 format
var values = stringValue.Split('-');
if(!int.TryParse(values[0], out var year)
|| !int.TryParse(values[1], out var month)
|| !int.TryParse(values[2], out var day))
{
Debug.WriteLine("Unknown DateTime format: " + reader.Value);
return default;
}
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
}
return JsonConvert.DeserializeObject(stringValue);
}
else if(reader.TokenType == JsonToken.Date)
{
return (DateTime)reader.Value;
}
else
{
Debug.WriteLine("Unknown DateTime format: " + reader.Value);
return default;
}
}
public static DateTime ConvertFromSeconds(double seconds) => _epoch.AddSeconds(seconds);
public static DateTime ConvertFromMilliseconds(double milliseconds) => _epoch.AddMilliseconds(milliseconds);
public static DateTime ConvertFromMicroseconds(long microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * ticksPerMicrosecond));
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);
/// <inheritdoc />
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));
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using Newtonsoft.Json;
namespace CryptoExchange.Net.Converters
{
/// <summary>
/// Converter for nanoseconds to datetime
/// </summary>
public class TimestampMicroSecondsConverter : JsonConverter
{
private const decimal ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;
/// <inheritdoc />
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
/// <inheritdoc />
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));
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteValue((long)Math.Round(((DateTime)value! - new DateTime(1970, 1, 1)).Ticks / ticksPerMicrosecond));
}
}
}

View File

@ -78,10 +78,15 @@ namespace CryptoExchange.Net.Objects
if (value == null) if (value == null)
return; return;
var newValue = value; // TODO addresses can't always be forced to end with '/', bybit websocket doesn't work with it.
if (!newValue.EndsWith("/")) // Should be fixed in the GetUrl methods?
newValue += "/";
_baseAddress = newValue; //var newValue = value;
//if (!newValue.EndsWith("/"))
// newValue += "/";
//_baseAddress = newValue;
_baseAddress = value;
} }
} }

View File

@ -346,6 +346,7 @@ namespace CryptoExchange.Net
var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false); var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false);
if (!result) if (!result)
{ {
await socket.CloseAsync().ConfigureAwait(false);
log.Write(LogLevel.Warning, $"Socket {socket.Socket.Id} authentication failed"); log.Write(LogLevel.Warning, $"Socket {socket.Socket.Id} authentication failed");
result.Error!.Message = "Authentication failed: " + result.Error.Message; result.Error!.Message = "Authentication failed: " + result.Error.Message;
return new CallResult<bool>(false, result.Error); return new CallResult<bool>(false, result.Error);