Skip to content

Commit

Permalink
[release/v1.0.0-rc3-hotfixes]: Fix ScheduleAt in C# (#2163)
Browse files Browse the repository at this point in the history
  • Loading branch information
kazimuth authored and bfops committed Jan 24, 2025
1 parent 9a071c7 commit 13ea461
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 4 deletions.
12 changes: 12 additions & 0 deletions crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,16 @@ public static void NonHexStrings()
() => Address.FromHexString("these are not hex characters....")
);
}

[Fact]
public static void TimestampConversionChecks()
{
ulong us = 1737582793990639;
var time = ScheduleAt.DateTimeOffsetFromMicrosSinceUnixEpoch(us);

Assert.Equal(ScheduleAt.ToMicrosecondsSinceUnixEpoch(time), us);

var interval = ScheduleAt.TimeSpanFromMicroseconds(us);
Assert.Equal(ScheduleAt.ToMicroseconds(interval), us);
}
}
29 changes: 29 additions & 0 deletions crates/bindings-csharp/BSATN.Runtime/Builtins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,31 @@ public sealed record Time(DateTimeOffset Time_) : ScheduleAt;

public static implicit operator ScheduleAt(DateTimeOffset time) => new Time(time);

/// <summary>
/// There are 10 C# Timestamp "Ticks" per microsecond.
/// </summary>
public static readonly ulong TicksPerMicrosecond = 10;

public static ulong ToMicroseconds(TimeSpan interval)
{
return (ulong)interval.Ticks / TicksPerMicrosecond;
}

public static TimeSpan TimeSpanFromMicroseconds(ulong intervalMicros)
{
return TimeSpan.FromTicks((long)(TicksPerMicrosecond * intervalMicros));
}

public static ulong ToMicrosecondsSinceUnixEpoch(DateTimeOffset time)
{
return ToMicroseconds(time - DateTimeOffset.UnixEpoch);
}

public static DateTimeOffset DateTimeOffsetFromMicrosSinceUnixEpoch(ulong microsSinceUnixEpoch)
{
return DateTimeOffset.UnixEpoch + TimeSpanFromMicroseconds(microsSinceUnixEpoch);
}

public readonly partial struct BSATN : IReadWrite<ScheduleAt>
{
[SpacetimeDB.Type]
Expand Down Expand Up @@ -289,6 +314,10 @@ public void Write(BinaryWriter writer, ScheduleAt value)
public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>
// Constructing a custom one instead of ScheduleAtRepr.GetAlgebraicType()
// to avoid leaking the internal *Repr wrappers in generated SATS.
// We are leveraging the fact that single-element structs are byte-compatible with their elements
// when parsing BSATN.
// TODO: this might break when working with other formats like JSON, but this is all going to be rewritten
// anyway with Phoebe's Timestamp PR.
new AlgebraicType.Sum(
[
new("Interval", new AlgebraicType.U64(default)),
Expand Down
8 changes: 4 additions & 4 deletions crates/bindings-csharp/BSATN.Runtime/Repr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ namespace SpacetimeDB.Internal;
[SpacetimeDB.Type] // we should be able to encode it to BSATN too
public partial struct DateTimeOffsetRepr(DateTimeOffset time)
{
public ulong MicrosecondsSinceEpoch = (ulong)time.Ticks / 10;
public ulong MicrosecondsSinceEpoch = ScheduleAt.ToMicrosecondsSinceUnixEpoch(time);

public readonly DateTimeOffset ToStd() =>
DateTimeOffset.UnixEpoch.AddTicks(10 * (long)MicrosecondsSinceEpoch);
ScheduleAt.DateTimeOffsetFromMicrosSinceUnixEpoch(MicrosecondsSinceEpoch);
}

[StructLayout(LayoutKind.Sequential)] // we should be able to use it in FFI
[SpacetimeDB.Type] // we should be able to encode it to BSATN too
public partial struct TimeSpanRepr(TimeSpan duration)
{
public ulong Microseconds = (ulong)duration.Ticks / 10;
public ulong Microseconds = ScheduleAt.ToMicroseconds(duration);

public readonly TimeSpan ToStd() => TimeSpan.FromTicks(10 * (long)Microseconds);
public readonly TimeSpan ToStd() => ScheduleAt.TimeSpanFromMicroseconds(Microseconds);
}

0 comments on commit 13ea461

Please sign in to comment.