diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs b/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs index 33cf8c14f..36e05954a 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Fields/Drivers/DateTimeFieldDriver.cs @@ -9,21 +9,21 @@ using Orchard.Fields.Settings; using Orchard.Fields.ViewModels; using Orchard.ContentManagement.Handlers; using Orchard.Localization; +using Orchard.Localization.Services; namespace Orchard.Fields.Drivers { [UsedImplicitly] public class DateTimeFieldDriver : ContentFieldDriver { - public IOrchardServices Services { get; set; } private const string TemplateName = "Fields/DateTime.Edit"; // EditorTemplates/Fields/DateTime.Edit.cshtml - private readonly Lazy _cultureInfo; - public DateTimeFieldDriver(IOrchardServices services) { + public DateTimeFieldDriver(IOrchardServices services, IDateServices dateServices) { Services = services; + DateServices = dateServices; T = NullLocalizer.Instance; - - _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(Services.WorkContext.CurrentCulture)); } + public IOrchardServices Services { get; set; } + public IDateServices DateServices { get; set; } public Localizer T { get; set; } private static string GetPrefix(ContentField field, ContentPart part) { @@ -36,16 +36,15 @@ namespace Orchard.Fields.Drivers { protected override DriverResult Display(ContentPart part, DateTimeField field, string displayType, dynamic shapeHelper) { return ContentShape("Fields_DateTime", // this is just a key in the Shape Table - GetDifferentiator(field, part), + GetDifferentiator(field, part), () => { var settings = field.PartFieldDefinition.Settings.GetModel(); var value = field.DateTime; - var viewModel = new DateTimeFieldViewModel { Name = field.DisplayName, - Date = value != DateTime.MinValue ? TimeZoneInfo.ConvertTimeFromUtc(value, Services.WorkContext.CurrentTimeZone).ToString("d", _cultureInfo.Value) : String.Empty, - Time = value != DateTime.MinValue ? TimeZoneInfo.ConvertTimeFromUtc(value, Services.WorkContext.CurrentTimeZone).ToString("t", _cultureInfo.Value) : String.Empty, + Date = DateServices.ConvertToLocalDateString(value, String.Empty), + Time = DateServices.ConvertToLocalTimeString(value, String.Empty), ShowDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly, ShowTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly, Hint = settings.Hint, @@ -59,14 +58,13 @@ namespace Orchard.Fields.Drivers { } protected override DriverResult Editor(ContentPart part, DateTimeField field, dynamic shapeHelper) { - var settings = field.PartFieldDefinition.Settings.GetModel(); var value = field.DateTime; var viewModel = new DateTimeFieldViewModel { Name = field.DisplayName, - Date = value != DateTime.MinValue ? TimeZoneInfo.ConvertTimeFromUtc(value, Services.WorkContext.CurrentTimeZone).ToString("d", _cultureInfo.Value) : String.Empty, - Time = value != DateTime.MinValue ? TimeZoneInfo.ConvertTimeFromUtc(value, Services.WorkContext.CurrentTimeZone).ToString("t", _cultureInfo.Value) : String.Empty, + Date = DateServices.ConvertToLocalDateString(value, String.Empty), + Time = DateServices.ConvertToLocalTimeString(value, String.Empty), ShowDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly, ShowTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly, Hint = settings.Hint, @@ -74,46 +72,32 @@ namespace Orchard.Fields.Drivers { }; return ContentShape("Fields_DateTime_Edit", GetDifferentiator(field, part), - () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: viewModel, Prefix: GetPrefix(field, part))); + () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: viewModel, Prefix: GetPrefix(field, part))); } protected override DriverResult Editor(ContentPart part, DateTimeField field, IUpdateModel updater, dynamic shapeHelper) { var viewModel = new DateTimeFieldViewModel(); - if(updater.TryUpdateModel(viewModel, GetPrefix(field, part), null, null)) { - DateTime value; + if (updater.TryUpdateModel(viewModel, GetPrefix(field, part), null, null)) { var settings = field.PartFieldDefinition.Settings.GetModel(); - - if (settings.Display == DateTimeFieldDisplays.DateOnly) { - viewModel.Time = new DateTime(1980, 1, 1).ToString("t", _cultureInfo.Value); - } - - if (settings.Display == DateTimeFieldDisplays.TimeOnly) { - viewModel.Date = new DateTime(1980, 1, 1).ToString("d", _cultureInfo.Value); - } - - string parseDateTime = String.Concat(viewModel.Date, " ", viewModel.Time); - - if(settings.Required && (String.IsNullOrWhiteSpace(viewModel.Time) || String.IsNullOrWhiteSpace(viewModel.Date))) { - updater.AddModelError(GetPrefix(field, part), T("{0} is required", field.DisplayName)); - } - - if(!settings.Required - && (settings.Display != DateTimeFieldDisplays.TimeOnly && String.IsNullOrWhiteSpace(viewModel.Date)) - || (settings.Display != DateTimeFieldDisplays.DateOnly && String.IsNullOrWhiteSpace(viewModel.Time)) - ) { - field.DateTime = DateTime.MinValue; - } - else if (DateTime.TryParse(parseDateTime, _cultureInfo.Value, DateTimeStyles.None, out value)) { - field.DateTime = TimeZoneInfo.ConvertTimeToUtc(value, Services.WorkContext.CurrentTimeZone); - } - else { - updater.AddModelError(GetPrefix(field, part), T("{0} is an invalid date and time", field.DisplayName)); - field.DateTime = DateTime.MinValue; + if (settings.Required && (((settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly) && String.IsNullOrWhiteSpace(viewModel.Date)) || ((settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly) && String.IsNullOrWhiteSpace(viewModel.Time)))) { + updater.AddModelError(GetPrefix(field, part), T("{0} is required.", field.DisplayName)); + } else { + try { + var utcDateTime = DateServices.ConvertFromLocalString(viewModel.Date, viewModel.Time); + if (utcDateTime.HasValue) { + field.DateTime = utcDateTime.Value; + } else { + field.DateTime = DateTime.MinValue; + } + } + catch (FormatException) { + updater.AddModelError(GetPrefix(field, part), T("{0} could not be parsed as a valid date and time.", field.DisplayName)); + } } } - + return Editor(part, field, shapeHelper); } @@ -127,10 +111,8 @@ namespace Orchard.Fields.Drivers { protected override void Describe(DescribeMembersContext context) { context - .Member(null, typeof(DateTime), T("Value"), T("The date time value of the field.")) - .Enumerate(() => field => new[] { field.DateTime }) - ; - + .Member(null, typeof(DateTime), T("Value"), T("The date and time value of the field.")) + .Enumerate(() => field => new[] { field.DateTime }); } } } diff --git a/src/Orchard/Localization/Services/DefaultDateServices.cs b/src/Orchard/Localization/Services/DefaultDateServices.cs new file mode 100644 index 000000000..b523f84ec --- /dev/null +++ b/src/Orchard/Localization/Services/DefaultDateServices.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Orchard; + +namespace Orchard.Localization.Services { + + public class DefaultDateServices : IDateServices { + + public DefaultDateServices(IOrchardServices orchardServices) { + _orchardServices = orchardServices; + _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(_orchardServices.WorkContext.CurrentCulture)); + } + + private readonly IOrchardServices _orchardServices; + private readonly Lazy _cultureInfo; + + public DateTime? ConvertToLocal(DateTime date) { + return ConvertToLocal(ToNullable(date)); + } + + public DateTime? ConvertToLocal(DateTime? date) { + if (!date.HasValue) { + return null; + } + return TimeZoneInfo.ConvertTimeFromUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone); + } + + public string ConvertToLocalString(DateTime date, string format, string nullText = null) { + return ConvertToLocalString(ToNullable(date), format, nullText); + } + + public string ConvertToLocalString(DateTime? date, string format, string nullText = null) { + var localDate = ConvertToLocal(date); + if (!localDate.HasValue) { + return nullText; + } + return localDate.Value.ToString(format, _cultureInfo.Value); + } + + public string ConvertToLocalDateString(DateTime date, string nullText = null) { + return ConvertToLocalDateString(ToNullable(date), nullText); + } + + public string ConvertToLocalDateString(DateTime? date, string nullText = null) { + return ConvertToLocalString(date, "d", nullText); + } + + public string ConvertToLocalTimeString(DateTime date, string nullText = null) { + return ConvertToLocalTimeString(ToNullable(date), nullText); + } + + public string ConvertToLocalTimeString(DateTime? date, string nullText = null) { + return ConvertToLocalString(date, "t", nullText); + } + + public DateTime? ConvertFromLocal(DateTime date) { + return ConvertToLocal(ToNullable(date)); + } + + public DateTime? ConvertFromLocal(DateTime? date) { + if (!date.HasValue) { + return null; + } + return TimeZoneInfo.ConvertTimeToUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone); + } + + public DateTime? ConvertFromLocalString(string dateString) { + if (String.IsNullOrWhiteSpace(dateString)) { + return null; + } + var localDate = DateTime.Parse(dateString, _cultureInfo.Value); + return ConvertFromLocal(localDate); + } + + public DateTime? ConvertFromLocalString(string dateString, string timeString) { + if (String.IsNullOrWhiteSpace(dateString) && String.IsNullOrWhiteSpace(timeString)) { + return null; + } + var localDate = !String.IsNullOrWhiteSpace(dateString) ? DateTime.Parse(dateString, _cultureInfo.Value) : new DateTime(1980, 1, 1); + var localTime = !String.IsNullOrWhiteSpace(timeString) ? DateTime.Parse(timeString, _cultureInfo.Value) : new DateTime(1980, 1, 1, 12, 0, 0); + var localDateTime = new DateTime(localDate.Year, localDate.Month, localDate.Day, localTime.Hour, localTime.Minute, localTime.Second); + return ConvertFromLocal(localDateTime); + } + + private DateTime? ToNullable(DateTime date) { + return date == DateTime.MinValue ? new DateTime?() : new DateTime?(date); + } + } +} \ No newline at end of file diff --git a/src/Orchard/Localization/Services/IDateServices.cs b/src/Orchard/Localization/Services/IDateServices.cs new file mode 100644 index 000000000..69e136245 --- /dev/null +++ b/src/Orchard/Localization/Services/IDateServices.cs @@ -0,0 +1,105 @@ +using System; + +namespace Orchard.Localization.Services { + + /// + /// Provides conversion and formatting of dates according to the Orchard configured + /// time zone, culture and calendar (as opposed to the system configured time zone and + /// culture). + /// + public interface IDateServices : IDependency { + + /// + /// Converts a non-nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone. + /// + /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. + /// + DateTime? ConvertToLocal(DateTime date); + + /// + /// Converts a nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone. + /// + /// The nullable UTC date to convert. + /// + DateTime? ConvertToLocal(DateTime? date); + + /// + /// 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. + /// + /// 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. + /// + string ConvertToLocalString(DateTime? date, string format, string nullText = 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. + /// + /// 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. + /// + string ConvertToLocalDateString(DateTime date, string nullText = 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. + /// + /// The nullable UTC date to convert. + /// A text to be returned if the supplied UTC date has no value. + /// + string ConvertToLocalDateString(DateTime? date, string nullText = 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. + /// + /// 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. + /// + string ConvertToLocalTimeString(DateTime date, string nullText = 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. + /// + /// The nullable UTC date to convert. + /// A text to be returned if the supplied UTC date has no value. + /// + string ConvertToLocalTimeString(DateTime? date, string nullText = null); + + /// + /// Converts a non-nullable date to Gregorian calendar UTC from the Orchard configured calendar and time zone. + /// + /// The non-nullable UTC date to convert. DateTime.MinValue is translated to null. + /// + DateTime? ConvertFromLocal(DateTime date); + + /// + /// Converts a nullable date from Gregorian calendar UTC to the Orchard configured calendar and time zone. + /// + /// The nullable UTC date to convert. + /// + DateTime? ConvertFromLocal(DateTime? date); + + /// + /// 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. + /// + /// The local date and time string to parse and convert. + /// + DateTime? ConvertFromLocalString(string dateString); + + /// + /// 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. + /// + /// 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. + /// + DateTime? ConvertFromLocalString(string dateString, string timeString); + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index d89042c0b..d8555d786 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -267,7 +267,9 @@ + +