Skip to content

Commit

Permalink
Refactor TimeFormatter (axuno#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
axunonb authored Oct 24, 2021
1 parent 5a08024 commit 4aa4e37
Show file tree
Hide file tree
Showing 11 changed files with 519 additions and 244 deletions.
51 changes: 51 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,57 @@ SmartFormat is not a fully-fledged HTML parser. If this is required, use [AngleS
b) Get the culture from the `IFormatProvider` argument (which may be a `CultureInfo`) to `SmartFormatter.Format(IFormatProvider, string, object?[])`<br/>
c) The `CultureInfo.CurrentUICulture`<br/>

### 19. Refactored `TimeFormatter`

* Constructor with string argument for default language is obsolete.
* Property `DefaultTwoLetterISOLanguageName` is obsolete.
* Culture is now determined in this sequence (same as with `LocalizationFormatter` and `PluralLocalizationFormatter`):<br/>
a) Get the culture from the `FormattingInfo.FormatterOptions`.<br/>
b) Get the culture from the `IFormatProvider` argument (which may be a `CultureInfo`) to `SmartFormatter.Format(IFormatProvider, string, object?[])`<br/>
c) The `CultureInfo.CurrentUICulture`<br/>
* **New:** With the extended `CommonLanguagesTimeTextInfo`, `TimeFormatter` includes French, Spanish, Portuguese, Italian and German as new languages besides English out-of-the-box.
* **New:** With `TimeFormatter.UseEnglishAsFallbackLanguage = true`, English will be used, if no supported language was found.
* **New:** Custom languages can now easily be added to `CommonLanguagesTimeTextInfo`. Custom languages override built-in definitions.
```CSharp
var language = "nl"; // dummy - it's English, not Dutch ;-)
TimeTextInfo custom = new(
pluralRule: PluralRules.GetPluralRule(language),
week: new[] { "{0} week", "{0} weeks" },
day: new[] { "{0} day", "{0} days" },
hour: new[] { "{0} hour", "{0} hours" },
minute: new[] { "{0} minute", "{0} minutes" },
second: new[] { "{0} second", "{0} seconds" },
millisecond: new[] { "{0} millisecond", "{0} milliseconds" },
w: new[] { "{0}w" },
d: new[] { "{0}d" },
h: new[] { "{0}h" },
m: new[] { "{0}m" },
s: new[] { "{0}s" },
ms: new[] { "{0}ms" },
lessThan: "less than {0}");
CommonLanguagesTimeTextInfo.AddLanguage(language, custom)
```
* **Changed:**
a) This notation - using formats as formatter options - was allowed in *Smart.Format v2.x*, but is now depreciated. It is still detected and working, as long as the format part is left empty
```CSharp
var formatDepreciated = "{0:time(abbr hours noless)}";
```
b) This format string is recommended for *Smart.Format v3* and later. It allows for including the language as an option to the `TimeFormatter`:
```CSharp
// Without language option:
var formatRecommended = "{0:time:abbr hours noless:}";
// With language option:
var formatRecommended = "{0:time(en):abbr hours noless:}";
```
* PRs for extending built-in languages are welcome.
* Example:
```Csharp
var timeSpan = new TimeSpan(1,1,1,1,1)
Smart.Format("{0:time(en):hours minutes}", timeSpan);
// result: "25 hours 1 minute"
Smart.Format("{0:time(fr):hours minutes}", timeSpan);
// result: "25 heures 1 minute"
```

v2.7.1
===
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ for:
- dotnet add ./SmartFormat.Tests/SmartFormat.Tests.csproj package AltCover
- dotnet build SmartFormat.sln /verbosity:minimal /t:rebuild /p:configuration=release /nowarn:CS1591,CS0618
test_script:
- dotnet test --no-build --framework net5.0 SmartFormat.sln /p:configuration=release /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCoverInplace=true /p:AltCoverForce=true /p:AltCoverStrongNameKey="../SmartFormat/SmartFormat.snk" /p:AltCoverAssemblyExcludeFilter="SmartFormat.Tests|NUnit3.TestAdapter|Cysharp" /p:AltCoverLineCover="true"
- dotnet test --no-build --framework net5.0 SmartFormat.sln /p:configuration=release /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCover=true /p:AltCoverStrongNameKey="..\SmartFormat\SmartFormat.snk" /p:AltCoverAssemblyExcludeFilter="SmartFormat.Tests|NUnit3.TestAdapter" /p:AltCoverLineCover="true"
- bash <(curl -s https://codecov.io/bash) -f ./SmartFormat.Tests/coverage.net5.0.xml -n net5.0linux

2 changes: 1 addition & 1 deletion src/SmartFormat.Tests/Extensions/ListFormatterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public void NestedFormatTest(string format, string expected)
smart.Test(new[] {format}, args, new[] {expected});
}
[TestCase("{2:list:{:{FirstName}}|, }", "Jim, Pam, Dwight")]
[TestCase("{3:list:{:M/d/yyyy} |}", "1/1/2000 10/10/2010 5/5/5555 ")]
[TestCase("{3:list:{:d:M/d/yyyy} |}", "1/1/2000 10/10/2010 5/5/5555 ")] // use the default formatter ("d") for nested dates
[TestCase("{2:list:{:{FirstName}'s friends: {Friends:list:{FirstName}|, }}|; }", "Jim's friends: Dwight, Michael; Pam's friends: Dwight, Michael; Dwight's friends: Michael")]
public void NestedArraysTest(string format, string expected)
{
Expand Down
116 changes: 9 additions & 107 deletions src/SmartFormat.Tests/Extensions/SmartExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,121 +27,23 @@ public static object[] GetArgs()
[Test]
public void Test_AppendLine()
{
var formats = new[]
{
"{0:time:}",
"{1:time:}",
"{2:time:}",
"{3:time:}",
"{4:time:}",
"{5:time:}"
};
var expected = new[]
{
"less than 1 second" + Environment.NewLine,
"1 day 1 hour 1 minute 1 second" + Environment.NewLine,
"2 hours 2 seconds" + Environment.NewLine,
"3 days 3 seconds" + Environment.NewLine,
"less than 1 second" + Environment.NewLine,
"5 days" + Environment.NewLine
};
var args = GetArgs();
var sb = new StringBuilder();
sb.AppendLineSmart("{0} {1} {2} {3}", "these", "are", "the", "args");

TestAppendLine(formats, args, expected);
var actual = sb.ToString();

Assert.That(actual, Is.EqualTo("these are the args" + Environment.NewLine));
}

[Test]
public void Test_Append()
{
var formats = new [] {
"{0:time:noless}",
"{1:time:hours}",
"{1:time:hours minutes}",
"{2:time:days milliseconds}",
"{2:time:days milliseconds auto}",
"{2:time:days milliseconds short}",
"{2:time:days milliseconds fill}",
"{2:time:days milliseconds full}",
"{3:time:abbr}",
};
var expected = new [] {
"0 seconds",
"25 hours",
"25 hours 1 minute",
"2 hours 2 seconds",
"2 hours 2 seconds",
"2 hours",
"2 hours 0 minutes 2 seconds 0 milliseconds",
"0 days 2 hours 0 minutes 2 seconds 0 milliseconds",
"3d 3s",
};
var args = GetArgs();

TestAppend(formats, args, expected);
}
var sb = new StringBuilder();
sb.AppendSmart("{0} {1} {2} {3}", "these", "are", "the", "args");

public static void TestAppend(string[] bunchOfFormat, object[] args, string[] bunchOfExpected)
{
var allErrors = new ExceptionCollection();

var numberOfTests = Math.Max(bunchOfFormat.Length, bunchOfExpected.Length);
for (int i = 0; i < numberOfTests; i++)
{
var format = bunchOfFormat[i % bunchOfFormat.Length];
var expected = bunchOfExpected[i % bunchOfExpected.Length];

string actual = string.Empty;

try
{
var builder = new StringBuilder();
builder.AppendSmart(format, args);

actual = builder.ToString();

Assert.AreEqual(expected, actual);
Console.WriteLine("Success: \"{0}\" => \"{1}\"", format, actual);
}
catch (Exception ex)
{
Console.WriteLine("Error: \"{0}\" => \"{1}\"", format, actual);
allErrors.Add(ex);
}
}

allErrors.ThrowIfNotEmpty();
}
var actual = sb.ToString();

public static void TestAppendLine(string[] bunchOfFormat, object[] args, string[] bunchOfExpected)
{
var allErrors = new ExceptionCollection();

var numberOfTests = Math.Max(bunchOfFormat.Length, bunchOfExpected.Length);
for (int i = 0; i < numberOfTests; i++)
{
var format = bunchOfFormat[i % bunchOfFormat.Length];
var expected = bunchOfExpected[i % bunchOfExpected.Length];

string? actual = null;

try
{
var builder = new StringBuilder();
builder.AppendLineSmart(format, args);

actual = builder.ToString();

Assert.AreEqual(expected, actual);
Console.WriteLine("Success: \"{0}\" => \"{1}\"", format, actual);
}
catch (Exception ex)
{
Console.WriteLine("Error: \"{0}\" => \"{1}\"", format, actual);
allErrors.Add(ex);
}
}

allErrors.ThrowIfNotEmpty();
Assert.That(actual, Is.EqualTo("these are the args"));
}

#endregion
Expand Down
Loading

0 comments on commit 4aa4e37

Please sign in to comment.