From c7bc2baa089eb0739ed93f312ef1c3ec376a2ba4 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 5 Mar 2025 11:13:09 +0800 Subject: [PATCH] Fix more compatiable timezone problem. --- framework/Volo.Abp.sln | 7 + .../AbpDatePickerBaseTagHelperService.cs | 10 +- .../AbpDatePickerTagHelperService.cs | 20 ++- .../AbpDateRangePickerTagHelperService.cs | 40 ++++-- .../Volo.Abp.Timing/Volo/Abp/Timing/Clock.cs | 43 +++++- .../Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs | 13 +- .../Volo.Abp.Timing.Tests.csproj | 16 +++ .../Volo/Abp/Timing/AbpTimingTestModule.cs | 14 ++ .../Volo/Abp/Timing/Clock_Default_Tests.cs | 110 +++++++++++++++ .../Volo/Abp/Timing/Clock_Utc_Tests.cs | 132 ++++++++++++++++++ npm/packs/core/src/abp.js | 9 +- npm/packs/luxon/src/abp.luxon.js | 52 +++++++ 12 files changed, 437 insertions(+), 29 deletions(-) create mode 100644 framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj create mode 100644 framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs create mode 100644 framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs create mode 100644 framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index ca471bb304..4c08cced94 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -485,6 +485,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Bunny" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Bunny.Tests", "test\Volo.Abp.BlobStoring.Bunny.Tests\Volo.Abp.BlobStoring.Bunny.Tests.csproj", "{BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Timing.Tests", "test\Volo.Abp.Timing.Tests\Volo.Abp.Timing.Tests.csproj", "{58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1447,6 +1449,10 @@ Global {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915}.Release|Any CPU.Build.0 = Release|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1691,6 +1697,7 @@ Global {C753DDD6-5699-45F8-8669-08CE0BB816DE} = {447C8A77-E5F0-4538-8687-7383196D04EA} {1BBCBA72-CDB6-4882-96EE-D4CD149433A2} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {BC4BB2D6-DFD8-4190-AAC3-32C0A7A8E915} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {58FCF22D-E8DB-4EB8-B586-9BB6E9899D64} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs index 786687696a..9dffa5bc86 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerBaseTagHelperService.cs @@ -54,7 +54,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { if(x is string s && DateTime.TryParse(s, out var dt)) { - return Clock.Convert(dt).ToString("O"); + return Clock.ConvertTo(dt).ToString("O"); } return string.Empty; @@ -65,7 +65,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { if(x is DateTime dt && dt != default) { - return Clock.Convert(dt).ToString("O"); + return Clock.ConvertTo(dt).ToString("O"); } return string.Empty; @@ -76,7 +76,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { if(x is DateTime dt && dt != default) { - return Clock.Convert(dt).ToString("O"); + return Clock.ConvertTo(dt).ToString("O"); } return string.Empty; } @@ -86,7 +86,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { if(x is DateTimeOffset dto && dto != default) { - return Clock.Convert(dto).ToString("O"); + return Clock.ConvertTo(dto).DateTime.ToString("O"); } return string.Empty; @@ -97,7 +97,7 @@ public abstract class AbpDatePickerBaseTagHelperService : AbpTagHelp { if(x is DateTimeOffset dto && dto != default) { - return Clock.Convert(dto).ToString("O"); + return Clock.ConvertTo(dto).DateTime.ToString("O"); } return string.Empty; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs index 9781a69b05..dc478952e5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DatePicker/AbpDatePickerTagHelperService.cs @@ -53,13 +53,23 @@ public class AbpDatePickerTagHelperService : AbpDatePickerBaseTagHelperService Options.Kind == DateTimeKind.Utc; + /// + /// Normalizes given . + /// + /// DateTime to be normalized. + /// Normalized DateTime public virtual DateTime Normalize(DateTime dateTime) { if (Kind == DateTimeKind.Unspecified || Kind == dateTime.Kind) @@ -46,7 +51,12 @@ public class Clock : IClock, ITransientDependency return DateTime.SpecifyKind(dateTime, Kind); } - public virtual DateTime Convert(DateTime dateTime) + /// + /// Converts given UTC to user's time zone. + /// + /// DateTime to be normalized. + /// Converted DateTime + public virtual DateTime ConvertTo(DateTime dateTime) { if (!SupportsMultipleTimezone || dateTime.Kind != DateTimeKind.Utc || @@ -59,7 +69,12 @@ public class Clock : IClock, ITransientDependency return TimeZoneInfo.ConvertTime(dateTime, timezoneInfo); } - public virtual DateTimeOffset Convert(DateTimeOffset dateTimeOffset) + /// + /// Converts given to user's time zone. + /// + /// DateTimeOffset to be normalized. + /// Converted DateTimeOffset + public virtual DateTimeOffset ConvertTo(DateTimeOffset dateTimeOffset) { if (!SupportsMultipleTimezone || CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) @@ -70,4 +85,28 @@ public class Clock : IClock, ITransientDependency var timezoneInfo = TimezoneProvider.GetTimeZoneInfo(CurrentTimezoneProvider.TimeZone); return TimeZoneInfo.ConvertTime(dateTimeOffset, timezoneInfo); } + + /// + /// Converts given user's to UTC or not. + /// + /// DateTime to be normalized. + /// Converted DateTime + public DateTime ConvertFrom(DateTime dateTime) + { + if (!SupportsMultipleTimezone || + CurrentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + return dateTime; + } + + var timezoneInfo = TimezoneProvider.GetTimeZoneInfo(CurrentTimezoneProvider.TimeZone); + var targetDateTime = dateTime; + if (dateTime.Kind == DateTimeKind.Utc) + { + return dateTime; + } + + targetDateTime = DateTime.SpecifyKind(targetDateTime, DateTimeKind.Unspecified); + return TimeZoneInfo.ConvertTimeToUtc(targetDateTime, timezoneInfo); + } } 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 3a87726655..0c757ab93c 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/IClock.cs @@ -27,16 +27,23 @@ public interface IClock DateTime Normalize(DateTime dateTime); /// - /// Converts given to user's time zone. + /// Converts given UTC to user's time zone. /// /// DateTime to be normalized. /// Converted DateTime - DateTime Convert(DateTime dateTime); + DateTime ConvertTo(DateTime dateTime); /// /// Converts given to user's time zone. /// /// DateTimeOffset to be normalized. /// Converted DateTimeOffset - DateTimeOffset Convert(DateTimeOffset dateTimeOffset); + DateTimeOffset ConvertTo(DateTimeOffset dateTimeOffset); + + /// + /// Converts given user's to UTC or not. + /// + /// DateTime to be normalized. + /// Converted DateTime + DateTime ConvertFrom(DateTime dateTime); } diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj b/framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj new file mode 100644 index 0000000000..58bc261247 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo.Abp.Timing.Tests.csproj @@ -0,0 +1,16 @@ + + + + + + net9.0 + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs new file mode 100644 index 0000000000..c416477f53 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/AbpTimingTestModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Timing; + +[DependsOn( + typeof(AbpTimingModule), + typeof(AbpTestBaseModule) +)] +public class AbpTimingTestModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + } +} diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs new file mode 100644 index 0000000000..4cd8c9b8c6 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Default_Tests.cs @@ -0,0 +1,110 @@ +using System; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Timing; + +public class Clock_Default_Tests : AbpIntegratedTest +{ + private readonly IClock _clock; + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + + public Clock_Default_Tests() + { + _clock = GetRequiredService(); + _currentTimezoneProvider = GetRequiredService(); + } + + [Fact] + public void ConvertTo_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertTo(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T05:30:00.0000000Z"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertTo(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + + using(_currentTimezoneProvider.Change(null)) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertTo(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + } + + [Fact] + public void ConvertTo_DateTimeOffset_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.Zero); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.Zero); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(3)); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(8)); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(8)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + } + + [Fact] + public void ConvertFrom_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); + var convertedDateTime = _clock.ConvertFrom(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Unspecified); + convertedDateTime.ShouldBe(dateTime); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertFrom(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ShouldBe(dateTime); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertFrom(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ShouldBe(dateTime); + } + } +} diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs new file mode 100644 index 0000000000..8f707fb901 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/Clock_Utc_Tests.cs @@ -0,0 +1,132 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Timing; + +public class Clock_Utc_Tests : AbpIntegratedTest +{ + private readonly IClock _clock; + private readonly ICurrentTimezoneProvider _currentTimezoneProvider; + + public Clock_Utc_Tests() + { + _clock = GetRequiredService(); + _currentTimezoneProvider = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.Kind = DateTimeKind.Utc; + }); + base.AfterAddApplication(services); + } + + [Fact] + public void ConvertTo_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertTo(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Unspecified); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000"); //Without Z + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + // ConvertTo will not convert the DateTimeKind.Local + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertTo(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + + using(_currentTimezoneProvider.Change(null)) + { + // ConvertTo will not convert if the timezone is not set + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertTo(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Local); + convertedDateTime.ToString("O").ShouldBe(dateTime.ToString("O")); + } + } + + [Fact] + public void ConvertTo_DateTimeOffset_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.Zero); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ToString("O").ShouldBe("2025-03-01T08:30:00.0000000+03:00"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(3)); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(8)); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(3)); + convertedDateTimeOffset.DateTime.ShouldBe(new DateTime(2025, 3, 1, 0, 30, 0, DateTimeKind.Unspecified)); + } + + using(_currentTimezoneProvider.Change(null)) + { + // ConvertTo will not convert if the timezone is not set + var dateTimeOffset = new DateTimeOffset(new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified), TimeSpan.FromHours(8)); + var convertedDateTimeOffset = _clock.ConvertTo(dateTimeOffset); + convertedDateTimeOffset.Offset.ShouldBe(TimeSpan.FromHours(8)); + convertedDateTimeOffset.ShouldBe(dateTimeOffset); + } + } + + [Fact] + public void ConvertFrom_Test() + { + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Unspecified); + var convertedDateTime = _clock.ConvertFrom(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Local); + var convertedDateTime = _clock.ConvertFrom(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T02:30:00.0000000Z"); + } + + using(_currentTimezoneProvider.Change("Europe/Istanbul")) + { + var dateTime = new DateTime(2025, 3, 1, 5, 30, 0, DateTimeKind.Utc); + var convertedDateTime = _clock.ConvertFrom(dateTime); + convertedDateTime.Kind.ShouldBe(DateTimeKind.Utc); + convertedDateTime.ToString("O").ShouldBe("2025-03-01T05:30:00.0000000Z"); + } + } + +} diff --git a/npm/packs/core/src/abp.js b/npm/packs/core/src/abp.js index c06e58631c..57a3c606cf 100644 --- a/npm/packs/core/src/abp.js +++ b/npm/packs/core/src/abp.js @@ -775,7 +775,7 @@ var abp = abp || {}; if (abp.clock.supportsMultipleTimezone()) { var timeZone = abp.clock.timeZone(); var now = new Date(); - var formattedDate = now.toLocaleString('en-US', { timeZone: timeZone, timeZoneName: 'short' }); + var formattedDate = now.toLocaleString('en-US', { timeZone: timeZone, timeZoneName: 'longOffset' }); var match = formattedDate.match(/GMT([+-]\d+)/); var targetOffsetHours = match ? parseInt(match[1], 10) : 0; var dateObj = new Date(dateObj.getTime() - (targetOffsetHours * 60 * 60 * 1000)); @@ -805,14 +805,15 @@ var abp = abp || {}; return dateString; } + var culture = abp.localization.currentCulture.cultureName; + options = Object.assign({}, abp.clock.toLocaleStringOptions, options || {}); if (abp.clock.supportsMultipleTimezone()) { var timezone = abp.clock.timeZone(); if (timezone) { - options = options || {}; - return date.toLocaleString(abp.localization.currentCulture.cultureName, Object.assign({}, abp.clock.toLocaleStringOptions, options, { timeZone: timezone })); + return date.toLocaleString(culture, Object.assign({}, options, { timeZone: timezone })); } } - return date.toLocaleString(); + return date.toLocaleString(culture, options); } /* FEATURES *************************************************/ diff --git a/npm/packs/luxon/src/abp.luxon.js b/npm/packs/luxon/src/abp.luxon.js index b04de7cadf..0cf90eb055 100644 --- a/npm/packs/luxon/src/abp.luxon.js +++ b/npm/packs/luxon/src/abp.luxon.js @@ -43,4 +43,56 @@ var abp = abp || {}; return form; } + // Normalize Date object or date string to standard string format that will be sent to server + abp.clock.normalizeToString = function (date) { + if (!date) { + return date; + } + + var dateObj = date instanceof Date ? date : new Date(date); + if (isNaN(dateObj)) { + return date; + } + + var timeZone = abp.clock.timeZone(); + if (abp.clock.supportsMultipleTimezone() && timeZone) { + return DateTime.fromObject({ + year: dateObj.getFullYear(), + month: dateObj.getMonth() + 1, + day: dateObj.getDate(), + hour: dateObj.getHours(), + minute: dateObj.getMinutes(), + second: dateObj.getSeconds() + }, { zone: timeZone }).setZone("utc").toISO(); + } else { + return luxon.DateTime.fromJSDate(dateObj).toFormat("yyyy-MM-dd'T'HH:mm:ss"); + } + }; + + // Normalize date string to locale date string that will be displayed to user + abp.clock.normalizeToLocaleString = function (dateString, options) { + if (!dateString) { + return dateString; + } + + var date = new Date(dateString); + if (isNaN(date)) { + return dateString; + } + + options = options || luxon.DateTime.DATETIME_FULL; + if (abp.clock.supportsMultipleTimezone()) { + var timezone = abp.clock.timeZone(); + if (timezone) { + return luxon.DateTime.fromJSDate(date) + .setZone(timezone) + .setLocale(abp.localization.currentCulture.cultureName) + .toLocaleString(Object.assign({}, abp.clock.toLocaleStringOptions, options)); + } + } + + return luxon.DateTime.fromJSDate(date) + .setLocale(abp.localization.currentCulture.cultureName) + .toLocaleString(Object.assign({}, abp.clock.toLocaleStringOptions, options)); + } })(jQuery);