diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs index c2fd8e21aa..8eb2788c01 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCachedApplicationConfigurationClient.cs @@ -5,6 +5,7 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components.MauiBlazor { @@ -18,16 +19,20 @@ namespace Volo.Abp.AspNetCore.Components.MauiBlazor protected ICurrentTenantAccessor CurrentTenantAccessor { get; } + protected ICurrentTimezoneProvider CurrentTimezoneProvider { get; } + public MauiBlazorCachedApplicationConfigurationClient( AbpApplicationConfigurationClientProxy applicationConfigurationClientProxy, ApplicationConfigurationCache cache, ICurrentTenantAccessor currentTenantAccessor, + ICurrentTimezoneProvider currentTimezoneProvider, AuthenticationStateProvider authenticationStateProvider, AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy) { ApplicationConfigurationClientProxy = applicationConfigurationClientProxy; Cache = cache; CurrentTenantAccessor = currentTenantAccessor; + CurrentTimezoneProvider = currentTimezoneProvider; ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; authenticationStateProvider.AuthenticationStateChanged += async _ => { await InitializeAsync(); }; @@ -51,6 +56,7 @@ namespace Volo.Abp.AspNetCore.Components.MauiBlazor ); configurationDto.Localization.Resources = localizationDto.Resources; + CurrentTimezoneProvider.TimeZone = configurationDto.Timing.TimeZone.Iana.TimeZoneName; Cache.Set(configurationDto); @@ -81,4 +87,4 @@ namespace Volo.Abp.AspNetCore.Components.MauiBlazor return configuration; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCurrentTimezoneProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCurrentTimezoneProvider.cs new file mode 100644 index 0000000000..2a1a37da07 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.MauiBlazor/Volo/Abp/AspNetCore/Components/MauiBlazor/MauiBlazorCurrentTimezoneProvider.cs @@ -0,0 +1,10 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; + +namespace Volo.Abp.AspNetCore.Components.MauiBlazor; + +[Dependency(ReplaceServices = true)] +public class MauiBlazorCurrentTimezoneProvider : ICurrentTimezoneProvider, ISingletonDependency +{ + public string? TimeZone { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs index bebb2a78e1..cb69ea3120 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCachedApplicationConfigurationClient.cs @@ -6,6 +6,7 @@ using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ClientProxies; using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Components.WebAssembly; @@ -19,6 +20,8 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio protected ICurrentTenantAccessor CurrentTenantAccessor { get; } + protected ICurrentTimezoneProvider CurrentTimezoneProvider { get; } + protected ApplicationConfigurationChangedService ApplicationConfigurationChangedService { get; } protected IJSRuntime JSRuntime { get; } @@ -27,6 +30,7 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio AbpApplicationConfigurationClientProxy applicationConfigurationClientProxy, ApplicationConfigurationCache cache, ICurrentTenantAccessor currentTenantAccessor, + ICurrentTimezoneProvider currentTimezoneProvider, AbpApplicationLocalizationClientProxy applicationLocalizationClientProxy, ApplicationConfigurationChangedService applicationConfigurationChangedService, IJSRuntime jsRuntime) @@ -34,6 +38,7 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio ApplicationConfigurationClientProxy = applicationConfigurationClientProxy; Cache = cache; CurrentTenantAccessor = currentTenantAccessor; + CurrentTimezoneProvider = currentTimezoneProvider; ApplicationLocalizationClientProxy = applicationLocalizationClientProxy; ApplicationConfigurationChangedService = applicationConfigurationChangedService; JSRuntime = jsRuntime; @@ -69,6 +74,8 @@ public class WebAssemblyCachedApplicationConfigurationClient : ICachedApplicatio configurationDto.CurrentTenant.Id, configurationDto.CurrentTenant.Name ); + + CurrentTimezoneProvider.TimeZone = configurationDto.Timing.TimeZone.Iana.TimeZoneName; } public virtual Task GetAsync() diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTimezoneProvider.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTimezoneProvider.cs new file mode 100644 index 0000000000..c387ec30ea --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly/Volo/Abp/AspNetCore/Components/WebAssembly/WebAssemblyCurrentTimezoneProvider.cs @@ -0,0 +1,10 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; + +namespace Volo.Abp.AspNetCore.Components.WebAssembly; + +[Dependency(ReplaceServices = true)] +public class WebAssemblyCurrentTimezoneProvider : ICurrentTimezoneProvider, ISingletonDependency +{ + public string? TimeZone { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index f0ca14657b..e04018cf66 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.RequestLocalization; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.StaticAssets; +using Microsoft.AspNetCore.Timing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; @@ -245,4 +246,14 @@ public static class AbpApplicationBuilderExtensions return app; } + + /// + /// Use this middleware after middleware. + /// + /// + /// + public static IApplicationBuilder UseAbpTimeZone(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } } diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Timing/AbpTimeZoneMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Timing/AbpTimeZoneMiddleware.cs new file mode 100644 index 0000000000..af0cb1c04c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Timing/AbpTimeZoneMiddleware.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Middleware; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; +using Volo.Abp.Timing; + +namespace Microsoft.AspNetCore.Timing; + +public class AbpTimeZoneMiddleware : AbpMiddlewareBase, ITransientDependency +{ + public async override Task InvokeAsync(HttpContext context, RequestDelegate next) + { + var settingProvider = context.RequestServices.GetRequiredService(); + var timezone = await settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); + if (timezone.IsNullOrEmpty()) + { + await next(context); + return; + } + + var currentTimezoneProvider = context.RequestServices.GetRequiredService(); + using (currentTimezoneProvider.Change(timezone)) + { + await next(context); + } + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs index 8788a2b5db..9e012ed11c 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs @@ -7,9 +7,16 @@ namespace Volo.Abp.Timing; public class Clock : IClock, ITransientDependency { protected AbpClockOptions Options { get; } + protected ICurrentTimezoneProvider CurrentTimezoneProvider { get; } + protected ITimezoneProvider TimezoneProvider { get; } - public Clock(IOptions options) + public Clock( + IOptions options, + ICurrentTimezoneProvider currentTimezoneProvider, + ITimezoneProvider timezoneProvider) { + CurrentTimezoneProvider = currentTimezoneProvider; + TimezoneProvider = timezoneProvider; Options = options.Value; } @@ -38,4 +45,17 @@ public class Clock : IClock, ITransientDependency return DateTime.SpecifyKind(dateTime, Kind); } + + public virtual DateTime Convert(DateTime dateTime) + { + if (!SupportsMultipleTimezone || + dateTime.Kind != DateTimeKind.Utc || + CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + return dateTime; + } + + var timezoneInfo = TimezoneProvider.GetTimeZoneInfo(CurrentTimezoneProvider.TimeZone); + return TimeZoneInfo.ConvertTime(dateTime, timezoneInfo); + } } diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProvider.cs new file mode 100644 index 0000000000..12781d4321 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProvider.cs @@ -0,0 +1,20 @@ +using System.Threading; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Timing; + +public class CurrentTimezoneProvider : ICurrentTimezoneProvider, ISingletonDependency +{ + public string? TimeZone + { + get => _currentScope.Value; + set => _currentScope.Value = value; + } + + private readonly AsyncLocal _currentScope; + + public CurrentTimezoneProvider() + { + _currentScope = new AsyncLocal(); + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProviderExtensions.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProviderExtensions.cs new file mode 100644 index 0000000000..2fc79289ad --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/CurrentTimezoneProviderExtensions.cs @@ -0,0 +1,18 @@ +using System; + +namespace Volo.Abp.Timing; + +public static class CurrentTimezoneProviderExtensions +{ + public static IDisposable Change(this ICurrentTimezoneProvider currentTimezoneProvider, string? timeZone) + { + var parentScope = currentTimezoneProvider.TimeZone; + currentTimezoneProvider.TimeZone = timeZone; + + return new DisposeAction>(static (state) => + { + var (currentTimezoneProvider, parentScope) = state; + currentTimezoneProvider.TimeZone = parentScope; + }, (currentTimezoneProvider, parentScope)); + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClientTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClientTimezoneProvider.cs new file mode 100644 index 0000000000..69c2835adc --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClientTimezoneProvider.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Timing; + +public interface ICurrentTimezoneProvider +{ + string? TimeZone { get; set; } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs index 33db4b45a8..29f16a749a 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs @@ -25,4 +25,11 @@ public interface IClock /// DateTime to be normalized. /// Normalized DateTime DateTime Normalize(DateTime dateTime); + + /// + /// Converts given to user's time zone. + /// + /// DateTime to be normalized. + /// Converted DateTime + DateTime Convert(DateTime dateTime); }