mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Add code in DefaultDateFormatterTests to analyze the tested format string to determine which parts we might reasonably expect back from parsing.
Fixed a number of bugs in DefaultDateFormatter. Improved test code in DefaultDateFormatterTests. Added genitive month name support to IDateTimeFormatProvider and its implementations. Added support to DefaultDateFormatter for considering both genitive and non-genitive month names during parsing.
This commit is contained in:
@@ -24,8 +24,13 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
var failedCases = new ConcurrentDictionary<string, Exception>();
|
var failedCases = new ConcurrentDictionary<string, Exception>();
|
||||||
var maxFailedCases = 0;
|
var maxFailedCases = 0;
|
||||||
|
|
||||||
|
var options = new ParallelOptions();
|
||||||
|
if (Debugger.IsAttached) {
|
||||||
|
options.MaxDegreeOfParallelism = 1;
|
||||||
|
}
|
||||||
|
|
||||||
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||||
Parallel.ForEach(allCultures, culture => { // All cultures on the machine.
|
Parallel.ForEach(allCultures, options, culture => { // All cultures on the machine.
|
||||||
var container = InitializeContainer(culture.Name, "GregorianCalendar");
|
var container = InitializeContainer(culture.Name, "GregorianCalendar");
|
||||||
var formats = container.Resolve<IDateTimeFormatProvider>();
|
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||||
var target = container.Resolve<IDateFormatter>();
|
var target = container.Resolve<IDateFormatter>();
|
||||||
@@ -66,29 +71,34 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
var failedCases = new ConcurrentDictionary<string, Exception>();
|
var failedCases = new ConcurrentDictionary<string, Exception>();
|
||||||
var maxFailedCases = 0;
|
var maxFailedCases = 0;
|
||||||
|
|
||||||
|
var options = new ParallelOptions();
|
||||||
|
if (Debugger.IsAttached) {
|
||||||
|
options.MaxDegreeOfParallelism = 1;
|
||||||
|
}
|
||||||
|
|
||||||
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||||
Parallel.ForEach(allCultures, culture => { // All cultures on the machine.
|
Parallel.ForEach(allCultures, options, culture => { // All cultures on the machine.
|
||||||
var container = InitializeContainer(culture.Name, "GregorianCalendar");
|
var container = InitializeContainer(culture.Name, "GregorianCalendar");
|
||||||
var formats = container.Resolve<IDateTimeFormatProvider>();
|
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||||
var target = container.Resolve<IDateFormatter>();
|
var target = container.Resolve<IDateFormatter>();
|
||||||
foreach (var dateFormat in formats.AllDateFormats) { // All date formats supported by the culture.
|
foreach (var dateFormat in formats.AllDateFormats) { // All date formats supported by the culture.
|
||||||
var caseKey = String.Format("{0}:{1}", culture.Name, dateFormat);
|
for (var month = 1; month <= 12; month++) { // All months in the year.
|
||||||
allCases.Add(caseKey);
|
DateTime date = new DateTime(1998, month, 1);
|
||||||
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
|
// Print string using Gregorian calendar to avoid calendar conversion.
|
||||||
try {
|
var cultureGregorian = (CultureInfo)culture.Clone();
|
||||||
for (var month = 1; month <= 12; month++) { // All months in the year.
|
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
||||||
DateTime date = new DateTime(1998, month, 1);
|
var dateString = date.ToString(dateFormat, cultureGregorian);
|
||||||
// Print string using Gregorian calendar to avoid calendar conversion.
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateFormat, dateString);
|
||||||
var cultureGregorian = (CultureInfo)culture.Clone();
|
allCases.Add(caseKey);
|
||||||
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
|
||||||
var dateString = date.ToString(dateFormat, cultureGregorian);
|
try {
|
||||||
var result = target.ParseDate(dateString, dateFormat);
|
var result = target.ParseDate(dateString, dateFormat);
|
||||||
var expected = new DateParts(date.Year, date.Month, date.Day);
|
var expected = GetExpectedDateParts(date, dateFormat);
|
||||||
Assert.AreEqual(expected, result);
|
Assert.AreEqual(expected, result);
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex) {
|
||||||
catch (Exception ex) {
|
failedCases.TryAdd(caseKey, ex);
|
||||||
failedCases.TryAdd(caseKey, ex);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -105,27 +115,31 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
var failedCases = new ConcurrentDictionary<string, Exception>();
|
var failedCases = new ConcurrentDictionary<string, Exception>();
|
||||||
var maxFailedCases = 0;
|
var maxFailedCases = 0;
|
||||||
|
|
||||||
|
var options = new ParallelOptions();
|
||||||
|
if (Debugger.IsAttached) {
|
||||||
|
options.MaxDegreeOfParallelism = 1;
|
||||||
|
}
|
||||||
|
|
||||||
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||||
Parallel.ForEach(allCultures, culture => { // All cultures on the machine.
|
Parallel.ForEach(allCultures, options, culture => { // All cultures on the machine.
|
||||||
var container = InitializeContainer(culture.Name, null);
|
var container = InitializeContainer(culture.Name, null);
|
||||||
var formats = container.Resolve<IDateTimeFormatProvider>();
|
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||||
var target = container.Resolve<IDateFormatter>();
|
var target = container.Resolve<IDateFormatter>();
|
||||||
foreach (var timeFormat in formats.AllTimeFormats) { // All time formats supported by the culture.
|
foreach (var timeFormat in formats.AllTimeFormats) { // All time formats supported by the culture.
|
||||||
var caseKey = String.Format("{0}:{1}", culture.Name, timeFormat);
|
for (var hour = 0; hour <= 23; hour++) { // All hours in the day.
|
||||||
allCases.Add(caseKey);
|
DateTime time = new DateTime(1998, 1, 1, hour, 30, 30);
|
||||||
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
|
var timeString = time.ToString(timeFormat, culture);
|
||||||
try {
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, timeFormat, timeString);
|
||||||
for (var hour = 0; hour <= 23; hour++) { // All hours in the day.
|
allCases.Add(caseKey);
|
||||||
DateTime time = new DateTime(1998, 1, 1, hour, 30, 30);
|
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
|
||||||
var timeString = time.ToString(timeFormat, culture);
|
try {
|
||||||
var result = target.ParseTime(timeString, timeFormat);
|
var result = target.ParseTime(timeString, timeFormat);
|
||||||
var reference = DateTime.ParseExact(timeString, timeFormat, culture);
|
var expected = GetExpectedTimeParts(time, timeFormat);
|
||||||
var expected = new TimeParts(reference.Hour, reference.Minute, reference.Second, reference.Millisecond);
|
|
||||||
Assert.AreEqual(expected, result);
|
Assert.AreEqual(expected, result);
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex) {
|
||||||
catch (Exception ex) {
|
failedCases.TryAdd(caseKey, ex);
|
||||||
failedCases.TryAdd(caseKey, ex);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -135,6 +149,23 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DateParts GetExpectedDateParts(DateTime date, string format) {
|
||||||
|
return new DateParts(
|
||||||
|
format.Contains('y') ? date.Year : 0,
|
||||||
|
format.Contains('M') ? date.Month : 0,
|
||||||
|
format.Contains('d') ? date.Day : 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeParts GetExpectedTimeParts(DateTime time, string format) {
|
||||||
|
return new TimeParts(
|
||||||
|
format.Contains('H') || format.Contains('h') ? time.Hour : 0,
|
||||||
|
format.Contains('m') ? time.Minute : 0,
|
||||||
|
format.Contains('s') ? time.Second : 0,
|
||||||
|
format.Contains('f') ? time.Millisecond : 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private IContainer InitializeContainer(string cultureName, string calendarName) {
|
private IContainer InitializeContainer(string cultureName, string calendarName) {
|
||||||
var builder = new ContainerBuilder();
|
var builder = new ContainerBuilder();
|
||||||
builder.RegisterInstance<WorkContext>(new StubWorkContext(cultureName, calendarName));
|
builder.RegisterInstance<WorkContext>(new StubWorkContext(cultureName, calendarName));
|
||||||
|
|||||||
@@ -26,12 +26,24 @@ namespace Orchard.Localization.Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<string> MonthNamesGenitive {
|
||||||
|
get {
|
||||||
|
return MonthNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<string> MonthNamesShort {
|
public IEnumerable<string> MonthNamesShort {
|
||||||
get {
|
get {
|
||||||
return T("Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec").Text.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
|
return T("Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec").Text.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<string> MonthNamesShortGenitive {
|
||||||
|
get {
|
||||||
|
return MonthNamesShort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<string> DayNames {
|
public IEnumerable<string> DayNames {
|
||||||
get {
|
get {
|
||||||
return T("Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday").Text.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
|
return T("Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday").Text.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ namespace Orchard.Localization.Services {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CultureDateTimeFormatProvider : IDateTimeFormatProvider {
|
public class CultureDateTimeFormatProvider : IDateTimeFormatProvider {
|
||||||
|
|
||||||
// TODO: This implementation should probably also depend on the current calendar, because DateTimeFormatInfo returns different strings depending on the calendar.
|
|
||||||
|
|
||||||
private readonly IWorkContextAccessor _workContextAccessor;
|
private readonly IWorkContextAccessor _workContextAccessor;
|
||||||
private readonly ICalendarManager _calendarManager;
|
private readonly ICalendarManager _calendarManager;
|
||||||
|
|
||||||
@@ -31,12 +29,24 @@ namespace Orchard.Localization.Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<string> MonthNamesGenitive {
|
||||||
|
get {
|
||||||
|
return DateTimeFormat.MonthGenitiveNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public virtual IEnumerable<string> MonthNamesShort {
|
public virtual IEnumerable<string> MonthNamesShort {
|
||||||
get {
|
get {
|
||||||
return DateTimeFormat.AbbreviatedMonthNames;
|
return DateTimeFormat.AbbreviatedMonthNames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<string> MonthNamesShortGenitive {
|
||||||
|
get {
|
||||||
|
return DateTimeFormat.AbbreviatedMonthGenitiveNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public virtual IEnumerable<string> DayNames {
|
public virtual IEnumerable<string> DayNames {
|
||||||
get {
|
get {
|
||||||
return DateTimeFormat.DayNames;
|
return DateTimeFormat.DayNames;
|
||||||
@@ -69,7 +79,7 @@ namespace Orchard.Localization.Services {
|
|||||||
|
|
||||||
public virtual string ShortDateTimeFormat {
|
public virtual string ShortDateTimeFormat {
|
||||||
get {
|
get {
|
||||||
// From empirical testing I am fairly certain this invariably evaluates to
|
// From empirical testing I am fairly certain First() invariably evaluates to
|
||||||
// the pattern actually used when printing using the 'g' (i.e. general date/time
|
// the pattern actually used when printing using the 'g' (i.e. general date/time
|
||||||
// pattern with short time) standard format string. /DS
|
// pattern with short time) standard format string. /DS
|
||||||
return DateTimeFormat.GetAllDateTimePatterns('g').First();
|
return DateTimeFormat.GetAllDateTimePatterns('g').First();
|
||||||
|
|||||||
@@ -167,10 +167,26 @@ namespace Orchard.Framework.Localization.Services {
|
|||||||
month = Int32.Parse(m.Groups["month"].Value);
|
month = Int32.Parse(m.Groups["month"].Value);
|
||||||
}
|
}
|
||||||
else if (m.Groups["monthNameShort"].Success) {
|
else if (m.Groups["monthNameShort"].Success) {
|
||||||
month = _dateTimeFormatProvider.MonthNamesShort.Select(x => x.ToLowerInvariant()).ToList().IndexOf(m.Groups["monthNameShort"].Value.ToLowerInvariant()) + 1;
|
var shortName = m.Groups["monthNameShort"].Value.ToLowerInvariant();
|
||||||
|
var allShortNamesGenitive = _dateTimeFormatProvider.MonthNamesShortGenitive.Select(x => x.ToLowerInvariant()).ToList();
|
||||||
|
var allShortNames = _dateTimeFormatProvider.MonthNamesShort.Select(x => x.ToLowerInvariant()).ToList();
|
||||||
|
if (allShortNamesGenitive.Contains(shortName)) {
|
||||||
|
month = allShortNamesGenitive.IndexOf(shortName) + 1;
|
||||||
|
}
|
||||||
|
else if (allShortNames.Contains(shortName)) {
|
||||||
|
month = allShortNames.IndexOf(shortName) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (m.Groups["monthName"].Success) {
|
else if (m.Groups["monthName"].Success) {
|
||||||
month = _dateTimeFormatProvider.MonthNames.Select(x => x.ToLowerInvariant()).ToList().IndexOf(m.Groups["monthName"].Value.ToLowerInvariant()) + 1;
|
var name = m.Groups["monthName"].Value.ToLowerInvariant();
|
||||||
|
var allNamesGenitive = _dateTimeFormatProvider.MonthNamesGenitive.Select(x => x.ToLowerInvariant()).ToList();
|
||||||
|
var allNames = _dateTimeFormatProvider.MonthNames.Select(x => x.ToLowerInvariant()).ToList();
|
||||||
|
if (allNamesGenitive.Contains(name)) {
|
||||||
|
month = allNamesGenitive.IndexOf(name) + 1;
|
||||||
|
}
|
||||||
|
else if (allNames.Contains(name)) {
|
||||||
|
month = allNames.IndexOf(name) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.Groups["day"].Success) {
|
if (m.Groups["day"].Success) {
|
||||||
@@ -215,12 +231,12 @@ namespace Orchard.Framework.Localization.Services {
|
|||||||
|
|
||||||
protected virtual Dictionary<string, string> GetDateParseReplacements() {
|
protected virtual Dictionary<string, string> GetDateParseReplacements() {
|
||||||
return new Dictionary<string, string>() {
|
return new Dictionary<string, string>() {
|
||||||
{"dddd", String.Format("(?<dayName>{0})", String.Join("|", _dateTimeFormatProvider.DayNames))},
|
{"dddd", String.Format("(?<dayName>{0})", String.Join("|", _dateTimeFormatProvider.DayNames.Select(x => EscapeForRegex(x))))},
|
||||||
{"ddd", String.Format("(?<dayNameShort>{0})", String.Join("|", _dateTimeFormatProvider.DayNamesShort))},
|
{"ddd", String.Format("(?<dayNameShort>{0})", String.Join("|", _dateTimeFormatProvider.DayNamesShort.Select(x => EscapeForRegex(x))))},
|
||||||
{"dd", "(?<day>[0-9]{2})"},
|
{"dd", "(?<day>[0-9]{2})"},
|
||||||
{"d", "(?<day>[0-9]{1,2})"},
|
{"d", "(?<day>[0-9]{1,2})"},
|
||||||
{"MMMM", String.Format("(?<monthName>{0})", String.Join("|", _dateTimeFormatProvider.MonthNames.Where(x => !String.IsNullOrEmpty(x))))},
|
{"MMMM", String.Format("(?<monthName>{0})", String.Join("|", _dateTimeFormatProvider.MonthNames.Union(_dateTimeFormatProvider.MonthNamesGenitive).Where(x => !String.IsNullOrEmpty(x)).Distinct().Select(x => EscapeForRegex(x))))},
|
||||||
{"MMM", String.Format("(?<monthNameShort>{0})", String.Join("|", _dateTimeFormatProvider.MonthNamesShort.Where(x => !String.IsNullOrEmpty(x))))},
|
{"MMM", String.Format("(?<monthNameShort>{0})", String.Join("|", _dateTimeFormatProvider.MonthNamesShort.Union(_dateTimeFormatProvider.MonthNamesShortGenitive).Where(x => !String.IsNullOrEmpty(x)).Distinct().Select(x => EscapeForRegex(x))))},
|
||||||
{"MM", "(?<month>[0-9]{2})"},
|
{"MM", "(?<month>[0-9]{2})"},
|
||||||
{"M", "(?<month>[0-9]{1,2})"},
|
{"M", "(?<month>[0-9]{1,2})"},
|
||||||
{"yyyyy", "(?<year>[0-9]{5})"},
|
{"yyyyy", "(?<year>[0-9]{5})"},
|
||||||
@@ -247,10 +263,10 @@ namespace Orchard.Framework.Localization.Services {
|
|||||||
{"ffff", "(?<millisecond>[0-9]{4})"},
|
{"ffff", "(?<millisecond>[0-9]{4})"},
|
||||||
{"fffff", "(?<millisecond>[0-9]{5})"},
|
{"fffff", "(?<millisecond>[0-9]{5})"},
|
||||||
{"ffffff", "(?<millisecond>[0-9]{6})"},
|
{"ffffff", "(?<millisecond>[0-9]{6})"},
|
||||||
{"tt", String.Format("\\s*(?<amPm>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
{"tt", String.Format("\\s*(?<amPm>{0}|{1})\\s*", EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[0]), EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[1]))},
|
||||||
{"t", String.Format("\\s*(?<amPm>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
{"t", String.Format("\\s*(?<amPm>{0}|{1})\\s*", EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[0]), EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[1]))},
|
||||||
{" tt", String.Format("\\s*(?<amPm>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])},
|
{" tt", String.Format("\\s*(?<amPm>{0}|{1})\\s*", EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[0]), EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[1]))},
|
||||||
{" t", String.Format("\\s*(?<amPm>{0}|{1})\\s*", _dateTimeFormatProvider.AmPmDesignators.ToArray()[0], _dateTimeFormatProvider.AmPmDesignators.ToArray()[1])}
|
{" t", String.Format("\\s*(?<amPm>{0}|{1})\\s*", EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[0]), EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators.ToArray()[1]))}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,11 +312,23 @@ namespace Orchard.Framework.Localization.Services {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
protected virtual string ConvertFormatStringToRegexPattern(string format, IDictionary<string, string> replacements) {
|
protected virtual string ConvertFormatStringToRegexPattern(string format, IDictionary<string, string> replacements) {
|
||||||
string result = null;
|
string result = format;
|
||||||
result = Regex.Replace(format, @"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\", m => String.Format(@"\{0}", m.Value));
|
|
||||||
|
// Transform the / and : characters into culture-specific date and time separators.
|
||||||
|
result = Regex.Replace(result, @"\/|:", m => m.Value == "/" ? _dateTimeFormatProvider.DateSeparator : _dateTimeFormatProvider.TimeSeparator);
|
||||||
|
|
||||||
|
// Escape all characters that are intrinsic Regex syntax.
|
||||||
|
result = EscapeForRegex(result);
|
||||||
|
|
||||||
|
// Transform all literals to corresponding wildcard matches.
|
||||||
result = Regex.Replace(result, @"(?<!\\)'(.*?)((?<!\\)')", m => String.Format("(.{{{0}}})", m.Value.Replace("\\", "").Length - 2));
|
result = Regex.Replace(result, @"(?<!\\)'(.*?)((?<!\\)')", m => String.Format("(.{{{0}}})", m.Value.Replace("\\", "").Length - 2));
|
||||||
|
|
||||||
|
// Transform all DateTime format specifiers into corresponding Regex captures.
|
||||||
result = result.ReplaceAll(replacements);
|
result = result.ReplaceAll(replacements);
|
||||||
result = String.Format(@"^{0}$", result); // Make sure string is anchored to beginning and end.
|
|
||||||
|
// Make sure string is anchored to beginning and end.
|
||||||
|
result = String.Format(@"^{0}$", result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,6 +339,10 @@ namespace Orchard.Framework.Localization.Services {
|
|||||||
return hour12 == 12 ? 0 : hour12;
|
return hour12 == 12 ? 0 : hour12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual string EscapeForRegex(string input) {
|
||||||
|
return Regex.Replace(input, @"\.|\$|\^|\{|\[|\(|\||\)|\*|\+|\?|\\", m => String.Format(@"\{0}", m.Value));
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual CultureInfo CurrentCulture {
|
protected virtual CultureInfo CurrentCulture {
|
||||||
get {
|
get {
|
||||||
var workContext = _workContextAccessor.GetContext();
|
var workContext = _workContextAccessor.GetContext();
|
||||||
|
|||||||
@@ -41,10 +41,8 @@ struct DateLocalizationOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
* Test for proper handling of fraction (f) format specifier - suspect it does not work properly in current state
|
||||||
* Literal parts in format strings should be transformed to corresponding literal in Regex, not just into a wildcard, otherwise the resulting wildcard in some cases will match subsequent non-literal parts of the date/time string.
|
* Literal parts in format strings should be transformed to corresponding literal in Regex, not just into a wildcard, otherwise the resulting wildcard in some cases will match subsequent non-literal parts of the date/time string.
|
||||||
* 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
|
* Rewrite DefaultDateLocalizationServices
|
||||||
* Write unit tests for DefaultDateLocalizationServices
|
* Write unit tests for DefaultDateLocalizationServices
|
||||||
* Add warning message when saving unsupported combination in settings
|
* Add warning message when saving unsupported combination in settings
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ namespace Orchard.Localization.Services {
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of genitive month names (used in contexts when a day is involved).
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<string> MonthNamesGenitive {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of abbreviated month names.
|
/// Gets a list of abbreviated month names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -23,6 +30,13 @@ namespace Orchard.Localization.Services {
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of abbreviated genivite month names (used in contexts when a day is involved).
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<string> MonthNamesShortGenitive {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of weekday names.
|
/// Gets a list of weekday names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user