diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs index da99a3e948..0001f56c1c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs @@ -1,6 +1,7 @@ using System; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; using Volo.Abp.AspNetCore.Mvc.MultiTenancy; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations { @@ -21,6 +22,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public CurrentTenantDto CurrentTenant { get; set; } + public TimingDto Timing { get; set; } + + public ClockDto Clock { get; set; } + public ObjectExtensionsDto ObjectExtensions { get; set; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs new file mode 100644 index 0000000000..2cff97f814 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + public class ClockDto + { + public string Kind { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs new file mode 100644 index 0000000000..bd95bfba0d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs @@ -0,0 +1,24 @@ +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + public class TimingDto + { + public TimeZone TimeZone { get; set; } + } + + public class TimeZone + { + public IanaTimeZone Iana { get; set; } + + public WindowsTimeZone Windows { get; set; } + } + + public class WindowsTimeZone + { + public string TimeZoneId { get; set; } + } + + public class IanaTimeZone + { + public string TimeZoneName { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index a68a38b274..f68d4d1450 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -15,6 +15,7 @@ using Volo.Abp.Features; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; +using Volo.Abp.Timing; using Volo.Abp.Users; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations @@ -31,6 +32,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations private readonly ISettingDefinitionManager _settingDefinitionManager; private readonly IFeatureDefinitionManager _featureDefinitionManager; private readonly ILanguageProvider _languageProvider; + private readonly ITimezoneProvider _timezoneProvider; + private readonly AbpClockOptions _abpClockOptions; private readonly ICachedObjectExtensionsDtoService _cachedObjectExtensionsDtoService; public AbpApplicationConfigurationAppService( @@ -44,6 +47,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations ISettingDefinitionManager settingDefinitionManager, IFeatureDefinitionManager featureDefinitionManager, ILanguageProvider languageProvider, + ITimezoneProvider timezoneProvider, + IOptions abpClockOptions, ICachedObjectExtensionsDtoService cachedObjectExtensionsDtoService) { _serviceProvider = serviceProvider; @@ -54,6 +59,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations _settingDefinitionManager = settingDefinitionManager; _featureDefinitionManager = featureDefinitionManager; _languageProvider = languageProvider; + _timezoneProvider = timezoneProvider; + _abpClockOptions = abpClockOptions.Value; _cachedObjectExtensionsDtoService = cachedObjectExtensionsDtoService; _localizationOptions = localizationOptions.Value; _multiTenancyOptions = multiTenancyOptions.Value; @@ -74,6 +81,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations Setting = await GetSettingConfigAsync(), MultiTenancy = GetMultiTenancy(), CurrentTenant = GetCurrentTenant(), + Timing = await GetTimingConfigAsync(), + Clock = GetClockConfig(), ObjectExtensions = _cachedObjectExtensionsDtoService.Get() }; @@ -226,5 +235,36 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations return result; } + + protected virtual async Task GetTimingConfigAsync() + { + var result = new TimingDto(); + + var windowsTimeZoneId = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); + if (!windowsTimeZoneId.IsNullOrWhiteSpace()) + { + result.TimeZone = new TimeZone + { + Windows = new WindowsTimeZone + { + TimeZoneId = windowsTimeZoneId + }, + Iana = new IanaTimeZone() + { + TimeZoneName = _timezoneProvider.WindowsToIana(windowsTimeZoneId) + } + }; + } + + return result; + } + + protected virtual ClockDto GetClockConfig() + { + return new ClockDto + { + Kind = Enum.GetName(typeof(DateTimeKind), _abpClockOptions.Kind) + }; + } } } diff --git a/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj b/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj index 4f9eb21f2f..53ef23fd62 100644 --- a/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj +++ b/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj @@ -14,8 +14,19 @@ + + + + + + + - + + + + + diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs index d48cc01ef7..a306f3def9 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs @@ -1,9 +1,29 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpLocalization; +using Volo.Abp.Modularity; +using Volo.Abp.Settings; +using Volo.Abp.Timing.Localization.Resources.AbpTiming; +using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Timing { + [DependsOn(typeof(AbpVirtualFileSystemModule), typeof(AbpSettingsModule))] public class AbpTimingModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded("Volo.Abp", "Volo/Abp"); + }); + Configure(options => + { + options + .Resources + .Add("en") + .AddVirtualJson("/Timing/Localization/Resources/AbpTiming"); + }); + } } } diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/ITimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/ITimezoneProvider.cs new file mode 100644 index 0000000000..3ee1566803 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/ITimezoneProvider.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Timing +{ + public interface ITimezoneProvider + { + List GetWindowsTimezones(); + + List GetIanaTimezones(); + + string WindowsToIana(string windowsTimeZoneId); + + string IanaToWindows(string ianaTimeZoneName); + + TimeZoneInfo GetTimeZoneInfo(string windowsOrIanaTimeZoneId); + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/AbpTimingResource.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/AbpTimingResource.cs new file mode 100644 index 0000000000..71f32d17cb --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/AbpTimingResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.Timing.Localization.Resources.AbpTiming +{ + [LocalizationResourceName("AbpTiming")] + public class AbpTimingResource + { + + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/en.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/en.json new file mode 100644 index 0000000000..c017c3ebea --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/en.json @@ -0,0 +1,7 @@ +{ + "culture": "en", + "texts": { + "DisplayName:Abp.Timing.Timezone": "Timezone", + "Description:Abp.Timing.Timezone": "Application time zone" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/tr.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/tr.json new file mode 100644 index 0000000000..18139d6080 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/tr.json @@ -0,0 +1,7 @@ +{ + "culture": "tr", + "texts": { + "DisplayName:Abp.Timing.Timezone": "saat dilimi", + "Description:Abp.Timing.Timezone": "Uygulama saat dilimi" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/zh-Hans.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/zh-Hans.json new file mode 100644 index 0000000000..41315bbfb0 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "culture": "zh-Hans", + "texts": { + "DisplayName:Abp.Timing.Timezone": "时区", + "Description:Abp.Timing.Timezone": "应用程序的时区" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/zh-Hant.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/zh-Hant.json new file mode 100644 index 0000000000..5836792e3d --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/Resources/AbpTiming/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "culture": "zh-Hant", + "texts": { + "DisplayName:Abp.Timing.Timezone": "時區", + "Description:Abp.Timing.Timezone": "應用程序的時區" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs new file mode 100644 index 0000000000..551b26e488 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TimeZoneConverter; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Timing +{ + public class TZConvertTimezoneProvider : ITimezoneProvider, ITransientDependency + { + public virtual List GetWindowsTimezones() + { + return TZConvert.KnownIanaTimeZoneNames.OrderBy(x => x).Select(x => new NameValue(x, x)).ToList(); + } + + public virtual List GetIanaTimezones() + { + return TZConvert.KnownIanaTimeZoneNames.OrderBy(x => x).Select(x => new NameValue(x, x)).ToList(); + } + + public virtual string WindowsToIana(string windowsTimeZoneId) + { + return TZConvert.WindowsToIana(windowsTimeZoneId); + } + + public virtual string IanaToWindows(string ianaTimeZoneName) + { + return TZConvert.IanaToWindows(ianaTimeZoneName); + } + + public virtual TimeZoneInfo GetTimeZoneInfo(string windowsOrIanaTimeZoneId) + { + return TZConvert.GetTimeZoneInfo(windowsOrIanaTimeZoneId); + } + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingNames.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingNames.cs new file mode 100644 index 0000000000..15ba1bc9f9 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingNames.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Timing +{ + public static class TimingSettingNames + { + public const string TimeZone = "Abp.Timing.TimeZone"; + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs new file mode 100644 index 0000000000..c2ebf73cfd --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs @@ -0,0 +1,25 @@ +using Volo.Abp.Localization; +using Volo.Abp.Settings; +using Volo.Abp.Timing.Localization.Resources.AbpTiming; + +namespace Volo.Abp.Timing +{ + public class TimingSettingProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + context.Add( + new SettingDefinition(TimingSettingNames.TimeZone, + "UTC", + L("DisplayName:Abp.Timing.Timezone"), + L("Description:Abp.Timing.Timezone"), + isVisibleToClients: true) + ); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/npm/packs/core/src/abp.js b/npm/packs/core/src/abp.js index fa42828306..722bd5f4a0 100644 --- a/npm/packs/core/src/abp.js +++ b/npm/packs/core/src/abp.js @@ -663,4 +663,60 @@ var abp = abp || {}; return abp.utils.getCookieValue(abp.security.antiForgery.tokenCookieName); }; + /* CLOCK *****************************************/ + abp.clock = abp.clock || {}; + + abp.clock.kind = 'Unspecified'; + + abp.clock.supportsMultipleTimezone = function () { + return abp.clock.kind === 'Utc'; + }; + + var toLocal = function (date) { + return new Date( + date.getUTCFullYear(), + date.getUTCMonth(), + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + date.getUTCMilliseconds() + ); + }; + + var toUtc = function (date) { + Date.UTC( + date.getUTCFullYear(), + date.getUTCMonth(), + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + date.getUTCMilliseconds() + ); + }; + + abp.clock.now = function () { + if (abp.clock.kind === 'Utc') { + return toUtc(new Date()); + } + return new Date(); + }; + + abp.clock.normalize = function (date) { + var kind = abp.clock.kind; + + if (kind === 'Unspecified') { + return date; + } + + if (kind === 'Local') { + return toLocal(new Date()); + } + + if (kind === 'Utc') { + return toUtc(new Date()); + } + }; + })(); \ No newline at end of file