Skip to content

Commit

Permalink
Add GetUtcNow to ITimeSystem (#1701)
Browse files Browse the repository at this point in the history
* Add GetUtcNow to ITimeSystem

* Add a test showing how to use virtualized TimeSystem

* Update GetUtcNow to return DateTimeOffset
  • Loading branch information
ejsmith authored Dec 12, 2023
1 parent a641932 commit 93c5ee5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 3 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageVersion Include="Jurassic" Version="3.2.7" />
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.116" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="MongoDB.Bson.signed" Version="2.19.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<PackageReference Include="MongoDB.Bson.signed" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="NodaTime" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
Expand Down
39 changes: 39 additions & 0 deletions Jint.Tests.PublicInterface/TimeSystemTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Globalization;
using Jint.Runtime;
using Microsoft.Extensions.Time.Testing;
using NodaTime;

namespace Jint.Tests.PublicInterface;
Expand Down Expand Up @@ -33,6 +34,29 @@ public void CanProduceValidDatesUsingNodaTimeIntegration(string input, long expe

Assert.Equal(expected, engine.Evaluate($"new Date({input}) * 1").AsNumber());
}

[Fact]
public void CanUseTimeProvider()
{
var defaultEngine = new Engine();
var defaultNow = DateTimeOffset.Now.ToUnixTimeMilliseconds();
var defaultScriptNow = defaultEngine.Evaluate("new Date() * 1").AsNumber();
Assert.InRange(defaultScriptNow, defaultNow, defaultNow + 100);

var timeProvider = new FakeTimeProvider();
timeProvider.SetUtcNow(new DateTimeOffset(2023, 11, 6, 0, 0, 0, 0, TimeSpan.Zero));

var timeProviderEngine = new Engine(options =>
{
options.TimeSystem = new TimeProviderTimeSystem(timeProvider);
});

var timeProviderNow = timeProvider.GetUtcNow().ToUnixTimeMilliseconds();
var timeProviderScriptNow = timeProviderEngine.Evaluate("new Date() * 1").AsNumber();
Assert.InRange(timeProviderNow, timeProviderScriptNow, timeProviderScriptNow + 100);

Assert.NotInRange(timeProviderScriptNow, defaultScriptNow - 10000, defaultScriptNow + 10000);
}
}

file sealed class NodaTimeSystem : DefaultTimeSystem
Expand All @@ -52,3 +76,18 @@ public override TimeSpan GetUtcOffset(long epochMilliseconds)
return offset.ToTimeSpan();
}
}

file sealed class TimeProviderTimeSystem : DefaultTimeSystem
{
private readonly TimeProvider _timeProvider;

public TimeProviderTimeSystem(TimeProvider timeProvider) : base(TimeZoneInfo.Utc, CultureInfo.CurrentCulture)
{
_timeProvider = timeProvider;
}

public override DateTimeOffset GetUtcNow()
{
return _timeProvider.GetUtcNow();
}
}
6 changes: 3 additions & 3 deletions Jint/Native/Date/DateConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ private static JsValue Utc(JsValue thisObject, JsValue[] arguments)
return finalDate.TimeClip().ToJsValue();
}

private static JsValue Now(JsValue thisObject, JsValue[] arguments)
private JsValue Now(JsValue thisObject, JsValue[] arguments)
{
return (long) (DateTime.UtcNow - Epoch).TotalMilliseconds;
return (long) (_timeSystem.GetUtcNow().DateTime - Epoch).TotalMilliseconds;
}

protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
Expand All @@ -120,7 +120,7 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
newTarget,
static intrinsics => intrinsics.Date.PrototypeObject,
static (engine, _, dateValue) => new JsDate(engine, dateValue),
(DateTime.UtcNow - Epoch).TotalMilliseconds);
(_timeSystem.GetUtcNow().DateTime - Epoch).TotalMilliseconds);
}

return ConstructUnlikely(arguments, newTarget);
Expand Down
5 changes: 5 additions & 0 deletions Jint/Runtime/DefaultTimeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public DefaultTimeSystem(TimeZoneInfo timeZoneInfo, CultureInfo parsingCulture)
DefaultTimeZone = timeZoneInfo;
}

public virtual DateTimeOffset GetUtcNow()
{
return DateTimeOffset.UtcNow;
}

public TimeZoneInfo DefaultTimeZone { get; }

public virtual bool TryParse(string date, out long epochMilliseconds)
Expand Down
6 changes: 6 additions & 0 deletions Jint/Runtime/ITimeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ namespace Jint.Runtime;
/// </remarks>
public interface ITimeSystem
{
/// <summary>
/// Retrieves current UTC time.
/// </summary>
/// <returns>Current UTC time.</returns>
DateTimeOffset GetUtcNow();

/// <summary>
/// Return the default time zone system is using. Usually <see cref="TimeZoneInfo.Local"/>, but can be altered via
/// engine configuration, see <see cref="Options.TimeZone"/>.
Expand Down

0 comments on commit 93c5ee5

Please sign in to comment.