Added format-specific overloads to IDateFormatter and DefaultDateFormatter for performance optimization.

This commit is contained in:
Daniel Stolt
2014-07-27 17:55:03 +02:00
parent b6cdebb58d
commit a09ab71c27
4 changed files with 86 additions and 19 deletions

View File

@@ -41,7 +41,7 @@ namespace Orchard.Framework.Tests.Localization {
var cultureGregorian = (CultureInfo)culture.Clone();
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
var dateTimeString = dateTime.ToString(dateTimeFormat, cultureGregorian);
var result = target.ParseDateTime(dateTimeString);
var result = target.ParseDateTime(dateTimeString, dateTimeFormat);
var reference = DateTime.ParseExact(dateTimeString, dateTimeFormat, culture);
var expected = new DateTimeParts(reference.Year, reference.Month, reference.Day, reference.Hour, reference.Minute, reference.Second, reference.Millisecond);
Assert.AreEqual(expected, result);
@@ -74,7 +74,7 @@ namespace Orchard.Framework.Tests.Localization {
foreach (var dateFormat in formats.AllDateFormats) { // All date formats supported by the culture.
var caseKey = String.Format("{0}:{1}", culture.Name, dateFormat);
allCases.Add(caseKey);
Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
try {
for (var month = 1; month <= 12; month++) { // All months in the year.
DateTime date = new DateTime(1998, month, 1);
@@ -82,7 +82,7 @@ namespace Orchard.Framework.Tests.Localization {
var cultureGregorian = (CultureInfo)culture.Clone();
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
var dateString = date.ToString(dateFormat, cultureGregorian);
var result = target.ParseDate(dateString);
var result = target.ParseDate(dateString, dateFormat);
var expected = new DateParts(date.Year, date.Month, date.Day);
Assert.AreEqual(expected, result);
}
@@ -113,12 +113,12 @@ namespace Orchard.Framework.Tests.Localization {
foreach (var timeFormat in formats.AllTimeFormats) { // All time formats supported by the culture.
var caseKey = String.Format("{0}:{1}", culture.Name, timeFormat);
allCases.Add(caseKey);
Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
try {
for (var hour = 0; hour <= 23; hour++) { // All hours in the day.
DateTime time = new DateTime(1998, 1, 1, hour, 30, 30);
var timeString = time.ToString(timeFormat, culture);
var result = target.ParseTime(timeString);
var result = target.ParseTime(timeString, timeFormat);
var reference = DateTime.ParseExact(timeString, timeFormat, culture);
var expected = new TimeParts(reference.Hour, reference.Minute, reference.Second, reference.Millisecond);
Assert.AreEqual(expected, result);

View File

@@ -28,44 +28,74 @@ namespace Orchard.Framework.Localization.Services {
var replacements = GetDateParseReplacements().Concat(GetTimeParseReplacements()).ToDictionary(item => item.Key, item => item.Value);
foreach (var dateTimeFormat in _dateTimeFormatProvider.AllDateTimeFormats) {
var dateTimePattern = ConvertFormatStringToRegexPattern(dateTimeFormat, replacements);
Match m = Regex.Match(dateTimeString.Trim(), dateTimePattern, RegexOptions.IgnoreCase);
if (m.Success) {
return new DateTimeParts(ExtractDateParts(m), ExtractTimeParts(m));
var result = TryParseDateTime(dateTimeString, dateTimeFormat, replacements);
if (result.HasValue) {
return result.Value;
}
}
throw new FormatException("The string was not recognized as a valid date and time.");
}
public virtual DateTimeParts ParseDateTime(string dateTimeString, string format) {
var replacements = GetDateParseReplacements().Concat(GetTimeParseReplacements()).ToDictionary(item => item.Key, item => item.Value);
var result = TryParseDateTime(dateTimeString, format, replacements);
if (result.HasValue) {
return result.Value;
}
throw new FormatException("The string was not recognized as a valid date and time.");
}
public virtual DateParts ParseDate(string dateString) {
var replacements = GetDateParseReplacements();
foreach (var dateFormat in _dateTimeFormatProvider.AllDateFormats) {
var datePattern = ConvertFormatStringToRegexPattern(dateFormat, replacements);
Match m = Regex.Match(dateString.Trim(), datePattern, RegexOptions.IgnoreCase);
if (m.Success) {
return ExtractDateParts(m);
var result = TryParseDate(dateString, dateFormat, replacements);
if (result.HasValue) {
return result.Value;
}
}
throw new FormatException("The string was not recognized as a valid date.");
}
public virtual DateParts ParseDate(string dateString, string format) {
var replacements = GetDateParseReplacements();
var result = TryParseDate(dateString, format, replacements);
if (result.HasValue) {
return result.Value;
}
throw new FormatException("The string was not recognized as a valid date.");
}
public virtual TimeParts ParseTime(string timeString) {
var replacements = GetTimeParseReplacements();
foreach (var timeFormat in _dateTimeFormatProvider.AllTimeFormats) {
var timePattern = ConvertFormatStringToRegexPattern(timeFormat, replacements);
Match m = Regex.Match(timeString.Trim(), timePattern, RegexOptions.IgnoreCase);
if (m.Success) {
return ExtractTimeParts(m);
var result = TryParseTime(timeString, timeFormat, replacements);
if (result.HasValue) {
return result.Value;
}
}
throw new FormatException("The string was not recognized as a valid time.");
}
public virtual TimeParts ParseTime(string timeString, string format) {
var replacements = GetTimeParseReplacements();
var result = TryParseTime(timeString, format, replacements);
if (result.HasValue) {
return result.Value;
}
throw new FormatException("The string was not recognized as a valid time.");
}
public virtual string FormatDateTime(DateTimeParts parts) {
// TODO: Mahsa should implement!
throw new NotImplementedException();
@@ -96,12 +126,41 @@ namespace Orchard.Framework.Localization.Services {
throw new NotImplementedException();
}
protected virtual DateTimeParts? TryParseDateTime(string dateTimeString, string format, IDictionary<string, string> replacements) {
var dateTimePattern = ConvertFormatStringToRegexPattern(format, replacements);
Match m = Regex.Match(dateTimeString.Trim(), dateTimePattern, RegexOptions.IgnoreCase);
if (m.Success) {
return new DateTimeParts(ExtractDateParts(m), ExtractTimeParts(m));
}
return null;
}
protected virtual DateParts? TryParseDate(string dateString, string format, IDictionary<string, string> replacements) {
var datePattern = ConvertFormatStringToRegexPattern(format, replacements);
Match m = Regex.Match(dateString.Trim(), datePattern, RegexOptions.IgnoreCase);
if (m.Success) {
return ExtractDateParts(m);
}
return null;
}
protected virtual TimeParts? TryParseTime(string timeString, string format, IDictionary<string, string> replacements) {
var timePattern = ConvertFormatStringToRegexPattern(format, replacements);
Match m = Regex.Match(timeString.Trim(), timePattern, RegexOptions.IgnoreCase);
if (m.Success) {
return ExtractTimeParts(m);
}
return null;
}
protected virtual DateParts ExtractDateParts(Match m) {
int year = 0,
month = 0,
day = 0;
year = CurrentCalendar.ToFourDigitYear(Int32.Parse(m.Groups["year"].Value));
if (m.Groups["year"].Success) {
year = CurrentCalendar.ToFourDigitYear(Int32.Parse(m.Groups["year"].Value));
}
// For the month we can either use the month number, the abbreviated month name or the full month name.
if (m.Groups["month"].Success) {
@@ -114,7 +173,9 @@ namespace Orchard.Framework.Localization.Services {
month = _dateTimeFormatProvider.MonthNames.Select(x => x.ToLowerInvariant()).ToList().IndexOf(m.Groups["monthName"].Value.ToLowerInvariant()) + 1;
}
day = Int32.Parse(m.Groups["day"].Value);
if (m.Groups["day"].Success) {
day = Int32.Parse(m.Groups["day"].Value);
}
return new DateParts(year, month, day);
}

View File

@@ -7,8 +7,11 @@ using Orchard.Framework.Localization.Models;
namespace Orchard.Framework.Localization.Services {
public interface IDateFormatter : IDependency {
DateTimeParts ParseDateTime(string dateTimeString);
DateTimeParts ParseDateTime(string dateTimeString, string format);
DateParts ParseDate(string dateString);
DateParts ParseDate(string dateString, string format);
TimeParts ParseTime(string timeString);
TimeParts ParseTime(string timeString, string format);
string FormatDateTime(DateTimeParts parts);
string FormatDateTime(DateTimeParts parts, string format);
string FormatDate(DateParts parts);

View File

@@ -41,6 +41,9 @@ struct DateLocalizationOptions {
}
TODO:
* Add ability to analyze format string to determine which parts we might reasonably expect back from parsing
- 4-digit year or only 2-digit so we must infer century?
- Which components are present?
* Rewrite DefaultDateLocalizationServices
* Write unit tests for DefaultDateLocalizationServices
* Add warning message when saving unsupported combination in settings