mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Refactored the IDateFormatter abstraction and its implementation.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Framework.Localization.Models;
|
||||
using Orchard.Framework.Localization.Services;
|
||||
using Orchard.Localization.Services;
|
||||
|
||||
namespace Orchard.Framework.Tests.Localization {
|
||||
|
||||
@@ -10,59 +13,136 @@ namespace Orchard.Framework.Tests.Localization {
|
||||
public class DefaultDateFormatterTests {
|
||||
|
||||
[Test]
|
||||
[Description("Correct Swedish date is parsed correctly.")]
|
||||
[Description("Correct en-US date is parsed correctly.")]
|
||||
public void ParseTest01() {
|
||||
IDateFormatter target = new DefaultDateFormatter();
|
||||
var cultureInfo = CultureInfo.GetCultureInfo("sv-SE");
|
||||
var dateTimeString = "2014-05-31 10:00:00";
|
||||
|
||||
var result = target.ParseDateTime(dateTimeString, cultureInfo);
|
||||
var container = InitializeContainer("en-US");
|
||||
var culture = CultureInfo.GetCultureInfo("en-US");
|
||||
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||
var target = container.Resolve<IDateFormatter>();
|
||||
|
||||
var value = new DateTime(2014, 5, 31, 10, 0, 0).ToString(formats.ShortDateTimeFormat, culture);
|
||||
var result = target.ParseDateTime(value);
|
||||
var expected = new DateTimeParts(2014, 5, 31, 10, 0, 0, 0);
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description("Correct US English date is parsed correctly.")]
|
||||
public void ParseTest02() {
|
||||
IDateFormatter target = new DefaultDateFormatter();
|
||||
var cultureInfo = CultureInfo.GetCultureInfo("en-US");
|
||||
|
||||
var dateString = "5/31/2014 10:00:00 AM";
|
||||
var result = target.ParseDateTime(dateString, cultureInfo);
|
||||
|
||||
var expected = new DateTimeParts(2014, 5, 31, 10, 0, 0, 0);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description("Incorrect US English date yields a FormatException.")]
|
||||
[Description("Incorrect en-US date yields an exception.")]
|
||||
[ExpectedException(typeof(FormatException))]
|
||||
public void ParseTest03() {
|
||||
IDateFormatter target = new DefaultDateFormatter();
|
||||
var cultureInfo = CultureInfo.GetCultureInfo("en-US");
|
||||
var dateString = "blablabla";
|
||||
public void ParseTest02() {
|
||||
var container = InitializeContainer("en-US");
|
||||
var target = container.Resolve<IDateFormatter>();
|
||||
|
||||
target.ParseDateTime(dateString, cultureInfo);
|
||||
var value = "BlaBlaBla";
|
||||
var result = target.ParseDateTime(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description("Loop through all cultures. Test Parse method by all possible DateTimeFormats.")]
|
||||
[Description("Correct sv-SE date is parsed correctly.")]
|
||||
public void ParseTest03() {
|
||||
var container = InitializeContainer("sv-SE");
|
||||
var culture = CultureInfo.GetCultureInfo("sv-SE");
|
||||
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||
var target = container.Resolve<IDateFormatter>();
|
||||
|
||||
var value = new DateTime(2014, 5, 31, 10, 0, 0).ToString(formats.ShortDateTimeFormat, culture);
|
||||
var result = target.ParseDateTime(value);
|
||||
var expected = new DateTimeParts(2014, 5, 31, 10, 0, 0, 0);
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description("Incorrect sv-SE date yields an exception.")]
|
||||
[ExpectedException(typeof(FormatException))]
|
||||
public void ParseTest04() {
|
||||
IDateFormatter target = new DefaultDateFormatter();
|
||||
var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||
foreach (CultureInfo cultureInfo in cultures) {
|
||||
// Due to a bug in .NET 4.5 in combination with updated Upper Sorbian culture in Windows 8.
|
||||
if (System.Environment.OSVersion.Version.ToString().CompareTo("6.2.0.0") >= 0 && cultureInfo.Name.StartsWith("hsb")) {
|
||||
continue;
|
||||
}
|
||||
DateTime dateTime = new DateTime(2014, 12, 31, 10, 20, 40, 567);
|
||||
cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar();
|
||||
var dateString = dateTime.ToString("G", cultureInfo);
|
||||
var result = target.ParseDateTime(dateString, cultureInfo);
|
||||
var millisecond = DateTime.Parse(dateString, cultureInfo.DateTimeFormat).Millisecond;
|
||||
var expected = new DateTimeParts(2014, 12, 31, 10, 20, 40, millisecond);
|
||||
Assert.AreEqual(expected, result);
|
||||
var container = InitializeContainer("sv-SE");
|
||||
var target = container.Resolve<IDateFormatter>();
|
||||
|
||||
var value = "BlaBlaBla";
|
||||
var result = target.ParseDateTime(value);
|
||||
}
|
||||
|
||||
//[Test]
|
||||
//[Description("Loop through all cultures. Test Parse method by all possible DateTimeFormats.")]
|
||||
//public void ParseTest04() {
|
||||
// IDateFormatter target = new DefaultDateFormatter();
|
||||
// var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||
// foreach (CultureInfo cultureInfo in cultures) {
|
||||
// // Due to a bug in .NET 4.5 in combination with updated Upper Sorbian culture in Windows 8.
|
||||
// if (System.Environment.OSVersion.Version.ToString().CompareTo("6.2.0.0") >= 0 && cultureInfo.Name.StartsWith("hsb")) {
|
||||
// continue;
|
||||
// }
|
||||
// DateTime dateTime = new DateTime(2014, 12, 31, 10, 20, 40, 567);
|
||||
// cultureInfo.DateTimeFormat.Calendar = new GregorianCalendar();
|
||||
// var dateString = dateTime.ToString("G", cultureInfo);
|
||||
// var result = target.ParseDateTime(dateString, cultureInfo);
|
||||
// var millisecond = DateTime.Parse(dateString, cultureInfo.DateTimeFormat).Millisecond;
|
||||
// var expected = new DateTimeParts(2014, 12, 31, 10, 20, 40, millisecond);
|
||||
// Assert.AreEqual(expected, result);
|
||||
// }
|
||||
//}
|
||||
|
||||
private IContainer InitializeContainer(string cultureName) {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterInstance<WorkContext>(new StubWorkContext(cultureName));
|
||||
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
|
||||
builder.RegisterType<CultureDateTimeFormatProvider>().As<IDateTimeFormatProvider>();
|
||||
builder.RegisterType<DefaultDateFormatter>().As<IDateFormatter>();
|
||||
builder.RegisterInstance(new Mock<ICalendarManager>().Object);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private class StubWorkContext : WorkContext {
|
||||
|
||||
private string _cultureName;
|
||||
|
||||
public StubWorkContext(string cultureName) {
|
||||
_cultureName = cultureName;
|
||||
}
|
||||
|
||||
public override T Resolve<T>() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool TryResolve<T>(out T service) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override T GetState<T>(string name) {
|
||||
if (name == "CurrentCulture") return (T)((object)_cultureName);
|
||||
if (name == "CurrentCalendar") return (T)default(object);
|
||||
throw new NotImplementedException(String.Format("Property '{0}' is not implemented.", name));
|
||||
}
|
||||
|
||||
public override void SetState<T>(string name, T value) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class StubWorkContextAccessor : IWorkContextAccessor {
|
||||
|
||||
private WorkContext _workContext;
|
||||
|
||||
public StubWorkContextAccessor(WorkContext workContext) {
|
||||
_workContext = workContext;
|
||||
}
|
||||
|
||||
public WorkContext GetContext(System.Web.HttpContextBase httpContext) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IWorkContextScope CreateWorkContextScope(System.Web.HttpContextBase httpContext) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public WorkContext GetContext() {
|
||||
return _workContext;
|
||||
}
|
||||
|
||||
public IWorkContextScope CreateWorkContextScope() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,55 +13,57 @@ namespace Orchard.Localization.Services {
|
||||
/// </summary>
|
||||
public class CultureDateTimeFormatProvider : IDateTimeFormatProvider {
|
||||
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
// TODO: This implementation should probably also depend on the current calendar, because DateTimeFormatInfo returns different strings depending on the calendar.
|
||||
|
||||
public CultureDateTimeFormatProvider(IOrchardServices orchardServices) {
|
||||
_orchardServices = orchardServices;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public CultureDateTimeFormatProvider(IWorkContextAccessor workContextAccessor) {
|
||||
_workContextAccessor = workContextAccessor;
|
||||
}
|
||||
|
||||
public IEnumerable<string> MonthNames {
|
||||
public virtual IEnumerable<string> MonthNames {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.MonthNames;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> MonthNamesShort {
|
||||
public virtual IEnumerable<string> MonthNamesShort {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.AbbreviatedMonthNames;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> DayNames {
|
||||
public virtual IEnumerable<string> DayNames {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.DayNames;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> DayNamesShort {
|
||||
public virtual IEnumerable<string> DayNamesShort {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.AbbreviatedDayNames;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> DayNamesMin {
|
||||
public virtual IEnumerable<string> DayNamesMin {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.ShortestDayNames;
|
||||
}
|
||||
}
|
||||
|
||||
public string ShortDateFormat {
|
||||
public virtual string ShortDateFormat {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.ShortDatePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public string ShortTimeFormat {
|
||||
public virtual string ShortTimeFormat {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.ShortTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public string ShortDateTimeFormat {
|
||||
public virtual string ShortDateTimeFormat {
|
||||
get {
|
||||
// From empirical testing I am fairly certain this invariably evaluates to
|
||||
// the pattern actually used when printing using the 'g' (i.e. general date/time
|
||||
@@ -70,25 +72,25 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public string LongDateFormat {
|
||||
public virtual string LongDateFormat {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.LongDatePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public string LongTimeFormat {
|
||||
public virtual string LongTimeFormat {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.LongTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public string LongDateTimeFormat {
|
||||
public virtual string LongDateTimeFormat {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.FullDateTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllDateFormats {
|
||||
public virtual IEnumerable<string> AllDateFormats {
|
||||
get {
|
||||
var patterns = new List<string>();
|
||||
patterns.AddRange(CurrentCulture.DateTimeFormat.GetAllDateTimePatterns('d'));
|
||||
@@ -100,7 +102,7 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllTimeFormats {
|
||||
public virtual IEnumerable<string> AllTimeFormats {
|
||||
get {
|
||||
var patterns = new List<string>();
|
||||
patterns.AddRange(CurrentCulture.DateTimeFormat.GetAllDateTimePatterns('t'));
|
||||
@@ -109,7 +111,7 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllDateTimeFormats {
|
||||
public virtual IEnumerable<string> AllDateTimeFormats {
|
||||
get {
|
||||
var patterns = new List<string>();
|
||||
patterns.AddRange(CurrentCulture.DateTimeFormat.GetAllDateTimePatterns('f'));
|
||||
@@ -125,13 +127,13 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public int FirstDay {
|
||||
public virtual int FirstDay {
|
||||
get {
|
||||
return Convert.ToInt32(CurrentCulture.DateTimeFormat.FirstDayOfWeek);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Use24HourTime {
|
||||
public virtual bool Use24HourTime {
|
||||
get {
|
||||
if (ShortTimeFormat.Contains("H")) // Capital H is the format specifier for the hour using a 24-hour clock.
|
||||
return true;
|
||||
@@ -139,33 +141,34 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
}
|
||||
|
||||
public string DateSeparator {
|
||||
public virtual string DateSeparator {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.DateSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
public string TimeSeparator {
|
||||
public virtual string TimeSeparator {
|
||||
get {
|
||||
return CurrentCulture.DateTimeFormat.TimeSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
public string AmPmPrefix {
|
||||
public virtual string AmPmPrefix {
|
||||
get {
|
||||
return " "; // No way to get this from CultureInfo unfortunately, so assume a single space.
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AmPmDesignators {
|
||||
public virtual IEnumerable<string> AmPmDesignators {
|
||||
get {
|
||||
return new string[] { CurrentCulture.DateTimeFormat.AMDesignator, CurrentCulture.DateTimeFormat.PMDesignator };
|
||||
}
|
||||
}
|
||||
|
||||
private CultureInfo CurrentCulture {
|
||||
protected virtual CultureInfo CurrentCulture {
|
||||
get {
|
||||
return CultureInfo.GetCultureInfo(_orchardServices.WorkContext.CurrentCulture);
|
||||
var workContext = _workContextAccessor.GetContext();
|
||||
return CultureInfo.GetCultureInfo(workContext.CurrentCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,101 +4,134 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Orchard.Framework.Localization.Models;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Framework.Localization.Services {
|
||||
|
||||
public class DefaultDateFormatter : IDateFormatter {
|
||||
|
||||
DateTimeParts IDateFormatter.ParseDateTime(string dateTimeString, CultureInfo culture) {
|
||||
DateParts date = ((IDateFormatter)this).ParseDate(dateTimeString, culture);
|
||||
TimeParts time = ((IDateFormatter)this).ParseTime(dateTimeString, culture);
|
||||
return new DateTimeParts(date, time);
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IDateTimeFormatProvider _dateTimeFormatProvider;
|
||||
private readonly ICalendarManager _calendarManager;
|
||||
|
||||
public DefaultDateFormatter(
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IDateTimeFormatProvider dateTimeFormatProvider,
|
||||
ICalendarManager calendarManager) {
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_dateTimeFormatProvider = dateTimeFormatProvider;
|
||||
_calendarManager = calendarManager;
|
||||
}
|
||||
|
||||
DateParts IDateFormatter.ParseDate(string dateString, CultureInfo culture) {
|
||||
var dateFormatString = Regex.Replace(culture.DateTimeFormat.ShortDatePattern, @"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\", m => String.Format(@"\{0}", m.Value));
|
||||
dateFormatString = Regex.Replace(dateFormatString, @"(?<!\\)'(.*?)((?<!\\)')", m => String.Format("(.{{{0}}})", m.Value.Replace("\\", "").Length - 2));
|
||||
public virtual DateTimeParts ParseDateTime(string dateTimeString) {
|
||||
var replacements = GetDateParseReplacements().Union(GetTimeParseReplacements()).ToDictionary(item => item.Key, item => item.Value);
|
||||
var dateTimePattern = ConvertFormatStringToRegExPattern(_dateTimeFormatProvider.ShortDateTimeFormat, replacements);
|
||||
|
||||
var dateFormat = ReplaceAll(dateFormatString, GetDateParseReplacements(culture));
|
||||
Match m = Regex.Match(dateTimeString, dateTimePattern, RegexOptions.IgnoreCase);
|
||||
if (!m.Success) {
|
||||
throw new FormatException("The string was not recognized as a valid date and time.");
|
||||
}
|
||||
|
||||
if (!Regex.IsMatch(dateString, dateFormat, RegexOptions.IgnoreCase))
|
||||
throw new FormatException("Invalid date format.");
|
||||
return new DateTimeParts(ExtractDateParts(m), ExtractTimeParts(m));
|
||||
}
|
||||
|
||||
Match dateMatch = Regex.Match(dateString, dateFormat, RegexOptions.IgnoreCase);
|
||||
public virtual DateParts ParseDate(string dateString) {
|
||||
var replacements = GetDateParseReplacements();
|
||||
var datePattern = ConvertFormatStringToRegExPattern(_dateTimeFormatProvider.ShortDateFormat, replacements);
|
||||
|
||||
Match m = Regex.Match(dateString, datePattern, RegexOptions.IgnoreCase);
|
||||
if (!m.Success) {
|
||||
throw new FormatException("The string was not recognized as a valid date.");
|
||||
}
|
||||
|
||||
return ExtractDateParts(m);
|
||||
}
|
||||
|
||||
public virtual TimeParts ParseTime(string timeString) {
|
||||
var replacements = GetTimeParseReplacements();
|
||||
var timePattern = ConvertFormatStringToRegExPattern(_dateTimeFormatProvider.LongTimeFormat, replacements);
|
||||
|
||||
Match m = Regex.Match(timeString, timePattern, RegexOptions.IgnoreCase);
|
||||
if (!m.Success) {
|
||||
throw new FormatException("The string was not recognized as a valid time.");
|
||||
}
|
||||
|
||||
return ExtractTimeParts(m);
|
||||
}
|
||||
|
||||
public virtual string FormatDateTime(DateTimeParts parts) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string FormatDateTime(DateTimeParts parts, string format) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string FormatDate(DateParts parts) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string FormatDate(DateParts parts, string format) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string FormatTime(TimeParts parts) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string FormatTime(TimeParts parts, string format) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected virtual DateParts ExtractDateParts(Match m) {
|
||||
int year = 0,
|
||||
month = 0,
|
||||
day = 0;
|
||||
|
||||
if (dateMatch.Groups["year"].Success) {
|
||||
int.TryParse(dateMatch.Groups["year"].Value, out year);
|
||||
year = culture.DateTimeFormat.Calendar.ToFourDigitYear(year);
|
||||
}
|
||||
if (dateMatch.Groups["month"].Success) {
|
||||
int.TryParse(dateMatch.Groups["month"].Value, out month);
|
||||
}
|
||||
if (dateMatch.Groups["day"].Success) {
|
||||
int.TryParse(dateMatch.Groups["day"].Value, out day);
|
||||
}
|
||||
year = CurrentCalendar.ToFourDigitYear(Int32.Parse(m.Groups["year"].Value));
|
||||
month = Int32.Parse(m.Groups["month"].Value);
|
||||
day = Int32.Parse(m.Groups["day"].Value);
|
||||
|
||||
// TODO: Also extract month names, not just numbers.
|
||||
|
||||
return new DateParts(year, month, day);
|
||||
}
|
||||
|
||||
TimeParts IDateFormatter.ParseTime(string timeString, CultureInfo culture) {
|
||||
var timeFormatString = Regex.Replace(culture.DateTimeFormat.LongTimePattern, @"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\", m => String.Format(@"\{0}", m.Value));
|
||||
timeFormatString = Regex.Replace(timeFormatString, @"(?<!\\)'(.*?)((?<!\\)')", m => String.Format("(.{{{0}}})", m.Value.Replace("\\", "").Length - 2));
|
||||
|
||||
var timeFormat = ReplaceAll(timeFormatString, GetTimeParseReplacements(culture));
|
||||
|
||||
if (!Regex.IsMatch(timeString, timeFormat, RegexOptions.IgnoreCase))
|
||||
throw new FormatException("Invalid time format.");
|
||||
|
||||
Match timeMatch = Regex.Match(timeString, timeFormat, RegexOptions.IgnoreCase);
|
||||
|
||||
protected virtual TimeParts ExtractTimeParts(Match m) {
|
||||
int hour = 0,
|
||||
minute = 0,
|
||||
second = 0,
|
||||
millisecond = 0;
|
||||
|
||||
if (timeMatch.Groups["hour"].Success) {
|
||||
int.TryParse(timeMatch.Groups["hour"].Value, out hour);
|
||||
hour = Int32.Parse(m.Groups["hour"].Value);
|
||||
minute = Int32.Parse(m.Groups["minute"].Value);
|
||||
if (m.Groups["second"].Success) {
|
||||
second = Int32.Parse(m.Groups["second"].Value);
|
||||
}
|
||||
if (timeMatch.Groups["minute"].Success) {
|
||||
int.TryParse(timeMatch.Groups["minute"].Value, out minute);
|
||||
}
|
||||
if (timeMatch.Groups["second"].Success) {
|
||||
int.TryParse(timeMatch.Groups["second"].Value, out second);
|
||||
}
|
||||
if (timeMatch.Groups["millisecond"].Success) {
|
||||
int.TryParse(timeMatch.Groups["millisecond"].Value, out millisecond);
|
||||
if (m.Groups["millisecond"].Success) {
|
||||
second = Int32.Parse(m.Groups["millisecond"].Value);
|
||||
}
|
||||
|
||||
// TODO: We must also handle 12-hour time with AM/PM designator.
|
||||
|
||||
return new TimeParts(hour, minute, second, millisecond);
|
||||
}
|
||||
|
||||
string IDateFormatter.FormatDateTime(DateTimeParts parts, CultureInfo culture) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
string IDateFormatter.FormatDate(DateParts parts, CultureInfo culture) {
|
||||
//var dateFormatString =
|
||||
//return string.Format(dateFormatString,parts.Year,parts.Month,parts.Day);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
string IDateFormatter.FormatTime(TimeParts parts, CultureInfo culture) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetDateParseReplacements(CultureInfo culture) {
|
||||
protected virtual Dictionary<string, string> GetDateParseReplacements() {
|
||||
return new Dictionary<string, string>() {
|
||||
{"dddd", String.Format("(?<day>{0})", String.Join("|", culture.DateTimeFormat.DayNames))},
|
||||
{"ddd", String.Format("(?<day>{0})", String.Join("|", culture.DateTimeFormat.AbbreviatedDayNames))},
|
||||
{"dddd", String.Format("(?<day>{0})", String.Join("|", _dateTimeFormatProvider.DayNames))},
|
||||
{"ddd", String.Format("(?<day>{0})", String.Join("|", _dateTimeFormatProvider.DayNamesShort))},
|
||||
{"dd", "(?<day>[0-9]{2})"},
|
||||
{"d", "(?<day>[0-9]{1,2})"},
|
||||
{"MMMM", String.Format("(?<month>{0})", String.Join("|", culture.DateTimeFormat.MonthNames.Where(x => !String.IsNullOrEmpty(x))))},
|
||||
{"MMM", String.Format("(?<month>{0})", String.Join("|", culture.DateTimeFormat.AbbreviatedMonthNames.Where(x => !String.IsNullOrEmpty(x))))},
|
||||
{"MMMM", String.Format("(?<month>{0})", String.Join("|", _dateTimeFormatProvider.MonthNames.Where(x => !String.IsNullOrEmpty(x))))},
|
||||
{"MMM", String.Format("(?<month>{0})", String.Join("|", _dateTimeFormatProvider.MonthNamesShort.Where(x => !String.IsNullOrEmpty(x))))},
|
||||
{"MM", "(?<month>[0-9]{2})"},
|
||||
{"M", "(?<month>[0-9]{1,2})"},
|
||||
{"yyyyy", "(?<year>[0-9]{5})"},
|
||||
@@ -109,14 +142,12 @@ namespace Orchard.Framework.Localization.Services {
|
||||
};
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetTimeParseReplacements(CultureInfo culture) {
|
||||
protected virtual Dictionary<string, string> GetTimeParseReplacements() {
|
||||
return new Dictionary<string, string>() {
|
||||
{"HH", "(?<hour>[0-9]{2})"},
|
||||
{"H", "(?<hour>[0-9]{1,2})"},
|
||||
{"hh", "(?<hour>[0-9]{2})"},
|
||||
{"h", "(?<hour>[0-9]{1,2})"},
|
||||
{"MM", "(?<minute>[0-9]{2})"},
|
||||
{"M", "(?<minute>[0-9]{1,2})"},
|
||||
{"mm", "(?<minute>[0-9]{2})"},
|
||||
{"m", "(?<minute>[0-9]{1,2})"},
|
||||
{"ss", "(?<second>[0-9]{2})"},
|
||||
@@ -127,59 +158,76 @@ namespace Orchard.Framework.Localization.Services {
|
||||
{"ffff", "(?<millisecond>[0-9]{4})"},
|
||||
{"fffff", "(?<millisecond>[0-9]{5})"},
|
||||
{"ffffff", "(?<millisecond>[0-9]{6})"},
|
||||
{"tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)},
|
||||
{"t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)},
|
||||
{" tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)},
|
||||
{" t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)}
|
||||
{"tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
||||
{"t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
||||
{" tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
||||
{" t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])}
|
||||
};
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetDateFormatReplacements(CultureInfo culture) {
|
||||
return new Dictionary<string, string>() {
|
||||
{"dddd", "{5:dddd}"},
|
||||
{"ddd", "{6:ddd}"},
|
||||
{"dd", "{2:00}"},
|
||||
{"d", "{2:##}"},
|
||||
{"MMMM", "{3:MMMM}"},
|
||||
{"MMM", "{4:MMM}"},
|
||||
{"MM", "{1:00}"},
|
||||
{"M", "{1:##}"},
|
||||
{"yyyyy", "{1:00000}"},
|
||||
{"yyyy", "{1:0000}"},
|
||||
{"yyy", "{1:000}"},
|
||||
{"yy", "{1:00}"},
|
||||
{"y", "{1:0}"}
|
||||
};
|
||||
//protected virtual Dictionary<string, string> GetDateFormatReplacements() {
|
||||
// return new Dictionary<string, string>() {
|
||||
// {"dddd", "{5:dddd}"},
|
||||
// {"ddd", "{6:ddd}"},
|
||||
// {"dd", "{2:00}"},
|
||||
// {"d", "{2:##}"},
|
||||
// {"MMMM", "{3:MMMM}"},
|
||||
// {"MMM", "{4:MMM}"},
|
||||
// {"MM", "{1:00}"},
|
||||
// {"M", "{1:##}"},
|
||||
// {"yyyyy", "{1:00000}"},
|
||||
// {"yyyy", "{1:0000}"},
|
||||
// {"yyy", "{1:000}"},
|
||||
// {"yy", "{1:00}"},
|
||||
// {"y", "{1:0}"}
|
||||
// };
|
||||
//}
|
||||
|
||||
//protected virtual Dictionary<string, string> GetTimeFormatReplacements() {
|
||||
// return new Dictionary<string, string>() {
|
||||
// {"HH", "(?<hour>[0-9]{2})"},
|
||||
// {"H", "(?<hour>[0-9]{1,2})"},
|
||||
// {"hh", "(?<hour>[0-9]{2})"},
|
||||
// {"h", "(?<hour>[0-9]{1,2})"},
|
||||
// {"mm", "(?<minute>[0-9]{2})"},
|
||||
// {"m", "(?<minute>[0-9]{1,2})"},
|
||||
// {"ss", "(?<second>[0-9]{2})"},
|
||||
// {"s", "(?<second>[0-9]{1,2})"},
|
||||
// {"f", "(?<millisecond>[0-9]{1})"},
|
||||
// {"ff", "(?<millisecond>[0-9]{2})"},
|
||||
// {"fff", "(?<millisecond>[0-9]{3})"},
|
||||
// {"ffff", "(?<millisecond>[0-9]{4})"},
|
||||
// {"fffff", "(?<millisecond>[0-9]{5})"},
|
||||
// {"ffffff", "(?<millisecond>[0-9]{6})"},
|
||||
// {"tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
||||
// {"t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
||||
// {" tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
||||
// {" t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])}
|
||||
// };
|
||||
//}
|
||||
|
||||
protected virtual string ConvertFormatStringToRegExPattern(string format, IDictionary<string, string> replacements) {
|
||||
string result = null;
|
||||
result = Regex.Replace(format, @"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\", m => String.Format(@"\{0}", m.Value));
|
||||
result = Regex.Replace(result, @"(?<!\\)'(.*?)((?<!\\)')", m => String.Format("(.{{{0}}})", m.Value.Replace("\\", "").Length - 2));
|
||||
result = result.ReplaceAll(replacements);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetTimeFormatReplacements(CultureInfo culture) {
|
||||
return new Dictionary<string, string>() {
|
||||
{"HH", "(?<hour>[0-9]{2})"},
|
||||
{"H", "(?<hour>[0-9]{1,2})"},
|
||||
{"hh", "(?<hour>[0-9]{2})"},
|
||||
{"h", "(?<hour>[0-9]{1,2})"},
|
||||
{"MM", "(?<minute>[0-9]{2})"},
|
||||
{"M", "(?<minute>[0-9]{1,2})"},
|
||||
{"mm", "(?<minute>[0-9]{2})"},
|
||||
{"m", "(?<minute>[0-9]{1,2})"},
|
||||
{"ss", "(?<second>[0-9]{2})"},
|
||||
{"s", "(?<second>[0-9]{1,2})"},
|
||||
{"f", "(?<millisecond>[0-9]{1})"},
|
||||
{"ff", "(?<millisecond>[0-9]{2})"},
|
||||
{"fff", "(?<millisecond>[0-9]{3})"},
|
||||
{"ffff", "(?<millisecond>[0-9]{4})"},
|
||||
{"fffff", "(?<millisecond>[0-9]{5})"},
|
||||
{"ffffff", "(?<millisecond>[0-9]{6})"},
|
||||
{"tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)},
|
||||
{"t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)},
|
||||
{" tt", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)},
|
||||
{" t", String.Format("\\s*(?<AMPM>{0}|{1})\\s*", culture.DateTimeFormat.AMDesignator, culture.DateTimeFormat.PMDesignator)}
|
||||
};
|
||||
protected virtual CultureInfo CurrentCulture {
|
||||
get {
|
||||
var workContext = _workContextAccessor.GetContext();
|
||||
return CultureInfo.GetCultureInfo(workContext.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
private string ReplaceAll(string original, IDictionary<string, string> replacements) {
|
||||
var pattern = String.Format("{0}", String.Join("|", replacements.Keys.ToArray()));
|
||||
return Regex.Replace(original, pattern, (match) => replacements[match.Value]);
|
||||
protected virtual Calendar CurrentCalendar {
|
||||
get {
|
||||
var workContext = _workContextAccessor.GetContext();
|
||||
if (!String.IsNullOrEmpty(workContext.CurrentCalendar))
|
||||
return _calendarManager.GetCalendarByName(workContext.CurrentCalendar);
|
||||
return CurrentCulture.Calendar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@ namespace Orchard.Localization.Services {
|
||||
public class DefaultDateLocalizationServices : IDateLocalizationServices {
|
||||
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IDateTimeFormatProvider _dateTimeLocalization;
|
||||
private readonly IDateTimeFormatProvider _dateTimeFormatProvider;
|
||||
private readonly ICalendarManager _calendarManager;
|
||||
|
||||
public DefaultDateLocalizationServices(
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IDateTimeFormatProvider dateTimeLocalization,
|
||||
IDateTimeFormatProvider dateTimeFormatProvider,
|
||||
ICalendarManager calendarManager) {
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_dateTimeLocalization = dateTimeLocalization;
|
||||
_dateTimeFormatProvider = dateTimeFormatProvider;
|
||||
_calendarManager = calendarManager;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Orchard.Localization.Services {
|
||||
|
||||
|
||||
public virtual string ConvertToLocalString(DateTime date, string nullText = null) {
|
||||
return ConvertToLocalString(ToNullable(date), _dateTimeLocalization.LongDateTimeFormat, nullText);
|
||||
return ConvertToLocalString(ToNullable(date), _dateTimeFormatProvider.LongDateTimeFormat, nullText);
|
||||
}
|
||||
|
||||
public virtual string ConvertToLocalString(DateTime date, string format, string nullText = null) {
|
||||
@@ -106,7 +106,7 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
|
||||
public virtual string ConvertToLocalDateString(DateTime? date, string nullText = null) {
|
||||
return ConvertToLocalString(date, _dateTimeLocalization.ShortDateFormat, nullText);
|
||||
return ConvertToLocalString(date, _dateTimeFormatProvider.ShortDateFormat, nullText);
|
||||
}
|
||||
|
||||
public virtual string ConvertToLocalTimeString(DateTime date, string nullText = null) {
|
||||
@@ -114,7 +114,7 @@ namespace Orchard.Localization.Services {
|
||||
}
|
||||
|
||||
public virtual string ConvertToLocalTimeString(DateTime? date, string nullText = null) {
|
||||
return ConvertToLocalString(date, _dateTimeLocalization.ShortTimeFormat, nullText);
|
||||
return ConvertToLocalString(date, _dateTimeFormatProvider.ShortTimeFormat, nullText);
|
||||
}
|
||||
|
||||
public virtual DateTime? ConvertFromLocalString(string dateString) {
|
||||
|
||||
@@ -6,11 +6,14 @@ using Orchard.Framework.Localization.Models;
|
||||
|
||||
namespace Orchard.Framework.Localization.Services {
|
||||
public interface IDateFormatter : IDependency {
|
||||
DateTimeParts ParseDateTime(string dateTimeString, CultureInfo culture);
|
||||
DateParts ParseDate(string dateString, CultureInfo culture);
|
||||
TimeParts ParseTime(string timeString, CultureInfo culture);
|
||||
string FormatDateTime(DateTimeParts parts, CultureInfo culture);
|
||||
string FormatDate(DateParts parts, CultureInfo culture);
|
||||
string FormatTime(TimeParts parts, CultureInfo culture);
|
||||
DateTimeParts ParseDateTime(string dateTimeString);
|
||||
DateParts ParseDate(string dateString);
|
||||
TimeParts ParseTime(string timeString);
|
||||
string FormatDateTime(DateTimeParts parts);
|
||||
string FormatDateTime(DateTimeParts parts, string format);
|
||||
string FormatDate(DateParts parts);
|
||||
string FormatDate(DateParts parts, string format);
|
||||
string FormatTime(TimeParts parts);
|
||||
string FormatTime(TimeParts parts, string format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +38,12 @@ struct DateLocalizationOptions {
|
||||
bool ConvertTimeZone;
|
||||
bool ConvertCalendar;
|
||||
string NullText;
|
||||
}
|
||||
}
|
||||
|
||||
TODO:
|
||||
* Consider current calendar in CultureDateTimeFormatProvider
|
||||
* Handle multiple allowed formats when parsing in DefaultDateFormatter
|
||||
* Handle month names as well as numbers in DefaultDateFormatter
|
||||
* Fix TODOs in DefaultDateFormatter
|
||||
* Rewrite DefaultDateLocalizationServices
|
||||
* Write unit tests for DefaultDateLocalizationServices
|
||||
@@ -321,7 +321,7 @@ namespace Orchard.Utility.Extensions {
|
||||
}
|
||||
|
||||
public static string ReplaceAll(this string original, IDictionary<string, string> replacements) {
|
||||
var pattern = String.Format("({0})", String.Join("|", replacements.Keys.ToArray()));
|
||||
var pattern = String.Format("{0}", String.Join("|", replacements.Keys.ToArray()));
|
||||
return Regex.Replace(original, pattern, (match) => replacements[match.Value]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user