Skip to content
This repository was archived by the owner on Dec 18, 2023. It is now read-only.

Commit 050cc65

Browse files
authored
Merge pull request #52 from Du-z/ConversionTests
2 parents 08fd347 + 96f9f64 commit 050cc65

22 files changed

Lines changed: 854 additions & 331 deletions

src/Abstractions/Database.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace SurrealDB.Abstractions;
1111
public interface IDatabase<TResponse>
1212
: IDatabase
1313
where TResponse : IResponse {
14-
14+
1515
/// <summary>
1616
/// Retrieves the current session information.
1717
/// </summary>
@@ -154,7 +154,7 @@ public interface IDatabase {
154154
/// Configures the client with all applicable settings.
155155
/// </summary>
156156
public Task Open(Config config, CancellationToken ct = default);
157-
157+
158158
/// <summary>
159159
/// Opens the connection to a Surreal database instance using the preconfigured configuration.
160160
/// Configures the client with all applicable settings.

src/Common/TimeZoneHelper.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
namespace SurrealDB.Json;
2+
3+
public static class TimeZoneHelper {
4+
private static Dictionary<TimeSpan, TimeZoneInfo>? s_cacheTz;
5+
private static readonly object s_cacheTzLock = new();
6+
7+
private static IReadOnlyDictionary<TimeSpan, TimeZoneInfo> GetTimeZones() {
8+
Dictionary<TimeSpan, TimeZoneInfo> cache;
9+
lock (s_cacheTzLock) {
10+
cache = EnsureTzCacheUnchecked();
11+
}
12+
13+
return cache;
14+
}
15+
16+
private static Dictionary<TimeSpan, TimeZoneInfo> EnsureTzCacheUnchecked() {
17+
Dictionary<TimeSpan, TimeZoneInfo>? cache = s_cacheTz;
18+
if (cache is null || cache.Count <= 0) {
19+
cache = TimeZoneInfo.GetSystemTimeZones().ToDictionary(static tz => tz.BaseUtcOffset);
20+
s_cacheTz = cache;
21+
}
22+
23+
return cache;
24+
}
25+
26+
private static TimeZoneInfo AddTimeZone(string name, in TimeSpan off) {
27+
lock (s_cacheTzLock) {
28+
Dictionary<TimeSpan, TimeZoneInfo> cache = EnsureTzCacheUnchecked();
29+
if (cache.TryGetValue(off, out TimeZoneInfo? tz)) {
30+
return tz;
31+
}
32+
tz = TimeZoneInfo.CreateCustomTimeZone(name, off, null, null, null, null, false);
33+
cache[off] = tz;
34+
return tz;
35+
}
36+
}
37+
38+
public static TimeZoneInfo FromOffset(in TimeSpan offset) {
39+
return GetTimeZones().TryGetValue(offset, out TimeZoneInfo? tz) ? tz : AddTimeZone(offset.ToString(), in offset);
40+
}
41+
42+
/// <summary>
43+
/// Converts the <see cref="DateTime"/> to a <see cref="DateTimeOffset"/> by adding a offset.
44+
/// </summary>
45+
/// <remarks>
46+
/// This method ignores the <see cref="DateTimeKind"/> property and assumes UTC time by setting <see cref="DateTimeKind.Unspecified"/>!
47+
/// </remarks>
48+
public static DateTimeOffset WithOffset(in this DateTime dt, in TimeSpan offset) {
49+
if (dt.Kind != DateTimeKind.Unspecified) {
50+
return new(new DateTime(dt.Ticks), offset);
51+
}
52+
return new(dt, offset);
53+
}
54+
}

src/Json/Constants.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
using System.Text.Json;
33
using System.Text.Json.Serialization;
44

5+
using SurrealDB.Json.Numbers;
6+
using SurrealDB.Json.Time;
7+
58
namespace SurrealDB.Json;
69

710
/// <summary>

src/Json/Json.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19+
<PackageReference Include="Superpower" Version="3.0.0" />
1920
<PackageReference Include="Rustic.Common" Version="0.5.80" />
2021
</ItemGroup>
2122

23+
<ItemGroup>
24+
<ProjectReference Include="..\Common\Common.csproj" />
25+
</ItemGroup>
26+
2227
</Project>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Text.Json;
33
using System.Text.Json.Serialization;
44

5-
namespace SurrealDB.Json;
5+
namespace SurrealDB.Json.Numbers;
66

77
public sealed class DecimalConv : JsonConverter<decimal> {
88
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Text.Json;
33
using System.Text.Json.Serialization;
44

5-
namespace SurrealDB.Json;
5+
namespace SurrealDB.Json.Numbers;
66

77
public sealed class DoubleConv : JsonConverter<double> {
88
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {

src/Json/Numbers/NumberParsers.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System.Globalization;
2+
3+
namespace SurrealDB.Json.Numbers;
4+
5+
internal static class SpecialNumbers {
6+
public static string NaN { get; } = NumberFormatInfo.InvariantInfo.NaNSymbol;
7+
public static string PosinfInv { get; } = NumberFormatInfo.InvariantInfo.PositiveInfinitySymbol;
8+
public static string NeginfInv { get; } = NumberFormatInfo.InvariantInfo.NegativeInfinitySymbol;
9+
public static string PosinfCur { get; } = NumberFormatInfo.CurrentInfo.PositiveInfinitySymbol;
10+
public static string NeginfCur { get; } = NumberFormatInfo.CurrentInfo.NegativeInfinitySymbol;
11+
public static string Posinf => "∞"; // &infin;
12+
public static string Neginf => "−∞"; // &minus;&infin;
13+
14+
private static bool IsNegInf(string special) {
15+
return special.Equals(Neginf, StringComparison.OrdinalIgnoreCase) || special.Equals(NeginfCur, StringComparison.OrdinalIgnoreCase) || special.Equals(NeginfInv, StringComparison.OrdinalIgnoreCase);
16+
}
17+
18+
private static bool IsPosInf(string special) {
19+
return special.Equals(Posinf, StringComparison.OrdinalIgnoreCase) || special.Equals(PosinfCur, StringComparison.OrdinalIgnoreCase) || special.Equals(PosinfInv, StringComparison.OrdinalIgnoreCase);
20+
}
21+
22+
public static float ToSingle(string special) {
23+
if (special.Equals(NaN, StringComparison.OrdinalIgnoreCase)) {
24+
return Single.NaN;
25+
}
26+
27+
if (IsPosInf(special)) {
28+
return Single.PositiveInfinity;
29+
}
30+
31+
if (IsNegInf(special)) {
32+
return Single.NegativeInfinity;
33+
}
34+
35+
return default;
36+
}
37+
38+
public static string? ToSpecial(in float value) {
39+
if (Single.IsNaN(value)) {
40+
return NaN;
41+
}
42+
43+
if (Single.IsPositiveInfinity(value)) {
44+
return Posinf;
45+
}
46+
47+
if (Single.IsNegativeInfinity(value)) {
48+
return Neginf;
49+
}
50+
51+
return null;
52+
}
53+
54+
public static double ToDouble(string special) {
55+
if (special.Equals(NaN, StringComparison.OrdinalIgnoreCase)) {
56+
return Double.NaN;
57+
}
58+
59+
if (IsPosInf(special)) {
60+
return Double.PositiveInfinity;
61+
}
62+
63+
if (IsNegInf(special)) {
64+
return Double.NegativeInfinity;
65+
}
66+
67+
return default;
68+
}
69+
70+
public static string? ToSpecial(in double value) {
71+
if (Double.IsNaN(value)) {
72+
return NaN;
73+
}
74+
75+
if (Double.IsPositiveInfinity(value)) {
76+
return Posinf;
77+
}
78+
79+
if (Double.IsNegativeInfinity(value)) {
80+
return Neginf;
81+
}
82+
83+
return null;
84+
}
85+
86+
public static decimal ToDecimal(string str) {
87+
return default;
88+
}
89+
public static string? ToSpecial(in decimal value) {
90+
return null;
91+
}
92+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Text.Json;
33
using System.Text.Json.Serialization;
44

5-
namespace SurrealDB.Json;
5+
namespace SurrealDB.Json.Numbers;
66

77
public sealed class SingleConv : JsonConverter<float> {
88
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {

src/Json/SpecialNumbers.cs

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using System.Diagnostics.CodeAnalysis;
2-
using System.Globalization;
32
using System.Text.Json;
43
using System.Text.Json.Serialization;
54

6-
using Rustic;
5+
using Superpower;
6+
using Superpower.Model;
77

8-
namespace SurrealDB.Json;
8+
namespace SurrealDB.Json.Time;
99

1010
public sealed class DateOnlyConv : JsonConverter<DateOnly> {
1111
public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
@@ -28,34 +28,27 @@ public override void WriteAsPropertyName(Utf8JsonWriter writer, DateOnly value,
2828
writer.WritePropertyName(ToString(in value));
2929
}
3030

31-
public static DateOnly Parse(in ReadOnlySpan<char> str) {
32-
ReadOnlySpan<char> slc, rem = str;
33-
slc = rem.Slice(0, 4);
34-
rem = rem.Slice(4);
35-
int y = Int32.Parse(slc, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
36-
slc = rem.Slice(1, 2);
37-
rem = rem.Slice(3);
38-
int m = Int32.Parse(slc, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
39-
slc = rem.Slice(1, 2);
40-
int d = Int32.Parse(slc, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
41-
return new(y, m , d);
31+
public static DateOnly Parse(string? s) {
32+
return TryParse(s, out DateOnly value) ? value : ThrowParseInvalid(s);
4233
}
43-
44-
// Needs 10 chars
45-
public static void ToString(ref StrBuilder builder, in DateOnly dt) {
46-
dt.Year.TryFormat(builder.AppendSpan(4), out _, "0000", NumberFormatInfo.InvariantInfo);
47-
builder.Append('-');
48-
dt.Month.TryFormat(builder.AppendSpan(2), out _, "00", NumberFormatInfo.InvariantInfo);
49-
builder.Append('-');
50-
dt.Day.TryFormat(builder.AppendSpan(2), out _, "00", NumberFormatInfo.InvariantInfo);
34+
public static bool TryParse(string? s, out DateOnly value) {
35+
if (String.IsNullOrEmpty(s)) {
36+
return false;
37+
}
38+
Result<DateOnly> res = TimeParsers.IsoDate(new TextSpan(s));
39+
value = res.HasValue ? res.Value : default;
40+
return res.HasValue;
5141
}
5242

53-
public static string ToString(in DateOnly dt) {
54-
StrBuilder builder = new(stackalloc char[11]);
55-
ToString(ref builder, dt);
56-
return builder.ToString();
43+
public static string ToString(in DateOnly value) {
44+
return $"{value.Year.ToString("D4")}-{value.Month.ToString("D2")}-{value.Day.ToString("D2")}";
5745
}
5846

47+
[DoesNotReturn]
48+
private static DateOnly ThrowParseInvalid(string? s) {
49+
throw new ParseException($"Unable to parse DateOnly from `{s}`");
50+
}
51+
5952
[DoesNotReturn]
6053
private DateOnly ThrowJsonTokenTypeInvalid() {
6154
throw new JsonException("Cannot deserialize a non string token as a DateOnly.");

0 commit comments

Comments
 (0)