mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-06-28 15:34:39 +08:00
Ignore DST in time zone conversion for time-only DateTime fields. Fixes #6731.
This commit is contained in:
parent
03bac9222b
commit
fbe5945ea6
@ -3,6 +3,7 @@ using Autofac;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Orchard.Localization.Models;
|
using Orchard.Localization.Models;
|
||||||
using Orchard.Localization.Services;
|
using Orchard.Localization.Services;
|
||||||
|
using Orchard.Tests.Stubs;
|
||||||
|
|
||||||
namespace Orchard.Tests.Localization {
|
namespace Orchard.Tests.Localization {
|
||||||
|
|
||||||
@ -303,10 +304,11 @@ namespace Orchard.Tests.Localization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Description("DST date and time are not properly round-tripped when date is ignored.")]
|
[Description("DST is ignored when date is ignored (non-DST date).")]
|
||||||
public void ConvertToLocalizedTimeStringTest03() {
|
public void ConvertToLocalizedTimeStringTest03() {
|
||||||
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
|
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
|
||||||
var container = TestHelpers.InitializeContainer("en-US", null, timeZone);
|
var clock = new StubClock(new DateTime(2012, 1, 1, 12, 0, 0, DateTimeKind.Utc));
|
||||||
|
var container = TestHelpers.InitializeContainer("en-US", null, timeZone, clock);
|
||||||
var target = container.Resolve<IDateLocalizationServices>();
|
var target = container.Resolve<IDateLocalizationServices>();
|
||||||
|
|
||||||
var dateString = "3/10/2012";
|
var dateString = "3/10/2012";
|
||||||
@ -318,7 +320,27 @@ namespace Orchard.Tests.Localization {
|
|||||||
var timeString2 = target.ConvertToLocalizedTimeString(dateTimeUtc, new DateLocalizationOptions() { IgnoreDate = true });
|
var timeString2 = target.ConvertToLocalizedTimeString(dateTimeUtc, new DateLocalizationOptions() { IgnoreDate = true });
|
||||||
|
|
||||||
Assert.AreEqual(dateString, dateString2);
|
Assert.AreEqual(dateString, dateString2);
|
||||||
Assert.AreNotEqual(timeString, timeString2);
|
Assert.AreEqual(timeString, timeString2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Description("DST is ignored when date is ignored (DST date).")]
|
||||||
|
public void ConvertToLocalizedTimeStringTest04() {
|
||||||
|
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
|
||||||
|
var clock = new StubClock(new DateTime(2012, 10, 3, 12, 0, 0, DateTimeKind.Utc));
|
||||||
|
var container = TestHelpers.InitializeContainer("en-US", null, timeZone, clock);
|
||||||
|
var target = container.Resolve<IDateLocalizationServices>();
|
||||||
|
|
||||||
|
var dateString = "3/10/2012";
|
||||||
|
var timeString = "12:00:00 PM";
|
||||||
|
|
||||||
|
var dateTimeUtc = target.ConvertFromLocalizedString(dateString, timeString);
|
||||||
|
|
||||||
|
var dateString2 = target.ConvertToLocalizedDateString(dateTimeUtc);
|
||||||
|
var timeString2 = target.ConvertToLocalizedTimeString(dateTimeUtc, new DateLocalizationOptions() { IgnoreDate = true });
|
||||||
|
|
||||||
|
Assert.AreEqual(dateString, dateString2);
|
||||||
|
Assert.AreEqual(timeString, timeString2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9,9 +9,12 @@ using Orchard.Tests.Stubs;
|
|||||||
namespace Orchard.Tests.Localization {
|
namespace Orchard.Tests.Localization {
|
||||||
|
|
||||||
internal class TestHelpers {
|
internal class TestHelpers {
|
||||||
public static IContainer InitializeContainer(string cultureName, string calendarName, TimeZoneInfo timeZone) {
|
public static IContainer InitializeContainer(string cultureName, string calendarName, TimeZoneInfo timeZone, IClock clock = null) {
|
||||||
var builder = new ContainerBuilder();
|
var builder = new ContainerBuilder();
|
||||||
builder.RegisterType<StubClock>().As<IClock>();
|
if (clock != null)
|
||||||
|
builder.RegisterInstance(clock);
|
||||||
|
else
|
||||||
|
builder.RegisterType<StubClock>().As<IClock>();
|
||||||
builder.RegisterInstance<WorkContext>(new StubWorkContext(cultureName, calendarName, timeZone));
|
builder.RegisterInstance<WorkContext>(new StubWorkContext(cultureName, calendarName, timeZone));
|
||||||
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
|
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
|
||||||
builder.RegisterType<CultureDateTimeFormatProvider>().As<IDateTimeFormatProvider>();
|
builder.RegisterType<CultureDateTimeFormatProvider>().As<IDateTimeFormatProvider>();
|
||||||
|
@ -4,8 +4,13 @@ using Orchard.Services;
|
|||||||
|
|
||||||
namespace Orchard.Tests.Stubs {
|
namespace Orchard.Tests.Stubs {
|
||||||
public class StubClock : IClock {
|
public class StubClock : IClock {
|
||||||
public StubClock() {
|
|
||||||
UtcNow = new DateTime(2009, 10, 14, 12, 34, 56, DateTimeKind.Utc);
|
public StubClock()
|
||||||
|
: this(new DateTime(2009, 10, 14, 12, 34, 56, DateTimeKind.Utc)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public StubClock(DateTime utcNow) {
|
||||||
|
UtcNow = utcNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime UtcNow { get; private set; }
|
public DateTime UtcNow { get; private set; }
|
||||||
@ -18,7 +23,6 @@ namespace Orchard.Tests.Stubs {
|
|||||||
return UtcNow.Add(span);
|
return UtcNow.Add(span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public IVolatileToken When(TimeSpan duration) {
|
public IVolatileToken When(TimeSpan duration) {
|
||||||
return new Clock.AbsoluteExpirationToken(this, duration);
|
return new Clock.AbsoluteExpirationToken(this, duration);
|
||||||
}
|
}
|
||||||
|
@ -110,14 +110,15 @@ namespace Orchard.Localization.Services {
|
|||||||
|
|
||||||
if (options.EnableTimeZoneConversion) {
|
if (options.EnableTimeZoneConversion) {
|
||||||
if (options.IgnoreDate) {
|
if (options.IgnoreDate) {
|
||||||
// The caller has asked us to ignore the date part and assume it is today. This usually because the source
|
// The caller has asked us to ignore the date part. This usually because the source
|
||||||
// is a time-only field, in which case the date part is usually DateTime.MinValue which we should not use
|
// is a time-only field. In such cases (with an undefined date) it does not make sense
|
||||||
// for the following reasons:
|
// to consider DST variations throughout the year, so we will use an arbitrary (but fixed)
|
||||||
// * DST can be active or not dependeng on the time of the year. We want the conversion to always act as if the time represents today, but we don't want that date stored.
|
// non-DST date for the conversion to ensure DST is never applied during conversion. The
|
||||||
// * Time zone conversion cannot wrap DateTime.MinValue around to the previous day, resulting in undefined result.
|
// date part is usually DateTime.MinValue which we should not use because time zone
|
||||||
// Therefore we convert the date to today's date before the conversion, and back to the original date after.
|
// conversion cannot wrap DateTime.MinValue around to the previous day, resulting in
|
||||||
var today = _clock.UtcNow.Date;
|
// an undefined result. Instead we convert the date to a hard-coded date of 2000-01-01
|
||||||
var tempDate = new DateTime(today.Year, today.Month, today.Day, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
|
// before the conversion, and back to the original date after.
|
||||||
|
var tempDate = new DateTime(2000, 1, 1, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
|
||||||
tempDate = ConvertToSiteTimeZone(tempDate);
|
tempDate = ConvertToSiteTimeZone(tempDate);
|
||||||
dateValue = new DateTime(dateValue.Year, dateValue.Month, dateValue.Day, tempDate.Hour, tempDate.Minute, tempDate.Second, tempDate.Millisecond, tempDate.Kind);
|
dateValue = new DateTime(dateValue.Year, dateValue.Month, dateValue.Day, tempDate.Hour, tempDate.Minute, tempDate.Second, tempDate.Millisecond, tempDate.Kind);
|
||||||
}
|
}
|
||||||
@ -201,14 +202,16 @@ namespace Orchard.Localization.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasTime && options.EnableTimeZoneConversion) {
|
if (hasTime && options.EnableTimeZoneConversion) {
|
||||||
// If there is no date component (technically the date component is that of DateTime.MinValue) then
|
// If there is no date component (technically the date component is that of DateTime.MinValue)
|
||||||
// we must employ some trickery, for two reasons:
|
// then we must employ some trickery. With an undefined date it does not make sense
|
||||||
// * DST can be active or not dependeng on the time of the year. We want the conversion to always act as if the time represents today, but we don't want that date stored.
|
// to consider DST variations throughout the year, so we will use an arbitrary (but fixed)
|
||||||
// * Time zone conversion cannot wrap DateTime.MinValue around to the previous day, resulting in undefined result.
|
// non-DST date for the conversion to ensure DST is never applied during conversion. The
|
||||||
// Therefore we convert the date to today's date before the conversion, and back to DateTime.MinValue after.
|
// date part is usually DateTime.MinValue which we should not use because time zone
|
||||||
|
// conversion cannot wrap DateTime.MinValue around to the previous day, resulting in
|
||||||
|
// an undefined result. Instead we convert the date to a hard-coded date of 2000-01-01
|
||||||
|
// before the conversion, and back to the original date after.
|
||||||
if (!hasDate) {
|
if (!hasDate) {
|
||||||
var now = _clock.UtcNow;
|
dateValue = new DateTime(2000, 1, 1, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
|
||||||
dateValue = new DateTime(now.Year, now.Month, now.Day, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
|
|
||||||
}
|
}
|
||||||
dateValue = ConvertFromSiteTimeZone(dateValue);
|
dateValue = ConvertFromSiteTimeZone(dateValue);
|
||||||
if (!hasDate) {
|
if (!hasDate) {
|
||||||
|
Loading…
Reference in New Issue
Block a user