diff --git a/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs b/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs index aada01272..fd7359c8b 100644 --- a/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs +++ b/src/Orchard.Web/Core/Common/DateEditor/DateEditorDriver.cs @@ -4,18 +4,18 @@ using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.Core.Common.Models; using Orchard.Localization; +using Orchard.Localization.Services; namespace Orchard.Core.Common.DateEditor { public class DateEditorDriver : ContentPartDriver { - private readonly Lazy _cultureInfo; + private readonly IDateServices _dateServices; public DateEditorDriver( - IOrchardServices services) { - T = NullLocalizer.Instance; - Services = services; - - // initializing the culture info lazy initializer - _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(Services.WorkContext.CurrentCulture)); + IOrchardServices services, + IDateServices dateServices) { + _dateServices = dateServices; + T = NullLocalizer.Instance; + Services = services; } public Localizer T { get; set; } @@ -53,40 +53,30 @@ namespace Orchard.Core.Common.DateEditor { thisIsTheInitialVersionRecord && theDatesHaveNotBeenModified; - if (theEditorShouldBeBlank == false) { - // date and time are formatted using the same patterns as DateTimePicker is, preventing other cultures issues - var createdLocal = TimeZoneInfo.ConvertTimeFromUtc(part.CreatedUtc.Value, Services.WorkContext.CurrentTimeZone); - - model.CreatedDate = createdLocal.ToString("d", _cultureInfo.Value); - model.CreatedTime = createdLocal.ToString("t", _cultureInfo.Value); + if (!theEditorShouldBeBlank) { + model.CreatedDate = _dateServices.ConvertToLocalDateString(part.CreatedUtc, ""); + model.CreatedTime = _dateServices.ConvertToLocalTimeString(part.CreatedUtc, ""); } } if (updater != null) { updater.TryUpdateModel(model, Prefix, null, null); - if (!string.IsNullOrWhiteSpace(model.CreatedDate) && !string.IsNullOrWhiteSpace(model.CreatedTime)) { - DateTime createdUtc; - - string parseDateTime = String.Concat(model.CreatedDate, " ", model.CreatedTime); - - // use current culture - if (DateTime.TryParse(parseDateTime, _cultureInfo.Value, DateTimeStyles.None, out createdUtc)) { - - // the date time is entered locally for the configured timezone - part.CreatedUtc = TimeZoneInfo.ConvertTimeToUtc(createdUtc, Services.WorkContext.CurrentTimeZone); - part.VersionCreatedUtc = part.CreatedUtc; + if (!String.IsNullOrWhiteSpace(model.CreatedDate) && !String.IsNullOrWhiteSpace(model.CreatedTime)) { + try { + var utcDateTime = _dateServices.ConvertFromLocalString(model.CreatedDate, model.CreatedTime); + part.CreatedUtc = utcDateTime; + part.VersionCreatedUtc = utcDateTime; } - else { - updater.AddModelError(Prefix, T("{0} is an invalid date and time", parseDateTime)); + catch (FormatException) { + updater.AddModelError(Prefix, T("'{0} {1}' could not be parsed as a valid date and time.", model.CreatedDate, model.CreatedTime)); } } - else if (!string.IsNullOrWhiteSpace(model.CreatedDate) || !string.IsNullOrWhiteSpace(model.CreatedTime)) { - // only one part is specified + else if (!String.IsNullOrWhiteSpace(model.CreatedDate) || !String.IsNullOrWhiteSpace(model.CreatedTime)) { updater.AddModelError(Prefix, T("Both the date and time need to be specified.")); } - // none date/time part is specified => do nothing + // Neither date/time part is specified => do nothing. } return model; diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index 665b0d9cd..6b4225c6d 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -249,7 +249,6 @@ - diff --git a/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs b/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs index ae76fe8ba..b686bd19c 100644 --- a/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs +++ b/src/Orchard.Web/Core/Shapes/DateTimeShapes.cs @@ -1,41 +1,38 @@ using System; +using System.Globalization; using System.Web; using System.Web.Mvc; -using Orchard.Core.Shapes.Localization; using Orchard.DisplayManagement; using Orchard.Localization; +using Orchard.Localization.Services; using Orchard.Mvc.Html; using Orchard.Services; -using System.Globalization; namespace Orchard.Core.Shapes { public class DateTimeShapes : IDependency { private readonly IClock _clock; + private readonly IDateServices _dateServices; private readonly IDateTimeLocalization _dateTimeLocalization; - private readonly IWorkContextAccessor _workContextAccessor; - private readonly Lazy _cultureInfo; public DateTimeShapes( IClock clock, - IDateTimeLocalization dateTimeLocalization, - IWorkContextAccessor workContextAccessor + IDateServices dateServices, + IDateTimeLocalization dateTimeLocalization ) { _clock = clock; + _dateServices = dateServices; _dateTimeLocalization = dateTimeLocalization; - _workContextAccessor = workContextAccessor; T = NullLocalizer.Instance; - - _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(_workContextAccessor.GetContext().CurrentCulture)); } public Localizer T { get; set; } [Shape] - public IHtmlString DateTimeRelative(dynamic Display, DateTime dateTimeUtc) { - var time = _clock.UtcNow - dateTimeUtc; + public IHtmlString DateTimeRelative(dynamic Display, DateTime DateTimeUtc) { + var time = _clock.UtcNow - DateTimeUtc; if (time.TotalDays > 7 || time.TotalDays < -7) - return Display.DateTime(DateTimeUtc: dateTimeUtc, CustomFormat: T("'on' MMM d yyyy 'at' h:mm tt")); + return Display.DateTime(DateTimeUtc: DateTimeUtc, CustomFormat: _dateTimeLocalization.LongDateTimeFormat); if (time.TotalHours > 24) return T.Plural("1 day ago", "{0} days ago", time.Days); @@ -67,24 +64,10 @@ namespace Orchard.Core.Shapes { //using a LocalizedString forces the caller to use a localizable format if (CustomFormat == null || String.IsNullOrWhiteSpace(CustomFormat.Text)) { - return DateTime(DateTimeUtc, _dateTimeLocalization.LongDateTimeFormat); + return new MvcHtmlString(_dateServices.ConvertToLocalString(DateTimeUtc, _dateTimeLocalization.LongDateTimeFormat.Text)); } - return new MvcHtmlString(ConvertToDisplayTime(DateTimeUtc).ToString(CustomFormat.Text, _cultureInfo.Value)); + return new MvcHtmlString(_dateServices.ConvertToLocalString(DateTimeUtc, CustomFormat.Text)); } - - /// - /// Converts a Coordinated Universal Time (UTC) to the time in the current time zone. - /// - /// The Coordinated Universal Time (UTC). - /// The date and time in the selected time zone. Its System.DateTime.Kind property is System.DateTimeKind.Utc if the current zone is System.TimeZoneInfo.Utc; otherwise, its System.DateTime.Kind property is System.DateTimeKind.Unspecified. - private DateTime ConvertToDisplayTime(DateTime dateTimeUtc) { - - // get the time zone for the current request - var timeZone = _workContextAccessor.GetContext().CurrentTimeZone; - - return TimeZoneInfo.ConvertTimeFromUtc(dateTimeUtc, timeZone); - } - } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Shapes/Localization/DateTimeLocalization.cs b/src/Orchard.Web/Core/Shapes/Localization/DateTimeLocalization.cs deleted file mode 100644 index 7db738692..000000000 --- a/src/Orchard.Web/Core/Shapes/Localization/DateTimeLocalization.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using Orchard.Localization; - -namespace Orchard.Core.Shapes.Localization { - - public interface IDateTimeLocalization : IDependency { - LocalizedString MonthNames { get; } - LocalizedString MonthNamesShort { get; } - LocalizedString DayNames { get; } - LocalizedString DayNamesShort { get; } - LocalizedString DayNamesMin { get; } - - LocalizedString ShortDateFormat { get; } - LocalizedString ShortTimeFormat { get; } - LocalizedString LongDateTimeFormat { get; } - - /// - /// The first day of the week, Sun = 0, Mon = 1, ... - /// - int FirstDay { get; } - - /// - /// True if the year select precedes month, false for month then year - /// - bool ShowMonthAfterYear { get; } - - /// - /// Additional text to append to the year in the month headers - /// - string YearSuffix { get; } - } - - public class DateTimeLocalization : IDateTimeLocalization { - - public Localizer T { get; set; } - - public DateTimeLocalization() { - T = NullLocalizer.Instance; - } - - public LocalizedString MonthNames { - get { return T("January, February, March, April, May, June, July, August, September, October, November, December"); } - } - - public LocalizedString MonthNamesShort { - get { return T("Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec"); } - } - - public LocalizedString DayNames { - get { return T("Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday"); } - } - - public LocalizedString DayNamesShort { - get { return T("Sun, Mon, Tue, Wed, Thu, Fri, Sat"); } - } - - public LocalizedString DayNamesMin { - get { return T("Su, Mo, Tu, We, Th, Fr, Sa"); } - } - - public LocalizedString ShortDateFormat { - get { return T("M/d/yyyy"); } - } - - public LocalizedString ShortTimeFormat { - get { return T("h:mm tt"); } - } - - public LocalizedString LongDateTimeFormat { - get { return T("MMM d yyyy h:mm tt"); } - } - - public int FirstDay { - get { - var firstDay = 1; - var t = T("firstDay: 1").Text; - var parts = t.Split(':'); - if(parts.Length == 2) { - Int32.TryParse(parts[1], out firstDay); - } - - return firstDay; - } - } - - public bool ShowMonthAfterYear { - get { - var showMonthAfterYear = false; - var t = T("showMonthAfterYear: false").Text; - var parts = t.Split(':'); - if (parts.Length == 2) { - Boolean.TryParse(parts[1], out showMonthAfterYear); - } - - return showMonthAfterYear; - } - } - - public string YearSuffix { - get { - var yearSuffix = String.Empty; - var t = T("yearSuffix: ").Text; - var parts = t.Split(':'); - if (parts.Length == 2) { - return parts[1].Trim(); - } - - return yearSuffix; - } - } - } -} \ 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 7e490f0c0..2016f9d57 100644 --- a/src/Orchard.Web/Modules/Orchard.ArchiveLater/Drivers/ArchiveLaterPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.ArchiveLater/Drivers/ArchiveLaterPartDriver.cs @@ -8,29 +8,38 @@ using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; using Orchard.Localization; using System.Globalization; +using Orchard.Localization.Services; namespace Orchard.ArchiveLater.Drivers { public class ArchiveLaterPartDriver : ContentPartDriver { private const string TemplateName = "Parts/ArchiveLater"; private readonly IArchiveLaterService _archiveLaterService; - private readonly Lazy _cultureInfo; + private readonly IDateServices _dateServices; public ArchiveLaterPartDriver( IOrchardServices services, - IArchiveLaterService archiveLaterService) { + IArchiveLaterService archiveLaterService, + IDateServices dateServices) { _archiveLaterService = archiveLaterService; + _dateServices = dateServices; T = NullLocalizer.Instance; Services = services; - - // initializing the culture info lazy initializer - _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(Services.WorkContext.CurrentCulture)); - } - public Localizer T { get; set; } - public IOrchardServices Services { get; set; } + public Localizer T { + get; + set; + } + public IOrchardServices Services { + get; + set; + } - protected override string Prefix { get { return "ArchiveLater"; } } + protected override string Prefix { + get { + return "ArchiveLater"; + } + } protected override DriverResult Display(ArchiveLaterPart part, string displayType, dynamic shapeHelper) { return ContentShape("Parts_ArchiveLater_Metadata_SummaryAdmin", @@ -47,8 +56,8 @@ namespace Orchard.ArchiveLater.Drivers { var model = new ArchiveLaterViewModel(part) { ScheduledArchiveUtc = part.ScheduledArchiveUtc.Value, ArchiveLater = part.ScheduledArchiveUtc.Value.HasValue, - ScheduledArchiveDate = part.ScheduledArchiveUtc.Value.HasValue ? localDate.Value.ToString("d", _cultureInfo.Value) : String.Empty, - ScheduledArchiveTime = part.ScheduledArchiveUtc.Value.HasValue ? localDate.Value.ToString("t", _cultureInfo.Value) : String.Empty + ScheduledArchiveDate = _dateServices.ConvertToLocalDateString(part.ScheduledArchiveUtc.Value, ""), + ScheduledArchiveTime = _dateServices.ConvertToLocalTimeString(part.ScheduledArchiveUtc.Value, "") }; return ContentShape("Parts_ArchiveLater_Edit", @@ -58,21 +67,15 @@ namespace Orchard.ArchiveLater.Drivers { protected override DriverResult Editor(ArchiveLaterPart part, IUpdateModel updater, dynamic shapeHelper) { var model = new ArchiveLaterViewModel(part); - if (updater.TryUpdateModel(model, Prefix, null, null) ) { - if ( model.ArchiveLater ) { - DateTime scheduled; - var parseDateTime = String.Concat(model.ScheduledArchiveDate, " ", model.ScheduledArchiveTime); - - // use an english culture as it is the one used by jQuery.datepicker by default - if (DateTime.TryParse(parseDateTime, _cultureInfo.Value, DateTimeStyles.None, out scheduled)) { - // the date time is entered locally for the configured timezone - var timeZone = Services.WorkContext.CurrentTimeZone; - - model.ScheduledArchiveUtc = TimeZoneInfo.ConvertTimeToUtc(scheduled, timeZone); + if (updater.TryUpdateModel(model, Prefix, null, null)) { + if (model.ArchiveLater) { + try { + var utcDateTime = _dateServices.ConvertFromLocalString(model.ScheduledArchiveDate, model.ScheduledArchiveTime); + model.ScheduledArchiveUtc = utcDateTime; _archiveLaterService.ArchiveLater(model.ContentItem, model.ScheduledArchiveUtc.HasValue ? model.ScheduledArchiveUtc.Value : DateTime.MaxValue); } - else { - updater.AddModelError(Prefix, T("{0} is an invalid date and time", parseDateTime)); + catch (FormatException) { + updater.AddModelError(Prefix, T("'{0} {1}' could not be parsed as a valid date and time.", model.ScheduledArchiveDate, model.ScheduledArchiveTime)); } } else { diff --git a/src/Orchard.Web/Modules/Orchard.Fields/Tokens/FieldTokens.cs b/src/Orchard.Web/Modules/Orchard.Fields/Tokens/FieldTokens.cs index 72a0a0bed..8b3a5d33b 100644 --- a/src/Orchard.Web/Modules/Orchard.Fields/Tokens/FieldTokens.cs +++ b/src/Orchard.Web/Modules/Orchard.Fields/Tokens/FieldTokens.cs @@ -2,7 +2,7 @@ using Orchard.Events; using Orchard.Fields.Fields; using Orchard.Localization; -using Orchard.Core.Shapes.Localization; +using Orchard.Localization.Services; using System.Globalization; namespace Orchard.Fields.Tokens { @@ -17,7 +17,6 @@ namespace Orchard.Fields.Tokens { private readonly IWorkContextAccessor _workContextAccessor; private readonly Lazy _cultureInfo; - public FieldTokens( IDateTimeLocalization dateTimeLocalization, IWorkContextAccessor workContextAccessor) { @@ -58,7 +57,7 @@ namespace Orchard.Fields.Tokens { context.For("DateTimeField") .Token("Date", (Func)(d => d.DateTime.ToString(_dateTimeLocalization.ShortDateFormat.Text, _cultureInfo.Value))) .Token("Time", (Func)(d => d.DateTime.ToString(_dateTimeLocalization.ShortTimeFormat.Text, _cultureInfo.Value))) - .Token("DateTime", (Func)(d => d.DateTime.ToString(_dateTimeLocalization.ShortDateFormat.Text + " " + _dateTimeLocalization.ShortTimeFormat.Text, _cultureInfo.Value))) + .Token("DateTime", (Func)(d => d.DateTime.ToString(_dateTimeLocalization.ShortDateTimeFormat.Text, _cultureInfo.Value))) .Chain("DateTime", "Date", (Func)(field => field.DateTime)) ; } diff --git a/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs b/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs index 8afd0edb4..62150c7a7 100644 --- a/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.PublishLater/Drivers/PublishLaterPartDriver.cs @@ -10,6 +10,7 @@ using Orchard.PublishLater.ViewModels; using Orchard.Localization; using System.Globalization; using Orchard.Services; +using Orchard.Localization.Services; namespace Orchard.PublishLater.Drivers { public class PublishLaterPartDriver : ContentPartDriver { @@ -17,29 +18,35 @@ namespace Orchard.PublishLater.Drivers { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IPublishLaterService _publishLaterService; private readonly IClock _clock; + private readonly IDateServices _dateServices; - private readonly Lazy _cultureInfo; - public PublishLaterPartDriver( IOrchardServices services, IHttpContextAccessor httpContextAccessor, IPublishLaterService publishLaterService, - IClock clock) { + IClock clock, + IDateServices dateServices) { _httpContextAccessor = httpContextAccessor; _publishLaterService = publishLaterService; _clock = clock; + _dateServices = dateServices; T = NullLocalizer.Instance; Services = services; - - // initializing the culture info lazy initializer - _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(Services.WorkContext.CurrentCulture)); } - public Localizer T { get; set; } - public IOrchardServices Services { get; set; } + public Localizer T { + get; + set; + } + public IOrchardServices Services { + get; + set; + } protected override string Prefix { - get { return "PublishLater"; } + get { + return "PublishLater"; + } } protected override DriverResult Display(PublishLaterPart part, string displayType, dynamic shapeHelper) { @@ -54,49 +61,39 @@ namespace Orchard.PublishLater.Drivers { } protected override DriverResult Editor(PublishLaterPart part, dynamic shapeHelper) { - // date and time are formatted using the same patterns as DateTimePicker is, preventing other cultures issues - var localDate = new Lazy( () => TimeZoneInfo.ConvertTimeFromUtc(part.ScheduledPublishUtc.Value.Value, Services.WorkContext.CurrentTimeZone)); var model = new PublishLaterViewModel(part) { ScheduledPublishUtc = part.ScheduledPublishUtc.Value, - ScheduledPublishDate = part.ScheduledPublishUtc.Value.HasValue && !part.IsPublished() ? localDate.Value.ToString("d", _cultureInfo.Value) : String.Empty, - ScheduledPublishTime = part.ScheduledPublishUtc.Value.HasValue && !part.IsPublished() ? localDate.Value.ToString("t", _cultureInfo.Value) : String.Empty, + ScheduledPublishDate = !part.IsPublished() ? _dateServices.ConvertToLocalDateString(part.ScheduledPublishUtc.Value, "") : "", + ScheduledPublishTime = !part.IsPublished() ? _dateServices.ConvertToLocalTimeString(part.ScheduledPublishUtc.Value, "") : "" }; return ContentShape("Parts_PublishLater_Edit", () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix)); } + protected override DriverResult Editor(PublishLaterPart part, IUpdateModel updater, dynamic shapeHelper) { var model = new PublishLaterViewModel(part); updater.TryUpdateModel(model, Prefix, null, null); var httpContext = _httpContextAccessor.Current(); - if (httpContext.Request.Form["submit.Save"] == "submit.PublishLater") { - if (!string.IsNullOrWhiteSpace(model.ScheduledPublishDate) && !string.IsNullOrWhiteSpace(model.ScheduledPublishTime)) { - DateTime scheduled; - - string parseDateTime = String.Concat(model.ScheduledPublishDate, " ", model.ScheduledPublishTime); - - // use current culture - if (DateTime.TryParse(parseDateTime, _cultureInfo.Value, DateTimeStyles.None, out scheduled)) { - - // the date time is entered locally for the configured timezone - var timeZone = Services.WorkContext.CurrentTimeZone; - - model.ScheduledPublishUtc = part.ScheduledPublishUtc.Value = TimeZoneInfo.ConvertTimeToUtc(scheduled, timeZone); + if (!String.IsNullOrWhiteSpace(model.ScheduledPublishDate) && !String.IsNullOrWhiteSpace(model.ScheduledPublishTime)) { + try { + var utcDateTime = _dateServices.ConvertFromLocalString(model.ScheduledPublishDate, model.ScheduledPublishTime); + model.ScheduledPublishUtc = part.ScheduledPublishUtc.Value = utcDateTime; if (model.ScheduledPublishUtc < _clock.UtcNow) { - updater.AddModelError("ScheduledPublishUtcDate", T("You cannot schedule a publishing date in the past")); + updater.AddModelError("ScheduledPublishUtcDate", T("You cannot schedule a publishing date in the past.")); } else { _publishLaterService.Publish(model.ContentItem, model.ScheduledPublishUtc.Value); } } - else { - updater.AddModelError(Prefix, T("{0} is an invalid date and time", parseDateTime)); + catch (FormatException) { + updater.AddModelError(Prefix, T("'{0} {1}' could not be parsed as a valid date and time.", model.ScheduledPublishDate, model.ScheduledPublishTime)); } } - else if (!string.IsNullOrWhiteSpace(model.ScheduledPublishDate) || !string.IsNullOrWhiteSpace(model.ScheduledPublishTime)) { + else if (!String.IsNullOrWhiteSpace(model.ScheduledPublishDate) || !String.IsNullOrWhiteSpace(model.ScheduledPublishTime)) { updater.AddModelError(Prefix, T("Both the date and time need to be specified for when this is to be published. If you don't want to schedule publishing then click Save or Publish Now.")); } } diff --git a/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs b/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs index 78155f6d0..a2c92c4a2 100644 --- a/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs +++ b/src/Orchard.Web/Modules/Orchard.Tokens/Providers/DateTokens.cs @@ -1,9 +1,9 @@ using System; -using Orchard.Core.Shapes.Localization; +using System.Globalization; using Orchard.Localization; +using Orchard.Localization.Services; using Orchard.Mvc.Html; using Orchard.Services; -using System.Globalization; namespace Orchard.Tokens.Providers { public class DateTokens : ITokenProvider { @@ -11,18 +11,18 @@ namespace Orchard.Tokens.Providers { private readonly IDateTimeLocalization _dateTimeLocalization; private readonly IWorkContextAccessor _workContextAccessor; private readonly Lazy _cultureInfo; - private readonly Lazy _timeZone; + private readonly IDateServices _dateServices; public DateTokens( IClock clock, IDateTimeLocalization dateTimeLocalization, - IWorkContextAccessor workContextAccessor) { + IWorkContextAccessor workContextAccessor, + IDateServices dateServices) { _clock = clock; _dateTimeLocalization = dateTimeLocalization; _workContextAccessor = workContextAccessor; - _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(_workContextAccessor.GetContext().CurrentCulture)); - _timeZone = new Lazy(() => _workContextAccessor.GetContext().CurrentTimeZone); + _dateServices = dateServices; T = NullLocalizer.Instance; } @@ -32,7 +32,7 @@ namespace Orchard.Tokens.Providers { public void Describe(DescribeContext context) { context.For("Date", T("Date/time"), T("Current date/time tokens")) .Token("Since", T("Since"), T("Relative to the current date/time."), "Date") - .Token("Local", T("Local"), T("Based on the configured time zone."), "Date") + .Token("Local", T("Local"), T("Based on the configured time zone and calendar."), "Date") .Token("ShortDate", T("Short Date"), T("Short date format.")) .Token("ShortTime", T("Short Time"), T("Short time format.")) .Token("Long", T("Long Date and Time"), T("Long date and time format.")) @@ -45,8 +45,8 @@ namespace Orchard.Tokens.Providers { .Token("Since", DateTimeRelative) .Chain("Since", "Date", DateTimeRelative) // {Date.Local} - .Token("Local", d => TimeZoneInfo.ConvertTimeFromUtc(d, _timeZone.Value)) - .Chain("Local", "Date", d => TimeZoneInfo.ConvertTimeFromUtc(d, _timeZone.Value)) + .Token("Local", d => _dateServices.ConvertToLocal(d)) + .Chain("Local", "Date", d => _dateServices.ConvertToLocal(d)) // {Date.ShortDate} .Token("ShortDate", d => d.ToString(_dateTimeLocalization.ShortDateFormat.Text, _cultureInfo.Value)) // {Date.ShortTime} diff --git a/src/Orchard.Web/Modules/Orchard.Tokens/Tests/DateTokenTests.cs b/src/Orchard.Web/Modules/Orchard.Tokens/Tests/DateTokenTests.cs index fde7de969..a0eb99382 100644 --- a/src/Orchard.Web/Modules/Orchard.Tokens/Tests/DateTokenTests.cs +++ b/src/Orchard.Web/Modules/Orchard.Tokens/Tests/DateTokenTests.cs @@ -2,7 +2,7 @@ using System.Globalization; using Autofac; using NUnit.Framework; -using Orchard.Core.Shapes.Localization; +using Orchard.Localization.Services; using Orchard.Services; using Orchard.Tokens.Implementation; using Orchard.Tokens.Providers; @@ -22,7 +22,7 @@ namespace Orchard.Tokens.Tests { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); _container = builder.Build(); _tokenizer = _container.Resolve(); diff --git a/src/Orchard/Localization/Services/DefaultDateServices.cs b/src/Orchard/Localization/Services/DefaultDateServices.cs index b523f84ec..9907eafbe 100644 --- a/src/Orchard/Localization/Services/DefaultDateServices.cs +++ b/src/Orchard/Localization/Services/DefaultDateServices.cs @@ -8,30 +8,39 @@ namespace Orchard.Localization.Services { public class DefaultDateServices : IDateServices { - public DefaultDateServices(IOrchardServices orchardServices) { + private readonly IOrchardServices _orchardServices; + private readonly IDateTimeLocalization _dateTimeLocalization; + private readonly Lazy _cultureInfo; + + public DefaultDateServices( + IOrchardServices orchardServices, + IDateTimeLocalization dateTimeLocalization) { + _orchardServices = orchardServices; + _dateTimeLocalization = dateTimeLocalization; _cultureInfo = new Lazy(() => CultureInfo.GetCultureInfo(_orchardServices.WorkContext.CurrentCulture)); } - private readonly IOrchardServices _orchardServices; - private readonly Lazy _cultureInfo; - - public DateTime? ConvertToLocal(DateTime date) { + public virtual DateTime? ConvertToLocal(DateTime date) { return ConvertToLocal(ToNullable(date)); } - public DateTime? ConvertToLocal(DateTime? date) { + public virtual 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) { + public virtual string ConvertToLocalString(DateTime date, string nullText = null) { + return ConvertToLocalString(ToNullable(date), _dateTimeLocalization.LongDateTimeFormat.Text, nullText); + } + + public virtual 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) { + public virtual string ConvertToLocalString(DateTime? date, string format, string nullText = null) { var localDate = ConvertToLocal(date); if (!localDate.HasValue) { return nullText; @@ -39,34 +48,34 @@ namespace Orchard.Localization.Services { return localDate.Value.ToString(format, _cultureInfo.Value); } - public string ConvertToLocalDateString(DateTime date, string nullText = null) { + public virtual 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 virtual string ConvertToLocalDateString(DateTime? date, string nullText = null) { + return ConvertToLocalString(date, _dateTimeLocalization.ShortDateFormat.Text, nullText); } - public string ConvertToLocalTimeString(DateTime date, string nullText = null) { + public virtual 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 virtual string ConvertToLocalTimeString(DateTime? date, string nullText = null) { + return ConvertToLocalString(date, _dateTimeLocalization.ShortTimeFormat.Text, nullText); } - public DateTime? ConvertFromLocal(DateTime date) { + public virtual DateTime? ConvertFromLocal(DateTime date) { return ConvertToLocal(ToNullable(date)); } - public DateTime? ConvertFromLocal(DateTime? date) { + public virtual DateTime? ConvertFromLocal(DateTime? date) { if (!date.HasValue) { return null; } return TimeZoneInfo.ConvertTimeToUtc(date.Value, _orchardServices.WorkContext.CurrentTimeZone); } - public DateTime? ConvertFromLocalString(string dateString) { + public virtual DateTime? ConvertFromLocalString(string dateString) { if (String.IsNullOrWhiteSpace(dateString)) { return null; } @@ -74,7 +83,7 @@ namespace Orchard.Localization.Services { return ConvertFromLocal(localDate); } - public DateTime? ConvertFromLocalString(string dateString, string timeString) { + public virtual DateTime? ConvertFromLocalString(string dateString, string timeString) { if (String.IsNullOrWhiteSpace(dateString) && String.IsNullOrWhiteSpace(timeString)) { return null; } @@ -84,7 +93,7 @@ namespace Orchard.Localization.Services { return ConvertFromLocal(localDateTime); } - private DateTime? ToNullable(DateTime date) { + protected virtual DateTime? ToNullable(DateTime date) { return date == DateTime.MinValue ? new DateTime?() : new DateTime?(date); } } diff --git a/src/Orchard/Localization/Services/IDateServices.cs b/src/Orchard/Localization/Services/IDateServices.cs index 69e136245..d22147fbf 100644 --- a/src/Orchard/Localization/Services/IDateServices.cs +++ b/src/Orchard/Localization/Services/IDateServices.cs @@ -23,6 +23,14 @@ namespace Orchard.Localization.Services { /// 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 default long date and time format 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. + /// + string ConvertToLocalString(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 using the specified format string using the Orchard configured culture. /// diff --git a/src/Orchard/Localization/Services/IDateTimeLocalization.cs b/src/Orchard/Localization/Services/IDateTimeLocalization.cs new file mode 100644 index 000000000..624541c6f --- /dev/null +++ b/src/Orchard/Localization/Services/IDateTimeLocalization.cs @@ -0,0 +1,97 @@ +using System; +using Orchard.Localization; + +namespace Orchard.Localization.Services { + + /// + /// Provides a set of localizable strings which in turn control localization of dates and + /// times in Orchard. These strings can be changed for other cultures using the normal + /// string localization process. + /// + public interface IDateTimeLocalization : IDependency { + + /// + /// Returns a comma-separated list of month names. + /// + LocalizedString MonthNames { + get; + } + + /// + /// Returns a comma-separated list of abbreviated month names. + /// + LocalizedString MonthNamesShort { + get; + } + + /// + /// Returns a comma-separated list of weekday names. + /// + LocalizedString DayNames { + get; + } + + /// + /// Returns a comma-separated list of abbreviated weekday names. + /// + LocalizedString DayNamesShort { + get; + } + + /// + /// Returns a comma-separated list of maximally abbreviated weekday names. + /// + LocalizedString DayNamesMin { + get; + } + + /// + /// Returns a standard or custom DateTime format string used to format dates for short date display. + /// + LocalizedString ShortDateFormat { + get; + } + + /// + /// Returns a standard or custom DateTime format string used to format dates for short time display. + /// + LocalizedString ShortTimeFormat { + get; + } + + /// + /// Returns a standard or custom DateTime format string used to format dates for general (short) date and time display. + /// + LocalizedString ShortDateTimeFormat { + get; + } + + /// + /// Returns a standard or custom DateTime format string used to format dates for full date and time display. + /// + LocalizedString LongDateTimeFormat { + get; + } + + /// + /// Returns an integer representing the first day of the week, where 0 is Sunday, 1 is Monday etc. + /// + int FirstDay { + get; + } + + /// + /// Returns true if the year select precedes month, false for month then year. + /// + bool ShowMonthAfterYear { + get; + } + + /// + /// Returns an additional text to append to the year in the month headers. + /// + string YearSuffix { + get; + } + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index d8555d786..90dab6d4e 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -267,9 +267,11 @@ + +