mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Integrated the IDateFormatter abstraction and a first draft implementation and unit tests.
This commit is contained in:
32
src/Orchard.Tests/Localization/DateParseResultTests.cs
Normal file
32
src/Orchard.Tests/Localization/DateParseResultTests.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Framework.Localization.Models;
|
||||
|
||||
namespace Orchard.Framework.Tests.Localization {
|
||||
|
||||
[TestFixture]
|
||||
public class DateParseResultTests {
|
||||
|
||||
[Test]
|
||||
[Description("Equal instances return equality.")]
|
||||
public void EqualsTest01() {
|
||||
var target = new DateTimeParts(2014, 5, 31, 10, 0, 0, 0);
|
||||
var other = new DateTimeParts(2014, 5, 31, 10, 0, 0, 0);
|
||||
|
||||
var result = target.Equals(other);
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description("Different instances do not return equality.")]
|
||||
public void EqualsTest02() {
|
||||
var target = new DateTimeParts(2014, 5, 31, 10, 0, 0, 0);
|
||||
var other = new DateTimeParts(2014, 5, 31, 10, 0, 0, 1);
|
||||
|
||||
var result = target.Equals(other);
|
||||
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
}
|
||||
}
|
65
src/Orchard.Tests/Localization/DefaultDateFormatterTests.cs
Normal file
65
src/Orchard.Tests/Localization/DefaultDateFormatterTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Framework.Localization.Models;
|
||||
using Orchard.Framework.Localization.Services;
|
||||
|
||||
namespace Orchard.Framework.Tests.Localization {
|
||||
|
||||
[TestFixture]
|
||||
public class DefaultDateFormatterTests {
|
||||
|
||||
[Test]
|
||||
[Description("Correct Swedish 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 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.")]
|
||||
[ExpectedException(typeof(FormatException))]
|
||||
public void ParseTest03() {
|
||||
IDateFormatter target = new DefaultDateFormatter();
|
||||
var cultureInfo = CultureInfo.GetCultureInfo("en-US");
|
||||
var dateString = "blablabla";
|
||||
|
||||
target.ParseDateTime(dateString, cultureInfo);
|
||||
}
|
||||
|
||||
[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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -259,6 +259,8 @@
|
||||
<Compile Include="FileSystems\Dependencies\DependenciesFolderTests.cs" />
|
||||
<Compile Include="FileSystems\VirtualPath\DefaultVirtualPathProviderTests.cs" />
|
||||
<Compile Include="Localization\CultureManagerTests.cs" />
|
||||
<Compile Include="Localization\DateParseResultTests.cs" />
|
||||
<Compile Include="Localization\DefaultDateFormatterTests.cs" />
|
||||
<Compile Include="Logging\OrchardFileAppenderTests.cs" />
|
||||
<Compile Include="Messaging\MessagingChannelStub.cs" />
|
||||
<Compile Include="Mvc\Html\HtmlHelperExtensionsTests.cs" />
|
||||
|
45
src/Orchard/Localization/Models/DateParts.cs
Normal file
45
src/Orchard/Localization/Models/DateParts.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Framework.Localization.Models {
|
||||
public struct DateParts {
|
||||
|
||||
public DateParts(int year, int month, int day) {
|
||||
_year = year;
|
||||
_month = month;
|
||||
_day = day;
|
||||
}
|
||||
|
||||
private readonly int _day;
|
||||
private readonly int _month;
|
||||
private readonly int _year;
|
||||
|
||||
public int Year {
|
||||
get {
|
||||
return _year;
|
||||
}
|
||||
}
|
||||
public int Month {
|
||||
get {
|
||||
return _month;
|
||||
}
|
||||
}
|
||||
public int Day {
|
||||
get {
|
||||
return _day;
|
||||
}
|
||||
}
|
||||
|
||||
//public override bool Equals(object obj) {
|
||||
// var other = (DateParts)obj;
|
||||
|
||||
// if (Year != other.Year ||
|
||||
// Month != other.Month ||
|
||||
// Day != other.Day)
|
||||
// return false;
|
||||
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
}
|
42
src/Orchard/Localization/Models/DateTimeParts.cs
Normal file
42
src/Orchard/Localization/Models/DateTimeParts.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Framework.Localization.Models {
|
||||
public struct DateTimeParts {
|
||||
|
||||
public DateTimeParts(int year, int month, int day, int hour, int minute, int second, int millisecond) {
|
||||
_date = new DateParts(year, month, day);
|
||||
_time = new TimeParts(hour, minute, second, millisecond);
|
||||
}
|
||||
|
||||
public DateTimeParts(DateParts dateParts, TimeParts timeParts) {
|
||||
_date = dateParts;
|
||||
_time = timeParts;
|
||||
}
|
||||
|
||||
private readonly DateParts _date;
|
||||
private readonly TimeParts _time;
|
||||
|
||||
public DateParts Date {
|
||||
get {
|
||||
return _date;
|
||||
}
|
||||
}
|
||||
public TimeParts Time {
|
||||
get {
|
||||
return _time;
|
||||
}
|
||||
}
|
||||
|
||||
//public override bool Equals(object obj) {
|
||||
// var other = (DateTimeParts)obj;
|
||||
|
||||
// if (!(Date.Equals(other.Date)) ||
|
||||
// !(Time.Equals(other.Time)))
|
||||
// return false;
|
||||
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
}
|
56
src/Orchard/Localization/Models/TimeParts.cs
Normal file
56
src/Orchard/Localization/Models/TimeParts.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Framework.Localization.Models {
|
||||
public struct TimeParts {
|
||||
|
||||
public TimeParts(int hour, int minute, int second, int millisecond) {
|
||||
_hour = hour;
|
||||
_minute = minute;
|
||||
_second = second;
|
||||
_millisecond = millisecond;
|
||||
}
|
||||
|
||||
private readonly int _hour;
|
||||
private readonly int _minute;
|
||||
private readonly int _second;
|
||||
private readonly int _millisecond;
|
||||
|
||||
public int Hour {
|
||||
get {
|
||||
return _hour;
|
||||
}
|
||||
}
|
||||
|
||||
public int Minute {
|
||||
get {
|
||||
return _minute;
|
||||
}
|
||||
}
|
||||
|
||||
public int Second {
|
||||
get {
|
||||
return _second;
|
||||
}
|
||||
}
|
||||
|
||||
public int Millisecond {
|
||||
get {
|
||||
return _millisecond;
|
||||
}
|
||||
}
|
||||
|
||||
//public override bool Equals(object obj) {
|
||||
// var other = (TimeParts)obj;
|
||||
|
||||
// if (Hour != other.Hour ||
|
||||
// Minute != other.Minute ||
|
||||
// Second != other.Second ||
|
||||
// Millisecond != other.Millisecond)
|
||||
// return false;
|
||||
|
||||
// return Hour == other.Hour;
|
||||
//}
|
||||
}
|
||||
}
|
141
src/Orchard/Localization/Services/DefaultDateFormatter.cs
Normal file
141
src/Orchard/Localization/Services/DefaultDateFormatter.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Orchard.Framework.Localization.Models;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
var dateFormat = ReplaceAll(dateFormatString, GetDateParseReplacements(culture));
|
||||
|
||||
if (!Regex.IsMatch(dateString, dateFormat, RegexOptions.IgnoreCase))
|
||||
throw new FormatException("Invalid date format.");
|
||||
|
||||
Match dateMatch = Regex.Match(dateString, dateFormat, RegexOptions.IgnoreCase);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int hour = 0,
|
||||
minute = 0,
|
||||
second = 0,
|
||||
millisecond = 0;
|
||||
|
||||
if (timeMatch.Groups["hour"].Success) {
|
||||
int.TryParse(timeMatch.Groups["hour"].Value, out hour);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
string IDateFormatter.FormatTime(TimeParts parts, CultureInfo culture) {
|
||||
// TODO: Mahsa should implement!
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetDateParseReplacements(CultureInfo culture) {
|
||||
return new Dictionary<string, string>() {
|
||||
{"dddd", "(?<day>[0-9]{4})"},
|
||||
{"ddd", "(?<day>[0-9]{3})"},
|
||||
{"dd", "(?<day>[0-9]{2})"},
|
||||
{"d", "(?<day>[0-9]{1,2})"},
|
||||
{"MMMM", String.Format("(?<month>{0})", String.Join("|", culture.DateTimeFormat.MonthNames))},
|
||||
{"MMM", String.Format("(?<month>{0})", String.Join("|", culture.DateTimeFormat.AbbreviatedMonthNames))},
|
||||
{"MM", "(?<month>[0-9]{2})"},
|
||||
{"M", "(?<month>[0-9]{1,2})"},
|
||||
{"yyyyy", "(?<year>[0-9]{5})"},
|
||||
{"yyyy", "(?<year>[0-9]{4})"},
|
||||
{"yyy", "(?<year>[0-9]{3})"},
|
||||
{"yy", "(?<year>[0-9]{2})"},
|
||||
{"y", "(?<year>[0-9]{1})"}
|
||||
};
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetTimeParseReplacements(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)}
|
||||
};
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
16
src/Orchard/Localization/Services/IDateFormatter.cs
Normal file
16
src/Orchard/Localization/Services/IDateFormatter.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Orchard.Framework.Localization.Models;
|
||||
|
||||
namespace Orchard.Framework.Localization.Services {
|
||||
public interface IDateFormatter {
|
||||
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);
|
||||
}
|
||||
}
|
@@ -261,8 +261,12 @@
|
||||
<Compile Include="FileSystems\Media\FileSystemStorageProvider.cs" />
|
||||
<Compile Include="FileSystems\Media\IMimeTypeProvider.cs" />
|
||||
<Compile Include="Indexing\ISearchBits.cs" />
|
||||
<Compile Include="Localization\Models\DateParts.cs" />
|
||||
<Compile Include="Localization\Models\DateTimeParts.cs" />
|
||||
<Compile Include="Localization\Models\TimeParts.cs" />
|
||||
<Compile Include="Localization\Services\CurrentCalendarWorkContext.cs" />
|
||||
<Compile Include="Localization\Services\CurrentCultureWorkContext.cs" />
|
||||
<Compile Include="Localization\Services\DefaultDateFormatter.cs" />
|
||||
<Compile Include="Localization\Services\DefaultCalendarManager.cs" />
|
||||
<Compile Include="Localization\Services\CultureDateTimeFormatProvider.cs" />
|
||||
<Compile Include="Localization\Services\DefaultDateLocalizationServices.cs" />
|
||||
@@ -270,6 +274,7 @@
|
||||
<Compile Include="Localization\Services\ICalendarSelector.cs" />
|
||||
<Compile Include="Localization\Services\ICalendarManager.cs" />
|
||||
<Compile Include="Localization\Services\IDateLocalizationServices.cs" />
|
||||
<Compile Include="Localization\Services\IDateFormatter.cs" />
|
||||
<Compile Include="Localization\Services\IDateTimeFormatProvider.cs" />
|
||||
<Compile Include="Localization\Services\ILocalizedStringManager.cs" />
|
||||
<Compile Include="Localization\Services\SiteCalendarSelector.cs" />
|
||||
|
Reference in New Issue
Block a user