mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Added date/time formatting (the last piece of the puzzle) to DefaultDateFormatter, and unit tests.
This commit is contained in:
@@ -44,16 +44,16 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
for (var month = 1; month <= 12; month++) { // All months in the year.
|
for (var month = 1; month <= 12; month++) { // All months in the year.
|
||||||
|
|
||||||
DateTime dateTime = new DateTime(1998, month, 1, 10, 30, 30);
|
DateTime dateTime = new DateTime(1998, month, 1, 10, 30, 30);
|
||||||
|
|
||||||
// Print string using Gregorian calendar to avoid calendar conversion.
|
// Print string using Gregorian calendar to avoid calendar conversion.
|
||||||
var cultureGregorian = (CultureInfo)culture.Clone();
|
var cultureGregorian = (CultureInfo)culture.Clone();
|
||||||
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
||||||
var dateTimeString = dateTime.ToString(dateTimeFormat, cultureGregorian);
|
var dateTimeString = dateTime.ToString(dateTimeFormat, cultureGregorian);
|
||||||
|
|
||||||
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateTimeFormat, dateTimeString);
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateTimeFormat, dateTimeString);
|
||||||
allCases.Add(caseKey);
|
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 {
|
try {
|
||||||
var result = target.ParseDateTime(dateTimeString, dateTimeFormat);
|
var result = target.ParseDateTime(dateTimeString, dateTimeFormat);
|
||||||
var expected = GetExpectedDateTimeParts(dateTime, dateTimeFormat);
|
var expected = GetExpectedDateTimeParts(dateTime, dateTimeFormat);
|
||||||
@@ -90,7 +90,7 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
var target = container.Resolve<IDateFormatter>();
|
var target = container.Resolve<IDateFormatter>();
|
||||||
|
|
||||||
foreach (var dateTimeFormat in formats.AllDateTimeFormats) { // All date and time formats supported by the culture.
|
foreach (var dateTimeFormat in formats.AllDateTimeFormats) { // All date and time formats supported by the culture.
|
||||||
foreach (var hour in new [] {0, 6, 12, 18}) { // Enough hours to cover all code paths (AM/PM, 12<->00, etc).
|
foreach (var hour in new[] { 0, 6, 12, 18 }) { // Enough hours to cover all code paths (AM/PM, 12<->00, etc).
|
||||||
|
|
||||||
DateTime dateTime = new DateTime(1998, 1, 1, hour, 30, 30);
|
DateTime dateTime = new DateTime(1998, 1, 1, hour, 30, 30);
|
||||||
|
|
||||||
@@ -151,16 +151,16 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
for (var month = 1; month <= 12; month++) { // All months in the year.
|
for (var month = 1; month <= 12; month++) { // All months in the year.
|
||||||
|
|
||||||
DateTime date = new DateTime(1998, month, 1);
|
DateTime date = new DateTime(1998, month, 1);
|
||||||
|
|
||||||
// Print string using Gregorian calendar to avoid calendar conversion.
|
// Print string using Gregorian calendar to avoid calendar conversion.
|
||||||
var cultureGregorian = (CultureInfo)culture.Clone();
|
var cultureGregorian = (CultureInfo)culture.Clone();
|
||||||
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
||||||
var dateString = date.ToString(dateFormat, cultureGregorian);
|
var dateString = date.ToString(dateFormat, cultureGregorian);
|
||||||
|
|
||||||
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateFormat, dateString);
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateFormat, dateString);
|
||||||
allCases.Add(caseKey);
|
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 {
|
try {
|
||||||
var result = target.ParseDate(dateString, dateFormat);
|
var result = target.ParseDate(dateString, dateFormat);
|
||||||
var expected = GetExpectedDateParts(date, dateFormat);
|
var expected = GetExpectedDateParts(date, dateFormat);
|
||||||
@@ -210,11 +210,11 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
|
|
||||||
DateTime time = new DateTime(1998, 1, 1, hour, 30, 30);
|
DateTime time = new DateTime(1998, 1, 1, hour, 30, 30);
|
||||||
var timeString = time.ToString(timeFormat, culture);
|
var timeString = time.ToString(timeFormat, culture);
|
||||||
|
|
||||||
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, timeFormat, timeString);
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, timeFormat, timeString);
|
||||||
allCases.Add(caseKey);
|
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 {
|
try {
|
||||||
var result = target.ParseTime(timeString, timeFormat);
|
var result = target.ParseTime(timeString, timeFormat);
|
||||||
var expected = GetExpectedTimeParts(time, timeFormat);
|
var expected = GetExpectedTimeParts(time, timeFormat);
|
||||||
@@ -241,6 +241,132 @@ namespace Orchard.Framework.Tests.Localization {
|
|||||||
target.ParseTime("BlaBlaBla");
|
target.ParseTime("BlaBlaBla");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Description("Date/time formatting works correctly for all combinations of months, format strings and cultures.")]
|
||||||
|
public void FormatDateTimeTest01() {
|
||||||
|
var allCases = new ConcurrentBag<string>();
|
||||||
|
var failedCases = new ConcurrentDictionary<string, Exception>();
|
||||||
|
var maxFailedCases = 0;
|
||||||
|
|
||||||
|
var options = new ParallelOptions();
|
||||||
|
if (Debugger.IsAttached) {
|
||||||
|
options.MaxDegreeOfParallelism = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||||
|
Parallel.ForEach(allCultures, options, culture => { // All cultures on the machine.
|
||||||
|
var container = InitializeContainer(culture.Name, "GregorianCalendar");
|
||||||
|
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||||
|
var target = container.Resolve<IDateFormatter>();
|
||||||
|
|
||||||
|
foreach (var dateTimeFormat in formats.AllDateTimeFormats) { // All date/time formats supported by the culture.
|
||||||
|
for (var month = 1; month <= 12; month++) { // All months in the year.
|
||||||
|
|
||||||
|
DateTime dateTime = new DateTime(1998, month, 1, 10, 30, 30);
|
||||||
|
DateTimeParts dateTimeParts = new DateTimeParts(1998, month, 1, 10, 30, 30, 0);
|
||||||
|
|
||||||
|
// Print reference string using Gregorian calendar to avoid calendar conversion.
|
||||||
|
var cultureGregorian = (CultureInfo)culture.Clone();
|
||||||
|
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
||||||
|
|
||||||
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateTimeFormat, dateTimeParts);
|
||||||
|
allCases.Add(caseKey);
|
||||||
|
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = target.FormatDateTime(dateTimeParts, dateTimeFormat);
|
||||||
|
var expected = dateTime.ToString(dateTimeFormat, cultureGregorian);
|
||||||
|
if (result != expected) {
|
||||||
|
// The .NET date formatting logic contains a bug that causes it to recognize 'd' and 'dd'
|
||||||
|
// as numerical day specifiers even when they are embedded in literals. Our implementation
|
||||||
|
// does not contain this bug. If we encounter an unexpected result and the .NET reference
|
||||||
|
// result contains the genitive month name, replace it with the non-genitive month name
|
||||||
|
// before asserting.
|
||||||
|
var numericalDayPattern = @"(\b|[^d])d{1,2}(\b|[^d])";
|
||||||
|
var containsNumericalDay = Regex.IsMatch(dateTimeFormat, numericalDayPattern);
|
||||||
|
if (containsNumericalDay) {
|
||||||
|
var monthName = formats.MonthNames[month - 1];
|
||||||
|
var monthNameGenitive = formats.MonthNamesGenitive[month - 1];
|
||||||
|
expected = expected.Replace(monthNameGenitive, monthName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.AreEqual(expected, result);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
failedCases.TryAdd(caseKey, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (failedCases.Count > maxFailedCases) {
|
||||||
|
throw new AggregateException(String.Format("Format tests failed for {0} of {1} cases. Expected {2} failed cases or less.", failedCases.Count, allCases.Count, maxFailedCases), failedCases.Values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Description("Date/time formatting works correctly for all combinations of hours, format strings and cultures.")]
|
||||||
|
public void FormatDateTimeTest02() {
|
||||||
|
var allCases = new ConcurrentBag<string>();
|
||||||
|
var failedCases = new ConcurrentDictionary<string, Exception>();
|
||||||
|
var maxFailedCases = 0;
|
||||||
|
|
||||||
|
var options = new ParallelOptions();
|
||||||
|
if (Debugger.IsAttached) {
|
||||||
|
options.MaxDegreeOfParallelism = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||||
|
Parallel.ForEach(allCultures, options, culture => { // All cultures on the machine.
|
||||||
|
var container = InitializeContainer(culture.Name, "GregorianCalendar");
|
||||||
|
var formats = container.Resolve<IDateTimeFormatProvider>();
|
||||||
|
var target = container.Resolve<IDateFormatter>();
|
||||||
|
|
||||||
|
foreach (var dateTimeFormat in formats.AllDateTimeFormats) { // All date/time formats supported by the culture.
|
||||||
|
foreach (var hour in new[] { 0, 6, 12, 18 }) { // Enough hours to cover all code paths (AM/PM, 12<->00, 1/2 digits, etc).
|
||||||
|
|
||||||
|
DateTime dateTime = new DateTime(1998, 1, 1, hour, 30, 30);
|
||||||
|
DateTimeParts dateTimeParts = new DateTimeParts(1998, 1, 1, hour, 30, 30, 0);
|
||||||
|
|
||||||
|
// Print reference string using Gregorian calendar to avoid calendar conversion.
|
||||||
|
var cultureGregorian = (CultureInfo)culture.Clone();
|
||||||
|
cultureGregorian.DateTimeFormat.Calendar = cultureGregorian.OptionalCalendars.OfType<GregorianCalendar>().First();
|
||||||
|
|
||||||
|
var caseKey = String.Format("{0}___{1}___{2}", culture.Name, dateTimeFormat, dateTimeParts);
|
||||||
|
allCases.Add(caseKey);
|
||||||
|
//Debug.WriteLine(String.Format("{0} cases tested so far. Testing case {1}...", allCases.Count, caseKey));
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = target.FormatDateTime(dateTimeParts, dateTimeFormat);
|
||||||
|
var expected = dateTime.ToString(dateTimeFormat, cultureGregorian);
|
||||||
|
if (result != expected) {
|
||||||
|
// The .NET date formatting logic contains a bug that causes it to recognize 'd' and 'dd'
|
||||||
|
// as numerical day specifiers even when they are embedded in literals. Our implementation
|
||||||
|
// does not contain this bug. If we encounter an unexpected result and the .NET reference
|
||||||
|
// result contains the genitive month name, replace it with the non-genitive month name
|
||||||
|
// before asserting.
|
||||||
|
var numericalDayPattern = @"(\b|[^d])d{1,2}(\b|[^d])";
|
||||||
|
var containsNumericalDay = Regex.IsMatch(dateTimeFormat, numericalDayPattern);
|
||||||
|
if (containsNumericalDay) {
|
||||||
|
var monthName = formats.MonthNames[0];
|
||||||
|
var monthNameGenitive = formats.MonthNamesGenitive[0];
|
||||||
|
expected = expected.Replace(monthNameGenitive, monthName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.AreEqual(expected, result);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
failedCases.TryAdd(caseKey, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (failedCases.Count > maxFailedCases) {
|
||||||
|
throw new AggregateException(String.Format("Format tests failed for {0} of {1} cases. Expected {2} failed cases or less.", failedCases.Count, allCases.Count, maxFailedCases), failedCases.Values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Description("Date formatting works correctly for all combinations of months, format strings and cultures.")]
|
[Description("Date formatting works correctly for all combinations of months, format strings and cultures.")]
|
||||||
public void FormatDateTest01() {
|
public void FormatDateTest01() {
|
||||||
|
@@ -30,6 +30,18 @@ namespace Orchard.Localization.Models {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateTime ToDateTime() {
|
||||||
|
return new DateTime(
|
||||||
|
Date.Year > 0 ? Date.Year : DateTime.MinValue.Year,
|
||||||
|
Date.Month > 0 ? Date.Month : DateTime.MinValue.Month,
|
||||||
|
Date.Day > 0 ? Date.Day : DateTime.MinValue.Day,
|
||||||
|
Time.Hour > 0 ? Time.Hour : DateTime.MinValue.Hour,
|
||||||
|
Time.Minute > 0 ? Time.Minute : DateTime.MinValue.Minute,
|
||||||
|
Time.Second > 0 ? Time.Second : DateTime.MinValue.Second,
|
||||||
|
Time.Millisecond > 0 ? Time.Millisecond : DateTime.MinValue.Millisecond
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return String.Format("{0} {1}", _date, _time);
|
return String.Format("{0} {1}", _date, _time);
|
||||||
}
|
}
|
||||||
|
@@ -98,13 +98,25 @@ namespace Orchard.Localization.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual string FormatDateTime(DateTimeParts parts) {
|
public virtual string FormatDateTime(DateTimeParts parts) {
|
||||||
// TODO: Mahsa should implement!
|
return FormatDateTime(parts, _dateTimeFormatProvider.ShortDateTimeFormat);
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string FormatDateTime(DateTimeParts parts, string format) {
|
public virtual string FormatDateTime(DateTimeParts parts, string format) {
|
||||||
// TODO: Mahsa should implement!
|
var useMonthNameGenitive = GetUseGenitiveMonthName(format);
|
||||||
throw new NotImplementedException();
|
|
||||||
|
var replacements = GetDateFormatReplacements(useMonthNameGenitive).Concat(GetTimeFormatReplacements()).ToDictionary(item => item.Key, item => item.Value);
|
||||||
|
var formatString = ConvertToFormatString(format, replacements);
|
||||||
|
var calendar = CurrentCalendar;
|
||||||
|
|
||||||
|
var dateTime = parts.ToDateTime();
|
||||||
|
|
||||||
|
int twoDigitYear, hour12;
|
||||||
|
bool isPm;
|
||||||
|
string monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, dayName, dayNameShort, amPm, amPmShort, timeZone;
|
||||||
|
GetDateFormatValues(parts.Date, calendar, dateTime, out twoDigitYear, out monthName, out monthNameShort, out monthNameGenitive, out monthNameShortGenitive, out dayName, out dayNameShort);
|
||||||
|
GetTimeFormatValues(parts.Time, out isPm, out hour12, out amPm, out amPmShort, out timeZone);
|
||||||
|
|
||||||
|
return String.Format(formatString, parts.Date.Year, twoDigitYear, parts.Date.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Date.Day, dayName, dayNameShort, parts.Time.Hour, hour12, parts.Time.Minute, parts.Time.Second, parts.Time.Millisecond, amPm, amPmShort, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string FormatDate(DateParts parts) {
|
public virtual string FormatDate(DateParts parts) {
|
||||||
@@ -120,15 +132,10 @@ namespace Orchard.Localization.Services {
|
|||||||
|
|
||||||
var dateTime = parts.ToDateTime();
|
var dateTime = parts.ToDateTime();
|
||||||
|
|
||||||
var yearString = parts.Year.ToString("00", System.Globalization.CultureInfo.InvariantCulture);
|
int twoDigitYear;
|
||||||
var twoDigitYear = Int32.Parse(yearString.Substring(yearString.Length - 2));
|
string monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, dayName, dayNameShort;
|
||||||
var monthName = parts.Month > 0 ? _dateTimeFormatProvider.MonthNames[parts.Month - 1] : null;
|
GetDateFormatValues(parts, calendar, dateTime, out twoDigitYear, out monthName, out monthNameShort, out monthNameGenitive, out monthNameShortGenitive, out dayName, out dayNameShort);
|
||||||
var monthNameShort = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesShort[parts.Month - 1] : null;
|
|
||||||
var monthNameGenitive = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesGenitive[parts.Month - 1] : null;
|
|
||||||
var monthNameShortGenitive = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesShortGenitive[parts.Month - 1] : null;
|
|
||||||
var dayName = parts.Day > 0 ? _dateTimeFormatProvider.DayNames[(int)calendar.GetDayOfWeek(dateTime)] : null;
|
|
||||||
var dayNameShort = parts.Day > 0 ? _dateTimeFormatProvider.DayNamesShort[(int)calendar.GetDayOfWeek(parts.ToDateTime())] : null;
|
|
||||||
|
|
||||||
return String.Format(formatString, parts.Year, twoDigitYear, parts.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Day, dayName, dayNameShort);
|
return String.Format(formatString, parts.Year, twoDigitYear, parts.Month, monthName, monthNameShort, monthNameGenitive, monthNameShortGenitive, parts.Day, dayName, dayNameShort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,11 +150,11 @@ namespace Orchard.Localization.Services {
|
|||||||
var dateTime = parts.ToDateTime();
|
var dateTime = parts.ToDateTime();
|
||||||
|
|
||||||
bool isPm;
|
bool isPm;
|
||||||
var hour12 = ConvertToHour12(parts.Hour, out isPm);
|
int hour12;
|
||||||
var amPm = _dateTimeFormatProvider.AmPmDesignators[isPm ? 1 : 0];
|
string amPm, amPmShort, timeZone;
|
||||||
var amPmShort = String.IsNullOrEmpty(amPm) ? "" : amPm[0].ToString();
|
GetTimeFormatValues(parts, out isPm, out hour12, out amPm, out amPmShort, out timeZone);
|
||||||
|
|
||||||
return String.Format(formatString, parts.Hour, hour12, parts.Minute, parts.Second, parts.Millisecond, amPm, amPmShort);
|
return String.Format(formatString, null, null, null, null, null, null, null, null, null, null, parts.Hour, hour12, parts.Minute, parts.Second, parts.Millisecond, amPm, amPmShort, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual DateTimeParts? TryParseDateTime(string dateTimeString, string format, IDictionary<string, string> replacements) {
|
protected virtual DateTimeParts? TryParseDateTime(string dateTimeString, string format, IDictionary<string, string> replacements) {
|
||||||
@@ -253,6 +260,24 @@ namespace Orchard.Localization.Services {
|
|||||||
return new TimeParts(hour, minute, second, millisecond);
|
return new TimeParts(hour, minute, second, millisecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void GetDateFormatValues(DateParts parts, Calendar calendar, DateTime dateTime, out int twoDigitYear, out string monthName, out string monthNameShort, out string monthNameGenitive, out string monthNameShortGenitive, out string dayName, out string dayNameShort) {
|
||||||
|
var yearString = parts.Year.ToString("00", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
twoDigitYear = Int32.Parse(yearString.Substring(yearString.Length - 2));
|
||||||
|
monthName = parts.Month > 0 ? _dateTimeFormatProvider.MonthNames[parts.Month - 1] : null;
|
||||||
|
monthNameShort = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesShort[parts.Month - 1] : null;
|
||||||
|
monthNameGenitive = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesGenitive[parts.Month - 1] : null;
|
||||||
|
monthNameShortGenitive = parts.Month > 0 ? _dateTimeFormatProvider.MonthNamesShortGenitive[parts.Month - 1] : null;
|
||||||
|
dayName = parts.Day > 0 ? _dateTimeFormatProvider.DayNames[(int)calendar.GetDayOfWeek(dateTime)] : null;
|
||||||
|
dayNameShort = parts.Day > 0 ? _dateTimeFormatProvider.DayNamesShort[(int)calendar.GetDayOfWeek(parts.ToDateTime())] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void GetTimeFormatValues(TimeParts parts, out bool isPm, out int hour12, out string amPm, out string amPmShort, out string timeZone) {
|
||||||
|
hour12 = ConvertToHour12(parts.Hour, out isPm);
|
||||||
|
amPm = _dateTimeFormatProvider.AmPmDesignators[isPm ? 1 : 0];
|
||||||
|
amPmShort = String.IsNullOrEmpty(amPm) ? "" : amPm[0].ToString();
|
||||||
|
timeZone = ""; // TODO: Add time zone information to TimeParts and print this out correctly.
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual bool GetUseGenitiveMonthName(string format) {
|
protected virtual bool GetUseGenitiveMonthName(string format) {
|
||||||
// Use genitive month name if the format (excluding literals) contains a numerical day component (d or dd).
|
// Use genitive month name if the format (excluding literals) contains a numerical day component (d or dd).
|
||||||
var formatWithoutLiterals = Regex.Replace(format, @"(?<!\\)'(.*?)(?<!\\)'|(?<!\\)""(.*?)(?<!\\)""", "", RegexOptions.Compiled);
|
var formatWithoutLiterals = Regex.Replace(format, @"(?<!\\)'(.*?)(?<!\\)'|(?<!\\)""(.*?)(?<!\\)""", "", RegexOptions.Compiled);
|
||||||
@@ -279,6 +304,8 @@ namespace Orchard.Localization.Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Dictionary<string, string> GetTimeParseReplacements() {
|
protected virtual Dictionary<string, string> GetTimeParseReplacements() {
|
||||||
|
var amDesignator = _dateTimeFormatProvider.AmPmDesignators[0];
|
||||||
|
var pmDesignator = _dateTimeFormatProvider.AmPmDesignators[1];
|
||||||
return new Dictionary<string, string>() {
|
return new Dictionary<string, string>() {
|
||||||
{"HH", "(?<hour24>[0-9]{2})"},
|
{"HH", "(?<hour24>[0-9]{2})"},
|
||||||
{"H", "(?<hour24>[0-9]{1,2})"},
|
{"H", "(?<hour24>[0-9]{1,2})"},
|
||||||
@@ -295,22 +322,22 @@ namespace Orchard.Localization.Services {
|
|||||||
{"fff", "(?<millisecond>[0-9]{3})"},
|
{"fff", "(?<millisecond>[0-9]{3})"},
|
||||||
{"ff", "(?<millisecond>[0-9]{2})"},
|
{"ff", "(?<millisecond>[0-9]{2})"},
|
||||||
{"f", "(?<millisecond>[0-9]{1})"},
|
{"f", "(?<millisecond>[0-9]{1})"},
|
||||||
{"tt", String.Format(@"\s*(?<amPm>{0}|{1})\s*", EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators[0]), EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators[1]))},
|
{"tt", String.Format(@"\s*(?<amPm>{0}|{1})\s*", EscapeForRegex(amDesignator), EscapeForRegex(pmDesignator))},
|
||||||
{"t", String.Format(@"\s*(?<amPm>{0}|{1})\s*", EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators[0][0].ToString()), EscapeForRegex(_dateTimeFormatProvider.AmPmDesignators[1][0].ToString()))},
|
{"t", String.Format(@"\s*(?<amPm>{0}|{1})\s*", String.IsNullOrEmpty(amDesignator) ? "" : EscapeForRegex(amDesignator[0].ToString()), String.IsNullOrEmpty(pmDesignator) ? "" : EscapeForRegex(pmDesignator[0].ToString()))},
|
||||||
{"K", @"(?<timezone>Z|(\+|-)[0-9]{2}:[0-9]{2})*"}
|
{"K", @"(?<timezone>Z|(\+|-)[0-9]{2}:[0-9]{2})*"}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Dictionary<string, string> GetDateFormatReplacements(bool useMonthNameGenitive) {
|
protected virtual Dictionary<string, string> GetDateFormatReplacements(bool useMonthNameGenitive) {
|
||||||
return new Dictionary<string, string>() {
|
return new Dictionary<string, string>() {
|
||||||
{"dddd", "{8:dddd}"},
|
{"dddd", "{8}"},
|
||||||
{"ddd", "{9:ddd}"},
|
{"ddd", "{9}"},
|
||||||
{"dd", "{7:00}"},
|
{"dd", "{7:00}"},
|
||||||
{"d", "{7:##}"},
|
{"d", "{7:##}"},
|
||||||
{"MMMM", useMonthNameGenitive ? "{5:MMMM}" : "{3:MMMM}"},
|
{"MMMM", useMonthNameGenitive ? "{5}" : "{3}"},
|
||||||
// The .NET formatting logic never uses the abbreviated genitive month name; doing the same for compatibility.
|
// The .NET formatting logic never uses the abbreviated genitive month name; doing the same for compatibility.
|
||||||
//{"MMM", useMonthNameGenitive ? "{6:MMM}" : "{4:MMM}"},
|
//{"MMM", useMonthNameGenitive ? "{6}" : "{4}"},
|
||||||
{"MMM", "{4:MMM}"},
|
{"MMM", "{4}"},
|
||||||
{"MM", "{2:00}"},
|
{"MM", "{2:00}"},
|
||||||
{"M", "{2:##}"},
|
{"M", "{2:##}"},
|
||||||
{"yyyyy", "{0:00000}"},
|
{"yyyyy", "{0:00000}"},
|
||||||
@@ -323,30 +350,31 @@ namespace Orchard.Localization.Services {
|
|||||||
|
|
||||||
protected virtual Dictionary<string, string> GetTimeFormatReplacements() {
|
protected virtual Dictionary<string, string> GetTimeFormatReplacements() {
|
||||||
return new Dictionary<string, string>() {
|
return new Dictionary<string, string>() {
|
||||||
{"HH", "{0:00}"},
|
{"HH", "{10:00}"},
|
||||||
{"H", "{0:#0}"},
|
{"H", "{10:#0}"},
|
||||||
{"hh", "{1:00}"},
|
{"hh", "{11:00}"},
|
||||||
{"h", "{1:#0}"},
|
{"h", "{11:#0}"},
|
||||||
{"mm", "{2:00}"},
|
{"mm", "{12:00}"},
|
||||||
{"m", "{2:#0}"},
|
{"m", "{12:#0}"},
|
||||||
{"ss", "{3:00}"},
|
{"ss", "{13:00}"},
|
||||||
{"s", "{3:#0}"},
|
{"s", "{13:#0}"},
|
||||||
{"fffffff", "{4:0000000}"},
|
{"fffffff", "{14:0000000}"},
|
||||||
{"ffffff", "{4:000000}"},
|
{"ffffff", "{14:000000}"},
|
||||||
{"fffff", "{4:00000}"},
|
{"fffff", "{14:00000}"},
|
||||||
{"ffff", "{4:0000}"},
|
{"ffff", "{14:0000}"},
|
||||||
{"fff", "{4:000}"},
|
{"fff", "{14:000}"},
|
||||||
{"ff", "{4:00}"},
|
{"ff", "{14:00}"},
|
||||||
{"f", "{4:0}"},
|
{"f", "{14:0}"},
|
||||||
{"FFFFFFF", "{4:#######}"},
|
{"FFFFFFF", "{14:#######}"},
|
||||||
{"FFFFFF", "{4:######}"},
|
{"FFFFFF", "{14:######}"},
|
||||||
{"FFFFF", "{4:#####}"},
|
{"FFFFF", "{14:#####}"},
|
||||||
{"FFFF", "{4:####}"},
|
{"FFFF", "{14:####}"},
|
||||||
{"FFF", "{4:###}"},
|
{"FFF", "{14:###}"},
|
||||||
{"FF", "{4:##}"},
|
{"FF", "{14:##}"},
|
||||||
{"F", "{4:#}"},
|
{"F", "{14:#}"},
|
||||||
{"tt", "{5}"},
|
{"tt", "{15}"},
|
||||||
{"t", "{6}"}
|
{"t", "{16}"},
|
||||||
|
{"K", "{17}"}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -41,8 +41,8 @@ struct DateLocalizationOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
* Implement formatting in addition to parsing
|
|
||||||
* Test for proper handling of fraction (f) format specifier - suspect it does not work properly in current state
|
* Test for proper handling of fraction (f) format specifier - suspect it does not work properly in current state
|
||||||
|
* Add formatting and parsing of time zone information (add timezone properties to TimeParts structure)
|
||||||
* 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
|
||||||
|
Reference in New Issue
Block a user