From 3175a280ac2dadbdd040097c50b797e7db22362a Mon Sep 17 00:00:00 2001 From: Daniel Stolt Date: Sat, 2 Aug 2014 03:19:34 +0200 Subject: [PATCH] Did initial draft rewrite of DefaultDateLocalizationServices. --- .../Common/DateEditor/DateEditorDriver.cs | 12 +- src/Orchard.Web/Core/Shapes/DateTimeShapes.cs | 10 +- .../Drivers/ArchiveLaterPartDriver.cs | 12 +- .../Controllers/AdminController.cs | 4 +- .../AuditTrailTrimmingSettingsPartDriver.cs | 9 +- .../Services/CommonAuditTrailEventHandler.cs | 8 +- .../Drivers/DateTimeFieldDriver.cs | 16 +- .../Drivers/PublishLaterPartDriver.cs | 12 +- .../Orchard.Tokens/Providers/DateTokens.cs | 8 +- .../Models/DateLocalizationOptions.cs | 31 +++ src/Orchard/Localization/Models/DateParts.cs | 6 + .../Localization/Models/DateTimeParts.cs | 11 + src/Orchard/Localization/Models/TimeParts.cs | 6 + .../DefaultDateLocalizationServices.cs | 191 ++++++++---------- .../Services/IDateLocalizationServices.cs | 170 ++++++++++------ .../Services/IDateLocalizationServices.txt | 4 +- src/Orchard/Orchard.Framework.csproj | 1 + 17 files changed, 299 insertions(+), 212 deletions(-) create mode 100644 src/Orchard/Localization/Models/DateLocalizationOptions.cs diff --git a/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs b/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs index d8859c7e5..ae6d689f3 100644 --- a/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs +++ b/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs @@ -8,12 +8,12 @@ using Orchard.Localization.Services; namespace Orchard.Core.Common.DateEditor { public class DateEditorDriver : ContentPartDriver { - private readonly IDateLocalizationServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; public DateEditorDriver( IOrchardServices services, - IDateLocalizationServices dateServices) { - _dateServices = dateServices; + IDateLocalizationServices dateLocalizationServices) { + _dateLocalizationServices = dateLocalizationServices; T = NullLocalizer.Instance; Services = services; } @@ -59,8 +59,8 @@ namespace Orchard.Core.Common.DateEditor { theDatesHaveNotBeenModified; if (!theEditorShouldBeBlank) { - model.Editor.Date = _dateServices.ConvertToLocalDateString(part.CreatedUtc, ""); - model.Editor.Time = _dateServices.ConvertToLocalTimeString(part.CreatedUtc, ""); + model.Editor.Date = _dateLocalizationServices.ConvertToLocalizedDateString(part.CreatedUtc); + model.Editor.Time = _dateLocalizationServices.ConvertToLocalizedTimeString(part.CreatedUtc); } } @@ -69,7 +69,7 @@ namespace Orchard.Core.Common.DateEditor { if (!String.IsNullOrWhiteSpace(model.Editor.Date) && !String.IsNullOrWhiteSpace(model.Editor.Time)) { try { - var utcDateTime = _dateServices.ConvertFromLocalString(model.Editor.Date, model.Editor.Time); + var utcDateTime = _dateLocalizationServices.ConvertFromLocalizedString(model.Editor.Date, model.Editor.Time); part.CreatedUtc = utcDateTime; part.VersionCreatedUtc = utcDateTime; } diff --git a/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs b/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs index d39f23adc..f63f4bedc 100644 --- a/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs +++ b/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs @@ -11,16 +11,16 @@ using Orchard.Services; namespace Orchard.Core.Shapes { public class DateTimeShapes : IDependency { private readonly IClock _clock; - private readonly IDateLocalizationServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; private readonly IDateTimeFormatProvider _dateTimeLocalization; public DateTimeShapes( IClock clock, - IDateLocalizationServices dateServices, + IDateLocalizationServices dateLocalizationServices, IDateTimeFormatProvider dateTimeLocalization ) { _clock = clock; - _dateServices = dateServices; + _dateLocalizationServices = dateLocalizationServices; _dateTimeLocalization = dateTimeLocalization; T = NullLocalizer.Instance; } @@ -65,10 +65,10 @@ namespace Orchard.Core.Shapes { //using a LocalizedString forces the caller to use a localizable format if (CustomFormat == null || String.IsNullOrWhiteSpace(CustomFormat.Text)) { - return new MvcHtmlString(_dateServices.ConvertToLocalString(DateTimeUtc, _dateTimeLocalization.LongDateTimeFormat, null)); + return new MvcHtmlString(_dateLocalizationServices.ConvertToLocalizedString(DateTimeUtc, _dateTimeLocalization.LongDateTimeFormat)); } - return new MvcHtmlString(_dateServices.ConvertToLocalString(DateTimeUtc, CustomFormat.Text, null)); + return new MvcHtmlString(_dateLocalizationServices.ConvertToLocalizedString(DateTimeUtc, CustomFormat.Text)); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.ArchiveLater/Drivers/ArchiveLaterPartDriver.cs b/src/Orchard.Web/Modules/Orchard.ArchiveLater/Drivers/ArchiveLaterPartDriver.cs index 5d9d031d7..0e53f9ce1 100644 --- a/src/Orchard.Web/Modules/Orchard.ArchiveLater/Drivers/ArchiveLaterPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.ArchiveLater/Drivers/ArchiveLaterPartDriver.cs @@ -14,14 +14,14 @@ namespace Orchard.ArchiveLater.Drivers { public class ArchiveLaterPartDriver : ContentPartDriver { private const string TemplateName = "Parts/ArchiveLater"; private readonly IArchiveLaterService _archiveLaterService; - private readonly IDateLocalizationServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; public ArchiveLaterPartDriver( IOrchardServices services, IArchiveLaterService archiveLaterService, - IDateLocalizationServices dateServices) { + IDateLocalizationServices dateLocalizationServices) { _archiveLaterService = archiveLaterService; - _dateServices = dateServices; + _dateLocalizationServices = dateLocalizationServices; T = NullLocalizer.Instance; Services = services; } @@ -56,8 +56,8 @@ namespace Orchard.ArchiveLater.Drivers { Editor = new DateTimeEditor() { ShowDate = true, ShowTime = true, - Date = _dateServices.ConvertToLocalDateString(part.ScheduledArchiveUtc.Value, ""), - Time = _dateServices.ConvertToLocalTimeString(part.ScheduledArchiveUtc.Value, ""), + Date = _dateLocalizationServices.ConvertToLocalizedDateString(part.ScheduledArchiveUtc.Value), + Time = _dateLocalizationServices.ConvertToLocalizedTimeString(part.ScheduledArchiveUtc.Value), } }; @@ -71,7 +71,7 @@ namespace Orchard.ArchiveLater.Drivers { if (updater.TryUpdateModel(model, Prefix, null, null)) { if (model.ArchiveLater) { try { - var utcDateTime = _dateServices.ConvertFromLocalString(model.Editor.Date, model.Editor.Time); + var utcDateTime = _dateLocalizationServices.ConvertFromLocalizedString(model.Editor.Date, model.Editor.Time); _archiveLaterService.ArchiveLater(model.ContentItem, utcDateTime.HasValue ? utcDateTime.Value : DateTime.MaxValue); } catch (FormatException) { diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/AdminController.cs index ef06f48f5..12e8e7790 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Controllers/AdminController.cs @@ -17,13 +17,11 @@ namespace Orchard.AuditTrail.Controllers { private readonly IAuditTrailManager _auditTrailManager; private readonly IOrchardServices _services; private readonly IAuditTrailEventDisplayBuilder _displayBuilder; - private readonly IDateServices _dateServices; - public AdminController(IAuditTrailManager auditTrailManager, IOrchardServices services, IAuditTrailEventDisplayBuilder displayBuilder, IDateServices dateServices) { + public AdminController(IAuditTrailManager auditTrailManager, IOrchardServices services, IAuditTrailEventDisplayBuilder displayBuilder) { _auditTrailManager = auditTrailManager; _services = services; _displayBuilder = displayBuilder; - _dateServices = dateServices; _authorizer = services.Authorizer; New = _services.New; } diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailTrimmingSettingsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailTrimmingSettingsPartDriver.cs index f48d13cf2..cc53548b0 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailTrimmingSettingsPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailTrimmingSettingsPartDriver.cs @@ -4,6 +4,7 @@ using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.Environment.Extensions; using Orchard.Localization; +using Orchard.Localization.Models; using Orchard.Localization.Services; using Orchard.Security; @@ -11,12 +12,12 @@ namespace Orchard.AuditTrail.Drivers { [OrchardFeature("Orchard.AuditTrail.Trimming")] public class AuditTrailTrimmingSettingsPartDriver : ContentPartDriver { private readonly IAuthorizer _authorizer; - private readonly IDateServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; private readonly IDateTimeFormatProvider _dateTimeLocalization; - public AuditTrailTrimmingSettingsPartDriver(IAuthorizer authorizer, IDateServices dateServices, IDateTimeFormatProvider dateTimeLocalization) { + public AuditTrailTrimmingSettingsPartDriver(IAuthorizer authorizer, IDateLocalizationServices dateLocalizationServices, IDateTimeFormatProvider dateTimeLocalization) { _authorizer = authorizer; - _dateServices = dateServices; + _dateLocalizationServices = dateLocalizationServices; _dateTimeLocalization = dateTimeLocalization; T = NullLocalizer.Instance; } @@ -35,7 +36,7 @@ namespace Orchard.AuditTrail.Drivers { var viewModel = new AuditTrailTrimmingSettingsViewModel { RetentionPeriod = part.RetentionPeriod, MinimumRunInterval = part.MinimumRunInterval, - LastRunDateString = _dateServices.ConvertToLocalString(part.LastRunUtc, _dateTimeLocalization.ShortDateTimeFormat, T("Never").Text) + LastRunDateString = _dateLocalizationServices.ConvertToLocalizedString(part.LastRunUtc, _dateTimeLocalization.ShortDateTimeFormat, new DateLocalizationOptions() { NullText = T("Never").Text }) }; if (updater != null) { diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/CommonAuditTrailEventHandler.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/CommonAuditTrailEventHandler.cs index de4a22499..9df7d536f 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/CommonAuditTrailEventHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/CommonAuditTrailEventHandler.cs @@ -8,11 +8,11 @@ using Orchard.Localization.Services; namespace Orchard.AuditTrail.Services { public class CommonAuditTrailEventHandler : AuditTrailEventHandlerBase { private readonly Lazy _auditTrailManager; - private readonly IDateServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; - public CommonAuditTrailEventHandler(Lazy auditTrailManager, IDateServices dateServices) { + public CommonAuditTrailEventHandler(Lazy auditTrailManager, IDateLocalizationServices dateLocalizationServices) { _auditTrailManager = auditTrailManager; - _dateServices = dateServices; + _dateLocalizationServices = dateLocalizationServices; } public override void Filter(QueryFilterContext context) { @@ -60,7 +60,7 @@ namespace Orchard.AuditTrail.Services { try { var dateString = filters.Get(prefix + ".Date"); var timeString = filters.Get(prefix + ".Time"); - return _dateServices.ConvertFromLocalString(dateString, timeString); + return _dateLocalizationServices.ConvertFromLocalizedString(dateString, timeString); } catch (FormatException ex) { filters.UpdateModel.AddModelError(prefix, T(@"Error parsing ""{0}"" date: {1}", fieldName, ex.Message)); diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs b/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs index 365ac45f7..12becc6cf 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs @@ -17,14 +17,14 @@ namespace Orchard.Fields.Drivers { public class DateTimeFieldDriver : ContentFieldDriver { private const string TemplateName = "Fields/DateTime.Edit"; // EditorTemplates/Fields/DateTime.Edit.cshtml - public DateTimeFieldDriver(IOrchardServices services, IDateLocalizationServices dateServices) { + public DateTimeFieldDriver(IOrchardServices services, IDateLocalizationServices dateLocalizationServices) { Services = services; - DateServices = dateServices; + DateLocalizationServices = dateLocalizationServices; T = NullLocalizer.Instance; } public IOrchardServices Services { get; set; } - public IDateLocalizationServices DateServices { get; set; } + public IDateLocalizationServices DateLocalizationServices { get; set; } public Localizer T { get; set; } private static string GetPrefix(ContentField field, ContentPart part) { @@ -47,8 +47,8 @@ namespace Orchard.Fields.Drivers { Hint = settings.Hint, IsRequired = settings.Required, Editor = new DateTimeEditor() { - Date = DateServices.ConvertToLocalDateString(value, String.Empty), - Time = DateServices.ConvertToLocalTimeString(value, String.Empty), + Date = DateLocalizationServices.ConvertToLocalizedDateString(value), + Time = DateLocalizationServices.ConvertToLocalizedTimeString(value), ShowDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly, ShowTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly, } @@ -69,8 +69,8 @@ namespace Orchard.Fields.Drivers { Hint = settings.Hint, IsRequired = settings.Required, Editor = new DateTimeEditor() { - Date = DateServices.ConvertToLocalDateString(value, String.Empty), - Time = DateServices.ConvertToLocalTimeString(value, String.Empty), + Date = DateLocalizationServices.ConvertToLocalizedDateString(value), + Time = DateLocalizationServices.ConvertToLocalizedTimeString(value), ShowDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly, ShowTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly, } @@ -90,7 +90,7 @@ namespace Orchard.Fields.Drivers { updater.AddModelError(GetPrefix(field, part), T("{0} is required.", field.DisplayName)); } else { try { - var utcDateTime = DateServices.ConvertFromLocalString(viewModel.Editor.Date, viewModel.Editor.Time); + var utcDateTime = DateLocalizationServices.ConvertFromLocalizedString(viewModel.Editor.Date, viewModel.Editor.Time); if (utcDateTime.HasValue) { field.DateTime = utcDateTime.Value; } else { diff --git a/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs b/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs index 8a5581adf..0547e5a26 100644 --- a/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs @@ -18,18 +18,18 @@ namespace Orchard.PublishLater.Drivers { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IPublishLaterService _publishLaterService; private readonly IClock _clock; - private readonly IDateLocalizationServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; public PublishLaterPartDriver( IOrchardServices services, IHttpContextAccessor httpContextAccessor, IPublishLaterService publishLaterService, IClock clock, - IDateLocalizationServices dateServices) { + IDateLocalizationServices dateLocalizationServices) { _httpContextAccessor = httpContextAccessor; _publishLaterService = publishLaterService; _clock = clock; - _dateServices = dateServices; + _dateLocalizationServices = dateLocalizationServices; T = NullLocalizer.Instance; Services = services; } @@ -65,8 +65,8 @@ namespace Orchard.PublishLater.Drivers { Editor = new DateTimeEditor() { ShowDate = true, ShowTime = true, - Date = !part.IsPublished() ? _dateServices.ConvertToLocalDateString(part.ScheduledPublishUtc.Value, "") : "", - Time = !part.IsPublished() ? _dateServices.ConvertToLocalTimeString(part.ScheduledPublishUtc.Value, "") : "", + Date = !part.IsPublished() ? _dateLocalizationServices.ConvertToLocalizedDateString(part.ScheduledPublishUtc.Value) : "", + Time = !part.IsPublished() ? _dateLocalizationServices.ConvertToLocalizedTimeString(part.ScheduledPublishUtc.Value) : "", } }; @@ -82,7 +82,7 @@ namespace Orchard.PublishLater.Drivers { if (httpContext.Request.Form["submit.Save"] == "submit.PublishLater") { if (!String.IsNullOrWhiteSpace(model.Editor.Date) && !String.IsNullOrWhiteSpace(model.Editor.Time)) { try { - var utcDateTime = _dateServices.ConvertFromLocalString(model.Editor.Date, model.Editor.Time); + var utcDateTime = _dateLocalizationServices.ConvertFromLocalizedString(model.Editor.Date, model.Editor.Time); if (utcDateTime.HasValue) { if (utcDateTime.Value < _clock.UtcNow) { updater.AddModelError("ScheduledPublishUtcDate", T("You cannot schedule a publishing date in the past.")); diff --git a/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs b/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs index 8418947cb..bfd0afc67 100644 --- a/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs +++ b/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs @@ -11,7 +11,7 @@ namespace Orchard.Tokens.Providers { private readonly IDateTimeFormatProvider _dateTimeLocalization; private readonly IWorkContextAccessor _workContextAccessor; private readonly Lazy _cultureInfo; - private readonly IDateLocalizationServices _dateServices; + private readonly IDateLocalizationServices _dateLocalizationServices; public DateTokens( IClock clock, @@ -22,7 +22,7 @@ namespace Orchard.Tokens.Providers { _dateTimeLocalization = dateTimeLocalization; _workContextAccessor = workContextAccessor; _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(_workContextAccessor.GetContext().CurrentCulture)); - _dateServices = dateServices; + _dateLocalizationServices = dateServices; T = NullLocalizer.Instance; } @@ -45,8 +45,8 @@ namespace Orchard.Tokens.Providers { .Token("Since", DateTimeRelative) .Chain("Since", "Date", DateTimeRelative) // {Date.Local} - .Token("Local", d => _dateServices.ConvertToSiteTimeZone(d)) - .Chain("Local", "Date", d => _dateServices.ConvertToSiteTimeZone(d)) + .Token("Local", d => _dateLocalizationServices.ConvertToSiteTimeZone(d)) + .Chain("Local", "Date", d => _dateLocalizationServices.ConvertToSiteTimeZone(d)) // {Date.ShortDate} .Token("ShortDate", d => d.ToString(_dateTimeLocalization.ShortDateFormat, _cultureInfo.Value)) // {Date.ShortTime} diff --git a/src/Orchard/Localization/Models/DateLocalizationOptions.cs b/src/Orchard/Localization/Models/DateLocalizationOptions.cs new file mode 100644 index 000000000..926d0f420 --- /dev/null +++ b/src/Orchard/Localization/Models/DateLocalizationOptions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Orchard.Localization.Models { + public class DateLocalizationOptions { + + public DateLocalizationOptions() { + NullText = String.Empty; + EnableTimeZoneConversion = true; + EnableCalendarConversion = true; + } + + /// + /// Gets on sets the string to use in place of a null date when converting to and from a string. The default is an empty string. + /// + public string NullText { get; set; } + + /// + /// Gets or sets a boolean indicating whether to perform time zone conversion as part of converting a date to and from a string. The default is true. + /// + public bool EnableTimeZoneConversion { get; set; } + + /// + /// Gets or sets a boolean indicating whether to perform calendar conversion as part of converting a date to and from a string. The default is true. + /// + public bool EnableCalendarConversion { get; set; } + } +} diff --git a/src/Orchard/Localization/Models/DateParts.cs b/src/Orchard/Localization/Models/DateParts.cs index c6e3b3380..46051d23e 100644 --- a/src/Orchard/Localization/Models/DateParts.cs +++ b/src/Orchard/Localization/Models/DateParts.cs @@ -5,6 +5,12 @@ using System.Linq; namespace Orchard.Localization.Models { public struct DateParts { + public static DateParts MinValue { + get { + return new DateParts(DateTime.MinValue.Year, DateTime.MinValue.Month, DateTime.MinValue.Day); + } + } + public DateParts(int year, int month, int day) { _year = year; _month = month; diff --git a/src/Orchard/Localization/Models/DateTimeParts.cs b/src/Orchard/Localization/Models/DateTimeParts.cs index d1cdf023c..64b027de4 100644 --- a/src/Orchard/Localization/Models/DateTimeParts.cs +++ b/src/Orchard/Localization/Models/DateTimeParts.cs @@ -5,6 +5,17 @@ using System.Linq; namespace Orchard.Localization.Models { public struct DateTimeParts { + //public static DateTimeParts? FromDateTime(DateTime? dateTime) { + // if (!dateTime.HasValue) { + // return null; + // } + // return FromDateTime(dateTime.Value); + //} + + public static DateTimeParts FromDateTime(DateTime dateTime) { + return new DateTimeParts(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + } + public DateTimeParts(int year, int month, int day, int hour, int minute, int second, int millisecond) { _date = new DateParts(year, month, day); _time = new TimeParts(hour, minute, second, millisecond); diff --git a/src/Orchard/Localization/Models/TimeParts.cs b/src/Orchard/Localization/Models/TimeParts.cs index f57690ee5..e78ae27d2 100644 --- a/src/Orchard/Localization/Models/TimeParts.cs +++ b/src/Orchard/Localization/Models/TimeParts.cs @@ -5,6 +5,12 @@ using System.Linq; namespace Orchard.Localization.Models { public struct TimeParts { + public static TimeParts MinValue { + get { + return new TimeParts(DateTime.MinValue.Hour, DateTime.MinValue.Minute, DateTime.MinValue.Second, DateTime.MinValue.Millisecond); + } + } + public TimeParts(int hour, int minute, int second, int millisecond) { _hour = hour; _minute = minute; diff --git a/src/Orchard/Localization/Services/DefaultDateLocalizationServices.cs b/src/Orchard/Localization/Services/DefaultDateLocalizationServices.cs index ca3b20783..dd71a76b7 100644 --- a/src/Orchard/Localization/Services/DefaultDateLocalizationServices.cs +++ b/src/Orchard/Localization/Services/DefaultDateLocalizationServices.cs @@ -10,169 +10,148 @@ namespace Orchard.Localization.Services { private readonly IWorkContextAccessor _workContextAccessor; private readonly IDateTimeFormatProvider _dateTimeFormatProvider; + private readonly IDateFormatter _dateFormatter; private readonly ICalendarManager _calendarManager; public DefaultDateLocalizationServices( IWorkContextAccessor workContextAccessor, IDateTimeFormatProvider dateTimeFormatProvider, + IDateFormatter dateFormatter, ICalendarManager calendarManager) { _workContextAccessor = workContextAccessor; _dateTimeFormatProvider = dateTimeFormatProvider; + _dateFormatter = dateFormatter; _calendarManager = calendarManager; } - public virtual DateTime? ConvertToSiteTimeZone(DateTime date) { - return ConvertToSiteTimeZone(ToNullable(date)); - } - - public virtual DateTime? ConvertToSiteTimeZone(DateTime? date) { - if (!date.HasValue) { - return null; - } + public virtual DateTime ConvertToSiteTimeZone(DateTime date) { var workContext = _workContextAccessor.GetContext(); - return TimeZoneInfo.ConvertTimeFromUtc(date.Value, workContext.CurrentTimeZone); + return TimeZoneInfo.ConvertTimeFromUtc(date, workContext.CurrentTimeZone); } - public virtual DateTime? ConvertFromSiteTimeZone(DateTime date) { - return ConvertFromSiteTimeZone(ToNullable(date)); - } - - public virtual DateTime? ConvertFromSiteTimeZone(DateTime? date) { - if (!date.HasValue) { - return null; - } + public virtual DateTime ConvertFromSiteTimeZone(DateTime date) { var workContext = _workContextAccessor.GetContext(); - return TimeZoneInfo.ConvertTimeToUtc(date.Value, workContext.CurrentTimeZone); + return TimeZoneInfo.ConvertTimeToUtc(date, workContext.CurrentTimeZone); } - public virtual DateTimeParts? ConvertToSiteCalendar(DateTime? date) { - if (!date.HasValue){ - return null; - } + public virtual DateTimeParts ConvertToSiteCalendar(DateTime date) { var calendar = CurrentCalendar; return new DateTimeParts( - calendar.GetYear(date.Value), - calendar.GetMonth(date.Value), - calendar.GetDayOfMonth(date.Value), - calendar.GetHour(date.Value), - calendar.GetMinute(date.Value), - calendar.GetSecond(date.Value), - Convert.ToInt32(calendar.GetMilliseconds(date.Value))); + calendar.GetYear(date), + calendar.GetMonth(date), + calendar.GetDayOfMonth(date), + calendar.GetHour(date), + calendar.GetMinute(date), + calendar.GetSecond(date), + Convert.ToInt32(calendar.GetMilliseconds(date))); } - public virtual DateTime? ConvertFromSiteCalendar(DateTimeParts? parts) { - if (!parts.HasValue) { - return null; - } - return new DateTime(parts.Value.Date.Year, parts.Value.Date.Month, parts.Value.Date.Day, parts.Value.Time.Hour, parts.Value.Time.Minute, parts.Value.Time.Second, parts.Value.Time.Millisecond, CurrentCalendar); + public virtual DateTime ConvertFromSiteCalendar(DateTimeParts parts) { + return CurrentCalendar.ToDateTime(parts.Date.Year, parts.Date.Month, parts.Date.Day, parts.Time.Hour, parts.Time.Minute, parts.Time.Second, parts.Time.Millisecond); } - - - public virtual string ConvertToLocalString(DateTime date, string nullText = null) { - return ConvertToLocalString(ToNullable(date), _dateTimeFormatProvider.LongDateTimeFormat, nullText); + public string ConvertToLocalizedDateString(DateTime date, DateLocalizationOptions options = null) { + return ConvertToLocalizedDateString(ToNullable(date), options); } - public virtual string ConvertToLocalString(DateTime date, string format, string nullText = null) { - return ConvertToLocalString(ToNullable(date), format, nullText); + public string ConvertToLocalizedDateString(DateTime? date, DateLocalizationOptions options = null) { + return ConvertToLocalizedString(date, _dateTimeFormatProvider.ShortDateFormat, options); } - public virtual string ConvertToLocalString(DateTime? date, string format, string nullText = null) { - var localDate = ConvertToSiteTimeZone(date); - if (!localDate.HasValue) { - return nullText; + public string ConvertToLocalizedTimeString(DateTime date, DateLocalizationOptions options = null) { + return ConvertToLocalizedTimeString(ToNullable(date), options); + } + + public string ConvertToLocalizedTimeString(DateTime? date, DateLocalizationOptions options = null) { + return ConvertToLocalizedString(date, _dateTimeFormatProvider.LongTimeFormat, options); + } + + public string ConvertToLocalizedString(DateTime date, DateLocalizationOptions options = null) { + return ConvertToLocalizedString(ToNullable(date), options); + } + + public string ConvertToLocalizedString(DateTime? date, DateLocalizationOptions options = null) { + return ConvertToLocalizedString(date, _dateTimeFormatProvider.ShortDateTimeFormat, options); + } + + public string ConvertToLocalizedString(DateTime date, string format, DateLocalizationOptions options = null) { + return ConvertToLocalizedString(ToNullable(date), format, options); + } + + public string ConvertToLocalizedString(DateTime? date, string format, DateLocalizationOptions options = null) { + options = options ?? new DateLocalizationOptions(); + + if (!date.HasValue) { + return options.NullText; } - // If the configured current calendar is different from the default calendar - // of the configured current culture we must override the conversion process. - // We do this by using a custom CultureInfo modified to use GregorianCalendar - // (means no calendar conversion will be performed as part of the string - // formatting) and instead perform the calendar conversion ourselves. + var dateValue = date.Value; - var cultureInfo = CurrentCulture; - var usingCultureCalendar = CurrentCulture.DateTimeFormat.Calendar.GetType().IsInstanceOfType(CurrentCalendar); - if (!usingCultureCalendar) { - cultureInfo = (CultureInfo)CurrentCulture.Clone(); - cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar(); - var calendar = CurrentCalendar; - localDate = new DateTime(calendar.GetYear(localDate.Value), calendar.GetMonth(localDate.Value), calendar.GetDayOfMonth(localDate.Value), calendar.GetHour(localDate.Value), calendar.GetMinute(localDate.Value), calendar.GetSecond(localDate.Value)); + if (options.EnableTimeZoneConversion) { + dateValue = ConvertToSiteTimeZone(dateValue); } - return localDate.Value.ToString(format, cultureInfo); + var parts = DateTimeParts.FromDateTime(dateValue); + if (options.EnableCalendarConversion && !(CurrentCalendar is GregorianCalendar)) { + parts = ConvertToSiteCalendar(dateValue); + } + + return _dateFormatter.FormatDateTime(parts, format); } - public virtual string ConvertToLocalDateString(DateTime date, string nullText = null) { - return ConvertToLocalDateString(ToNullable(date), nullText); + public DateTime? ConvertFromLocalizedDateString(string dateString, DateLocalizationOptions options = null) { + return ConvertFromLocalizedString(dateString, null, options); } - public virtual string ConvertToLocalDateString(DateTime? date, string nullText = null) { - return ConvertToLocalString(date, _dateTimeFormatProvider.ShortDateFormat, nullText); + public DateTime? ConvertFromLocalizedTimeString(string timeString, DateLocalizationOptions options = null) { + return ConvertFromLocalizedString(null, timeString, options); } - public virtual string ConvertToLocalTimeString(DateTime date, string nullText = null) { - return ConvertToLocalTimeString(ToNullable(date), nullText); - } + public DateTime? ConvertFromLocalizedString(string dateString, string timeString, DateLocalizationOptions options = null) { + options = options ?? new DateLocalizationOptions(); - public virtual string ConvertToLocalTimeString(DateTime? date, string nullText = null) { - return ConvertToLocalString(date, _dateTimeFormatProvider.ShortTimeFormat, nullText); - } - - public virtual DateTime? ConvertFromLocalString(string dateString) { - if (String.IsNullOrWhiteSpace(dateString)) { + var hasDate = dateString != null && dateString != options.NullText; + var hasTime = timeString != null && timeString != options.NullText; + if (!hasDate && !hasTime) { return null; } - // If the configured current calendar is different from the default calendar - // of the configured current culture we must override the conversion process. - // We do this by using a custom CultureInfo modified to use GregorianCalendar - // (means no calendar conversion will be performed as part of the string - // parsing) and instead perform the calendar conversion ourselves. + var parts = new DateTimeParts( + hasDate ? _dateFormatter.ParseDate(dateString) : DateParts.MinValue, + hasTime ? _dateFormatter.ParseTime(timeString) : TimeParts.MinValue + ); - var cultureInfo = CurrentCulture; - var usingCultureCalendar = CurrentCulture.DateTimeFormat.Calendar.GetType().IsInstanceOfType(CurrentCalendar); - if (!usingCultureCalendar) { - cultureInfo = (CultureInfo)CurrentCulture.Clone(); - cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar(); + var dateValue = parts.ToDateTime(); + if (hasDate && options.EnableCalendarConversion && !(CurrentCalendar is GregorianCalendar)) { + dateValue = ConvertFromSiteCalendar(parts); } - var localDate = DateTime.Parse(dateString, CurrentCulture); - - if (!usingCultureCalendar) { - var calendar = CurrentCalendar; - localDate = calendar.ToDateTime(localDate.Year, localDate.Month, localDate.Day, localDate.Hour, localDate.Minute, localDate.Second, localDate.Millisecond); + if (hasTime && options.EnableTimeZoneConversion) { + dateValue = ConvertFromSiteTimeZone(dateValue); } - return ConvertFromSiteTimeZone(localDate); + return dateValue; } - public virtual DateTime? ConvertFromLocalString(string dateString, string timeString) { - if (String.IsNullOrWhiteSpace(dateString) && String.IsNullOrWhiteSpace(timeString)) { + public DateTime? ConvertFromLocalizedString(string dateTimeString, DateLocalizationOptions options = null) { + options = options ?? new DateLocalizationOptions(); + + if (dateTimeString == null || dateTimeString == options.NullText) { return null; } - // If the configured current calendar is different from the default calendar - // of the configured current culture we must override the conversion process. - // We do this by using a custom CultureInfo modified to use GregorianCalendar - // (means no calendar conversion will be performed as part of the string - // parsing) and instead perform the calendar conversion ourselves. + var parts = _dateFormatter.ParseDateTime(dateTimeString); - var cultureInfo = CurrentCulture; - var usingCultureCalendar = CurrentCulture.DateTimeFormat.Calendar.GetType().IsInstanceOfType(CurrentCalendar); - if (!usingCultureCalendar) { - cultureInfo = (CultureInfo)CurrentCulture.Clone(); - cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar(); + var dateValue = parts.ToDateTime(); + if (options.EnableCalendarConversion && !(CurrentCalendar is GregorianCalendar)) { + dateValue = ConvertFromSiteCalendar(parts); } - var localDate = !String.IsNullOrWhiteSpace(dateString) ? DateTime.Parse(dateString, cultureInfo) : new DateTime(1980, 1, 1); - var localTime = !String.IsNullOrWhiteSpace(timeString) ? DateTime.Parse(timeString, cultureInfo) : new DateTime(1980, 1, 1, 12, 0, 0); - var localDateTime = new DateTime(localDate.Year, localDate.Month, localDate.Day, localTime.Hour, localTime.Minute, localTime.Second); - - if (!usingCultureCalendar) { - var calendar = CurrentCalendar; - localDateTime = calendar.ToDateTime(localDateTime.Year, localDateTime.Month, localDateTime.Day, localDateTime.Hour, localDateTime.Minute, localDateTime.Second, localDateTime.Millisecond); + if (options.EnableTimeZoneConversion) { + dateValue = ConvertFromSiteTimeZone(dateValue); } - return ConvertFromSiteTimeZone(localDateTime); + return dateValue; } protected virtual CultureInfo CurrentCulture { diff --git a/src/Orchard/Localization/Services/IDateLocalizationServices.cs b/src/Orchard/Localization/Services/IDateLocalizationServices.cs index 8d8fc4f8d..e34e339c2 100644 --- a/src/Orchard/Localization/Services/IDateLocalizationServices.cs +++ b/src/Orchard/Localization/Services/IDateLocalizationServices.cs @@ -11,120 +11,172 @@ namespace Orchard.Localization.Services { public interface IDateLocalizationServices : IDependency { /// - /// Converts a non-nullable date from UTC to the Orchard configured time zone. + /// Converts a date from UTC to the Orchard configured time zone. /// - /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. + /// The UTC date to convert. /// - DateTime? ConvertToSiteTimeZone(DateTime dateUtc); + DateTime ConvertToSiteTimeZone(DateTime dateUtc); + + ///// + ///// Converts a nullable date from UTC to the Orchard configured time zone. + ///// + ///// The nullable UTC date to convert. + ///// + //DateTime? ConvertToSiteTimeZone(DateTime? dateUtc); /// - /// Converts a nullable date from UTC to the Orchard configured time zone. + /// Converts a date from the Orchard configured time zone to UTC. /// - /// The nullable UTC date to convert. + /// The local date to convert. /// - DateTime? ConvertToSiteTimeZone(DateTime? dateUtc); + DateTime ConvertFromSiteTimeZone(DateTime dateLocal); - /// - /// Converts a non-nullable date from the Orchard configured time zone to UTC. - /// - /// The non-nullable local date to convert. DateTime.MinValue is translated to null. - /// - DateTime? ConvertFromSiteTimeZone(DateTime dateLocal); - - /// - /// Converts a nullable date from the Orchard configured time zone to UTC. - /// - /// The nullable local date to convert. - /// - DateTime? ConvertFromSiteTimeZone(DateTime? dateLocal); + ///// + ///// Converts a nullable date from the Orchard configured time zone to UTC. + ///// + ///// The nullable local date to convert. + ///// + //DateTime? ConvertFromSiteTimeZone(DateTime? dateLocal); /// /// Converts a date from Gregorian calendar to the Orchard configured calendar. /// /// The Gregorian calendar date to convert. - /// Null if the supplied date parameter was null. Otherwise a DateTimeParts instance representing the converted date. - DateTimeParts? ConvertToSiteCalendar(DateTime? date); + /// A DateTimeParts instance representing the converted date. + DateTimeParts ConvertToSiteCalendar(DateTime date); + + ///// + ///// Converts a nullable date from Gregorian calendar to the Orchard configured calendar. + ///// + ///// The nullable Gregorian calendar date to convert. + ///// Null if the supplied date parameter was null. Otherwise a DateTimeParts instance representing the converted date. + //DateTimeParts? ConvertToSiteCalendar(DateTime? date); /// /// Converts a date from the Orchard configured calendar to Gregorian calendar. /// /// A DateTimeParts instance representing the Orchard configured calendar date to convert. - /// Null if the supplied parts parameter was null. Otherwise the converted Gregorian calendar date. - DateTime? ConvertFromSiteCalendar(DateTimeParts? parts); - + /// A DateTime instance representing the converted date. + DateTime ConvertFromSiteCalendar(DateTimeParts parts); + ///// + ///// Converts a nullable date from the Orchard configured calendar to Gregorian calendar. + ///// + ///// A DateTimeParts instance representing the Orchard configured calendar date to convert. + ///// Null if the supplied parts parameter was null. Otherwise a DateTime instance representing the converted date. + //DateTime? ConvertFromSiteCalendar(DateTimeParts? parts); /// - /// Converts a non-nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it using the default long date and time format string. + /// Converts a non-nullable UTC date in Gregorian calendar to a localized short date string. /// /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. - /// A text to be returned if the supplied UTC date is equal to DateTime.MinValue. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - string ConvertToLocalString(DateTime date, string nullText = null); + string ConvertToLocalizedDateString(DateTime date, DateLocalizationOptions options = null); /// - /// Converts a non-nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it using the specified format string using the Orchard configured culture. - /// - /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. - /// A standard DateTime format string to use for formating the converted date. - /// A text to be returned if the supplied UTC date is equal to DateTime.MinValue. - /// - string ConvertToLocalString(DateTime date, string format, string nullText = null); - - /// - /// Converts a nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it using the specified format string using the Orchard configured culture. + /// Converts a nullable UTC date in Gregorian calendar to a localized short date string. /// /// The nullable UTC date to convert. - /// A standard DateTime format string to use for formating the converted date. - /// A text to be returned if the supplied UTC date has no value. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - string ConvertToLocalString(DateTime? date, string format, string nullText = null); + string ConvertToLocalizedDateString(DateTime? date, DateLocalizationOptions options = null); /// - /// Converts a non-nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it as a date-only string using the Orchard configured culture. + /// Converts a non-nullable UTC date in Gregorian calendar to a localized long time string. /// /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. - /// A text to be returned if the supplied UTC date is equal to DateTime.MinValue. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - string ConvertToLocalDateString(DateTime date, string nullText = null); + string ConvertToLocalizedTimeString(DateTime date, DateLocalizationOptions options = null); /// - /// Converts a nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it as a date-only string using the Orchard configured culture. + /// Converts a nullable UTC date in Gregorian calendar to a localized long time string. /// /// The nullable UTC date to convert. - /// A text to be returned if the supplied UTC date has no value. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - string ConvertToLocalDateString(DateTime? date, string nullText = null); + string ConvertToLocalizedTimeString(DateTime? date, DateLocalizationOptions options = null); /// - /// Converts a non-nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it as a time-only string using the Orchard configured culture. + /// Converts a non-nullable UTC date in Gregorian calendar to a localized short date/time string. /// /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. - /// A text to be returned if the supplied UTC date is equal to DateTime.MinValue. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - string ConvertToLocalTimeString(DateTime date, string nullText = null); + string ConvertToLocalizedString(DateTime date, DateLocalizationOptions options = null); /// - /// Converts a nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone and formats it as a time-only string using the Orchard configured culture. + /// Converts a nullable UTC date in Gregorian calendar to a localized short date/time string. /// /// The nullable UTC date to convert. - /// A text to be returned if the supplied UTC date has no value. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - string ConvertToLocalTimeString(DateTime? date, string nullText = null); + string ConvertToLocalizedString(DateTime? date, DateLocalizationOptions options = null); /// - /// Parses a date and time string using the Orchard configured culture and converts it to Gregorian calendar UTC from the Orchard configured calendar and time zone. + /// Converts a non-nullable UTC date in Gregorian calendar to a localized string with the specified format. /// - /// The local date and time string to parse and convert. + /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. + /// A standard or custom DateTime format string to use when formatting the date. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - DateTime? ConvertFromLocalString(string dateString); + string ConvertToLocalizedString(DateTime date, string format, DateLocalizationOptions options = null); /// - /// Parses separate date and time strings using the Orchard configured culture and converts the resulting combined DateTime to Gregorian calendar UTC from the Orchard configured calendar and time zone. + /// Converts a nullable UTC date in Gregorian calendar to a localized string with the specified format. /// - /// The local date string to parse and convert, or null or an empty string to only convert the time component. - /// The local time string to parse and convert, or null or an empty string to only convert the date component. + /// The nullable UTC date to convert. + /// A standard or custom DateTime format string to use when formatting the date. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. /// - DateTime? ConvertFromLocalString(string dateString, string timeString); + string ConvertToLocalizedString(DateTime? date, string format, DateLocalizationOptions options = null); + + /// + /// Converts a localized date string to a UTC date in Gregorian calendar. + /// + /// The localized date string to convert. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. + /// A DateTime instance where the time component equals that of DateTime.MinValue. + /// + /// If the dateString parameter is null or equal to DateLocalizationOptions.NullText property, this method returns null. + /// + DateTime? ConvertFromLocalizedDateString(string dateString, DateLocalizationOptions options = null); + + /// + /// Converts a localized time string to a UTC date in Gregorian calendar. + /// + /// The localized time string to convert. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. + /// A DateTime instance where the date component equals that of DateTime.MinValue. + /// + /// If the timeString parameter is null or equal to DateLocalizationOptions.NullText property, this method returns null. + /// + DateTime? ConvertFromLocalizedTimeString(string timeString, DateLocalizationOptions options = null); + + /// + /// Converts separate localized date and time strings to a single UTC date in Gregorian calendar. + /// + /// The localized date/time string to convert. + /// The localized time string to convert. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. + /// + /// + /// If the dateString parameter is null or equal to DateLocalizationOptions.NullText property, the returned DateTime instance will have a date component that equals that of DateTime.MinValue. + /// If the timeString parameter is null or equal to DateLocalizationOptions.NullText property, the returned DateTime instance will have a time component that equals that of DateTime.MinValue. + /// If both dateString and timeString parameters are null or equal to DateLocalizationOptions.NullText property, this method returns null. + /// + DateTime? ConvertFromLocalizedString(string dateString, string timeString, DateLocalizationOptions options = null); + + /// + /// Converts a localized date/time string to a UTC date in Gregorian calendar. + /// + /// The localized date/time string to convert. + /// An optional DateLocalizationOptions instance used to control various aspects of the conversion process. + /// + /// + /// If the dateTimeString parameter is null or equal to DateLocalizationOptions.NullText property, this method returns null. + /// + DateTime? ConvertFromLocalizedString(string dateTimeString, DateLocalizationOptions options = null); } } diff --git a/src/Orchard/Localization/Services/IDateLocalizationServices.txt b/src/Orchard/Localization/Services/IDateLocalizationServices.txt index 622e1cf55..1b7341488 100644 --- a/src/Orchard/Localization/Services/IDateLocalizationServices.txt +++ b/src/Orchard/Localization/Services/IDateLocalizationServices.txt @@ -43,10 +43,12 @@ struct DateLocalizationOptions { TODO: * Test for proper handling of fraction (f) format specifier - suspect it does not work properly in current state * Add formatting and parsing of time zone information (add timezone properties to TimeParts structure) - * Rewrite DefaultDateLocalizationServices + * Add support for both standard and custom format strings * Write unit tests for DefaultDateLocalizationServices * Add warning message when saving unsupported combination in settings * Add support for the different Gregorian calendar types + * Go over date conversion logic in audit trail filtering to make sure it's correct + * Go over date conversion logic in DateTokens to make sure it's correct * Improve DateTimeField: - Surface the field mode (date, time or both) - Do not perform time-zone conversion in date-only mode diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 0d8965681..29041ca91 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -261,6 +261,7 @@ +