diff --git a/docs/design/features/timezone-invariant-mode.md b/docs/design/features/timezone-invariant-mode.md new file mode 100644 index 0000000000000..e66da8ea5407d --- /dev/null +++ b/docs/design/features/timezone-invariant-mode.md @@ -0,0 +1,12 @@ +# Timezone Invariant Mode + +Author: [Pavel Savara](https://github.com/pavelsavara) + +It's currently only available for Browser OS + +- you enable it in project file: + ```xml + + true + + ``` diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index 5cf78e89c3c15..60d60e7416898 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -47,6 +47,8 @@ Copyright (c) .NET Foundation. All rights reserved. false + true + false false true false diff --git a/src/mono/sample/wasm/browser-advanced/Program.cs b/src/mono/sample/wasm/browser-advanced/Program.cs index af6ca78e0bbda..bec9cb3256b4c 100644 --- a/src/mono/sample/wasm/browser-advanced/Program.cs +++ b/src/mono/sample/wasm/browser-advanced/Program.cs @@ -13,6 +13,26 @@ public partial class Test public static int Main(string[] args) { Console.WriteLine ("Hello, World!"); + + var start = DateTime.UtcNow; + var timezonesCount = TimeZoneInfo.GetSystemTimeZones().Count; + var end = DateTime.UtcNow; + Console.WriteLine($"Found {timezonesCount} timezones in the TZ database in {end-start}"); + + TimeZoneInfo utc = TimeZoneInfo.FindSystemTimeZoneById("UTC"); + Console.WriteLine($"{utc.DisplayName} BaseUtcOffset is {utc.BaseUtcOffset}"); + + try + { + TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("Asia/Tokyo"); + Console.WriteLine($"{tst.DisplayName} BaseUtcOffset is {tst.BaseUtcOffset}"); + } + catch (TimeZoneNotFoundException tznfe) + { + Console.WriteLine($"Could not find Asia/Tokyo: {tznfe.Message}"); + } + + return 0; } diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj index d37b17495581b..782efa96a8236 100644 --- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj +++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj @@ -14,6 +14,7 @@ <_ServeHeaders>$(_ServeHeaders) -h "Content-Security-Policy: default-src 'self' 'wasm-unsafe-eval'" browser; + true diff --git a/src/mono/wasi/build/WasiApp.Native.targets b/src/mono/wasi/build/WasiApp.Native.targets index e0e43c0edffc4..8fd82eaca343f 100644 --- a/src/mono/wasi/build/WasiApp.Native.targets +++ b/src/mono/wasi/build/WasiApp.Native.targets @@ -168,8 +168,9 @@ <_WasmCommonCFlags Include="-DGEN_PINVOKE=1" /> - <_WasmCommonCFlags Condition="'$(WasmSingleFileBundle)' == 'true'" Include="-DWASM_SINGLE_FILE=1" /> + <_WasmCommonCFlags Condition="'$(WasmSingleFileBundle)' == 'true'" Include="-DWASM_SINGLE_FILE=1" /> <_WasmCommonCFlags Condition="'$(InvariantGlobalization)' == 'true'" Include="-DINVARIANT_GLOBALIZATION=1" /> + <_WasmCommonCFlags Condition="'$(InvariantTimezone)' == 'true'" Include="-DINVARIANT_TIMEZONE=1" /> diff --git a/src/mono/wasi/build/WasiApp.targets b/src/mono/wasi/build/WasiApp.targets index c39ec3a0a880f..2a711e5eeea2f 100644 --- a/src/mono/wasi/build/WasiApp.targets +++ b/src/mono/wasi/build/WasiApp.targets @@ -29,6 +29,7 @@ - $(WasmProfilers) - Profilers to use - $(AOTProfilePath) - profile data file to be used for profile-guided optimization - $(InvariantGlobalization) - Whenever to disable ICU. Defaults to false. + - $(InvariantTimezone) - Whenever to disable Timezone database. Defaults to false. - $(WasmResolveAssembliesBeforeBuild) - Resolve the assembly dependencies. Defaults to false - $(WasmAssemblySearchPaths) - used for resolving assembly dependencies diff --git a/src/mono/wasi/runtime/driver.c b/src/mono/wasi/runtime/driver.c index d6a51f9be51b0..98fbe1fef04bb 100644 --- a/src/mono/wasi/runtime/driver.c +++ b/src/mono/wasi/runtime/driver.c @@ -63,7 +63,9 @@ int32_t monoeg_g_hasenv(const char *variable); void mono_free (void*); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); +#ifndef INVARIANT_TIMEZONE extern void mono_register_timezones_bundle (void); +#endif /* INVARIANT_TIMEZONE */ #ifdef WASM_SINGLE_FILE extern void mono_register_assemblies_bundle (void); #ifndef INVARIANT_GLOBALIZATION @@ -439,7 +441,9 @@ mono_wasm_load_runtime (const char *unused, int debug_level) mini_parse_debug_option ("top-runtime-invoke-unhandled"); +#ifndef INVARIANT_TIMEZONE mono_register_timezones_bundle (); +#endif /* INVARIANT_TIMEZONE */ #ifdef WASM_SINGLE_FILE mono_register_assemblies_bundle (); #endif diff --git a/src/mono/wasi/wasi.proj b/src/mono/wasi/wasi.proj index fbf434ce4964a..d601f2c9e09ab 100644 --- a/src/mono/wasi/wasi.proj +++ b/src/mono/wasi/wasi.proj @@ -66,7 +66,7 @@ - + <_WasmTimezonesPath>$([MSBuild]::NormalizePath('$(PkgSystem_Runtime_TimeZoneData)', 'contentFiles', 'any', 'any', 'data')) <_WasmTimezonesBundleObjectFile>wasm-bundled-timezones.o diff --git a/src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs b/src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs new file mode 100644 index 0000000000000..1f10c80e14a38 --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/InvariantTimezoneTests.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using Xunit; +using Xunit.Abstractions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class InvariantTimezoneTests : BuildTestBase + { + public InvariantTimezoneTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + public static IEnumerable InvariantTimezoneTestData(bool aot, RunHost host) + => ConfigWithAOTData(aot) + .Multiply( + new object?[] { null }, + new object?[] { false }, + new object?[] { true }) + .WithRunHosts(host) + .UnwrapItemsAsArrays(); + + [Theory] + [MemberData(nameof(InvariantTimezoneTestData), parameters: new object[] { /*aot*/ false, RunHost.All })] + [MemberData(nameof(InvariantTimezoneTestData), parameters: new object[] { /*aot*/ true, RunHost.All })] + public void AOT_InvariantTimezone(BuildArgs buildArgs, bool? invariantTimezone, RunHost host, string id) + => TestInvariantTimezone(buildArgs, invariantTimezone, host, id); + + [Theory] + [MemberData(nameof(InvariantTimezoneTestData), parameters: new object[] { /*aot*/ false, RunHost.All })] + public void RelinkingWithoutAOT(BuildArgs buildArgs, bool? invariantTimezone, RunHost host, string id) + => TestInvariantTimezone(buildArgs, invariantTimezone, host, id, + extraProperties: "true", + dotnetWasmFromRuntimePack: false); + + private void TestInvariantTimezone(BuildArgs buildArgs, bool? invariantTimezone, + RunHost host, string id, string extraProperties="", bool? dotnetWasmFromRuntimePack=null) + { + string projectName = $"invariant_{invariantTimezone?.ToString() ?? "unset"}"; + if (invariantTimezone != null) + extraProperties = $"{extraProperties}{invariantTimezone}"; + + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs, extraProperties); + + if (dotnetWasmFromRuntimePack == null) + dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release"); + + string programText = @" + using System; + + // https://github.com/dotnet/runtime/blob/main/docs/design/features/timezone-invariant-mode.md + + var timezonesCount = TimeZoneInfo.GetSystemTimeZones().Count; + Console.WriteLine($""Found {timezonesCount} timezones in the TZ database""); + + TimeZoneInfo utc = TimeZoneInfo.FindSystemTimeZoneById(""UTC""); + Console.WriteLine($""{utc.DisplayName} BaseUtcOffset is {utc.BaseUtcOffset}""); + + try + { + TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(""Asia/Tokyo""); + Console.WriteLine($""{tst.DisplayName} BaseUtcOffset is {tst.BaseUtcOffset}""); + } + catch (TimeZoneNotFoundException tznfe) + { + Console.WriteLine($""Could not find Asia/Tokyo: {tznfe.Message}""); + } + + return 42; + "; + + BuildProject(buildArgs, + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText), + DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack)); + + string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, host: host, id: id); + Assert.Contains("UTC BaseUtcOffset is 0", output); + if (invariantGlobalization == true) + { + Assert.Contains("Could not find Asia/Tokyo", output); + } + else + { + Assert.Contains("Asia/Tokyo BaseUtcOffset is 09:00:00", output); + } + } + } +} diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 310d77cf36d6d..403096064dd47 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -219,6 +219,7 @@ <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(_WasmShouldAOT)' == 'true'" /> <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(_WasmShouldAOT)' == 'true'" /> <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> + <_EmccCFlags Include="-DINVARIANT_TIMEZONE=1" Condition="'$(InvariantTimezone)' == 'true'" /> <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DENABLE_AOT_PROFILER=1" Condition="$(WasmProfilers.Contains('aot'))" /> <_EmccCFlags Include="-DENABLE_BROWSER_PROFILER=1" Condition="$(WasmProfilers.Contains('browser'))" /> diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 929089695e14a..b243b3d64b03d 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -28,6 +28,7 @@ - $(WasmProfilers) - Profilers to use - $(AOTProfilePath) - profile data file to be used for profile-guided optimization - $(InvariantGlobalization) - Whenever to disable ICU. Defaults to false. + - $(InvariantTimezone) - Whenever to disable Timezone database. Defaults to false. - $(HybridGlobalization) - Whenever to enable reduced ICU + native platform functions. Defaults to false and can be set only for InvariantGlobalization=false, WasmIncludeFullIcuData=false and empty WasmIcuDataFileName. - $(WasmResolveAssembliesBeforeBuild) - Resolve the assembly dependencies. Defaults to false diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index 51cd2497a1b9d..551d1b4ca8709 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -54,7 +54,9 @@ int monoeg_g_setenv(const char *variable, const char *value, int overwrite); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); +#ifndef INVARIANT_TIMEZONE extern void mono_register_timezones_bundle (void); +#endif /* INVARIANT_TIMEZONE */ extern void mono_wasm_set_entrypoint_breakpoint (const char* assembly_name, int method_token); static void mono_wasm_init_finalizer_thread (void); @@ -473,7 +475,9 @@ mono_wasm_load_runtime (const char *unused, int debug_level) mini_parse_debug_option ("top-runtime-invoke-unhandled"); +#ifndef INVARIANT_TIMEZONE mono_register_timezones_bundle (); +#endif /* INVARIANT_TIMEZONE */ mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL); mono_wasm_install_get_native_to_interp_tramp (get_native_to_interp); diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index cea78ac93822a..8ea0b7deb2f89 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -137,6 +137,7 @@ linked_functions = [...linked_functions, "mono_wasm_install_js_worker_interop", "mono_wasm_uninstall_js_worker_interop", ] +#endif if (ENABLE_AOT_PROFILER) { linked_functions = [...linked_functions, @@ -153,7 +154,6 @@ if (ENABLE_AOT_PROFILER) { ] } -#endif if (!DISABLE_LEGACY_JS_INTEROP) { linked_functions = [...linked_functions, "mono_wasm_invoke_js_with_args_ref", diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index c150a1fe09d31..98a62905600f7 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -80,7 +80,7 @@ - + <_WasmTimezonesPath>$([MSBuild]::NormalizePath('$(PkgSystem_Runtime_TimeZoneData)', 'contentFiles', 'any', 'any', 'data')) <_WasmTimezonesBundleSourceFile>wasm-bundled-timezones.c