-
Notifications
You must be signed in to change notification settings - Fork 965
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #399 from tompazourek/datetimeoffset
Support for humanizing DateTimeOffset
- Loading branch information
Showing
14 changed files
with
317 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using Humanizer.Configuration; | ||
using Humanizer.DateTimeHumanizeStrategy; | ||
using Xunit; | ||
|
||
namespace Humanizer.Tests | ||
{ | ||
public class DateTimeOffsetHumanizeTests : AmbientCulture | ||
{ | ||
public DateTimeOffsetHumanizeTests() : base("en-US") | ||
{ | ||
} | ||
|
||
[Fact] | ||
public void DefaultStrategy_SameOffset() | ||
{ | ||
Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeOffsetHumanizeStrategy(); | ||
|
||
var inputTime = new DateTimeOffset(2015, 07, 05, 04, 0, 0, TimeSpan.Zero); | ||
var baseTime = new DateTimeOffset(2015, 07, 05, 03, 0, 0, TimeSpan.Zero); | ||
|
||
const string expectedResult = "an hour from now"; | ||
var actualResult = inputTime.Humanize(baseTime); | ||
|
||
Assert.Equal(expectedResult, actualResult); | ||
} | ||
|
||
[Fact] | ||
public void DefaultStrategy_DifferentOffsets() | ||
{ | ||
Configurator.DateTimeOffsetHumanizeStrategy = new DefaultDateTimeOffsetHumanizeStrategy(); | ||
|
||
var inputTime = new DateTimeOffset(2015, 07, 05, 03, 0, 0, new TimeSpan(2, 0, 0)); | ||
var baseTime = new DateTimeOffset(2015, 07, 05, 02, 30, 0, new TimeSpan(1, 0, 0)); | ||
|
||
const string expectedResult = "30 minutes ago"; | ||
var actualResult = inputTime.Humanize(baseTime); | ||
|
||
Assert.Equal(expectedResult, actualResult); | ||
} | ||
|
||
[Fact] | ||
public void PrecisionStrategy_SameOffset() | ||
{ | ||
Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeOffsetHumanizeStrategy(0.75); | ||
|
||
var inputTime = new DateTimeOffset(2015, 07, 05, 04, 0, 0, TimeSpan.Zero); | ||
var baseTime = new DateTimeOffset(2015, 07, 04, 05, 0, 0, TimeSpan.Zero); | ||
|
||
const string expectedResult = "tomorrow"; | ||
var actualResult = inputTime.Humanize(baseTime); | ||
|
||
Assert.Equal(expectedResult, actualResult); | ||
} | ||
|
||
[Fact] | ||
public void PrecisionStrategy_DifferentOffsets() | ||
{ | ||
Configurator.DateTimeOffsetHumanizeStrategy = new PrecisionDateTimeOffsetHumanizeStrategy(0.75); | ||
|
||
var inputTime = new DateTimeOffset(2015, 07, 05, 03, 45, 0, new TimeSpan(2, 0, 0)); | ||
var baseTime = new DateTimeOffset(2015, 07, 05, 02, 30, 0, new TimeSpan(-5, 0, 0)); | ||
|
||
const string expectedResult = "6 hours ago"; | ||
var actualResult = inputTime.Humanize(baseTime); | ||
|
||
Assert.Equal(expectedResult, actualResult); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
src/Humanizer/DateTimeHumanizeStrategy/DateTimeHumanizeAlgorithms.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
using System; | ||
using System.Globalization; | ||
using Humanizer.Configuration; | ||
using Humanizer.Localisation; | ||
|
||
namespace Humanizer.DateTimeHumanizeStrategy | ||
{ | ||
/// <summary> | ||
/// Algorithms used to convert distance between two dates into words. | ||
/// </summary> | ||
internal static class DateTimeHumanizeAlgorithms | ||
{ | ||
/// <summary> | ||
/// Returns localized & humanized distance of time between two dates; given a specific precision. | ||
/// </summary> | ||
public static string PrecisionHumanize(DateTime input, DateTime comparisonBase, double precision, CultureInfo culture) | ||
{ | ||
var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); | ||
var tense = input > comparisonBase ? Tense.Future : Tense.Past; | ||
|
||
int seconds = ts.Seconds, minutes = ts.Minutes, hours = ts.Hours, days = ts.Days; | ||
int years = 0, months = 0; | ||
|
||
// start approximate from smaller units towards bigger ones | ||
if (ts.Milliseconds >= 999 * precision) seconds += 1; | ||
if (seconds >= 59 * precision) minutes += 1; | ||
if (minutes >= 59 * precision) hours += 1; | ||
if (hours >= 23 * precision) days += 1; | ||
|
||
// month calculation | ||
if (days >= 30 * precision & days <= 31) months = 1; | ||
if (days > 31 && days < 365 * precision) | ||
{ | ||
int factor = Convert.ToInt32(Math.Floor((double)days / 30)); | ||
int maxMonths = Convert.ToInt32(Math.Ceiling((double)days / 30)); | ||
months = (days >= 30 * (factor + precision)) ? maxMonths : maxMonths - 1; | ||
} | ||
|
||
// year calculation | ||
if (days >= 365 * precision && days <= 366) years = 1; | ||
if (days > 365) | ||
{ | ||
int factor = Convert.ToInt32(Math.Floor((double)days / 365)); | ||
int maxMonths = Convert.ToInt32(Math.Ceiling((double)days / 365)); | ||
years = (days >= 365 * (factor + precision)) ? maxMonths : maxMonths - 1; | ||
} | ||
|
||
// start computing result from larger units to smaller ones | ||
var formatter = Configurator.GetFormatter(culture); | ||
if (years > 0) return formatter.DateHumanize(TimeUnit.Year, tense, years); | ||
if (months > 0) return formatter.DateHumanize(TimeUnit.Month, tense, months); | ||
if (days > 0) return formatter.DateHumanize(TimeUnit.Day, tense, days); | ||
if (hours > 0) return formatter.DateHumanize(TimeUnit.Hour, tense, hours); | ||
if (minutes > 0) return formatter.DateHumanize(TimeUnit.Minute, tense, minutes); | ||
if (seconds > 0) return formatter.DateHumanize(TimeUnit.Second, tense, seconds); | ||
return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); | ||
} | ||
|
||
// http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time | ||
/// <summary> | ||
/// Calculates the distance of time in words between two provided dates | ||
/// </summary> | ||
public static string DefaultHumanize(DateTime input, DateTime comparisonBase, CultureInfo culture) | ||
{ | ||
var tense = input > comparisonBase ? Tense.Future : Tense.Past; | ||
var ts = new TimeSpan(Math.Abs(comparisonBase.Ticks - input.Ticks)); | ||
|
||
var formatter = Configurator.GetFormatter(culture); | ||
|
||
if (ts.TotalMilliseconds < 500) | ||
return formatter.DateHumanize(TimeUnit.Millisecond, tense, 0); | ||
|
||
if (ts.TotalSeconds < 60) | ||
return formatter.DateHumanize(TimeUnit.Second, tense, ts.Seconds); | ||
|
||
if (ts.TotalSeconds < 120) | ||
return formatter.DateHumanize(TimeUnit.Minute, tense, 1); | ||
|
||
if (ts.TotalMinutes < 60) | ||
return formatter.DateHumanize(TimeUnit.Minute, tense, ts.Minutes); | ||
|
||
if (ts.TotalMinutes < 90) | ||
return formatter.DateHumanize(TimeUnit.Hour, tense, 1); | ||
|
||
if (ts.TotalHours < 24) | ||
return formatter.DateHumanize(TimeUnit.Hour, tense, ts.Hours); | ||
|
||
if (ts.TotalHours < 48) | ||
{ | ||
var days = Math.Abs((input.Date - comparisonBase.Date).Days); | ||
return formatter.DateHumanize(TimeUnit.Day, tense, days); | ||
} | ||
|
||
if (ts.TotalDays < 28) | ||
return formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); | ||
|
||
if (ts.TotalDays >= 28 && ts.TotalDays < 30) | ||
{ | ||
if (comparisonBase.Date.AddMonths(tense == Tense.Future ? 1 : -1) == input.Date) | ||
return formatter.DateHumanize(TimeUnit.Month, tense, 1); | ||
return formatter.DateHumanize(TimeUnit.Day, tense, ts.Days); | ||
} | ||
|
||
if (ts.TotalDays < 345) | ||
{ | ||
int months = Convert.ToInt32(Math.Floor(ts.TotalDays / 29.5)); | ||
return formatter.DateHumanize(TimeUnit.Month, tense, months); | ||
} | ||
|
||
int years = Convert.ToInt32(Math.Floor(ts.TotalDays / 365)); | ||
if (years == 0) years = 1; | ||
|
||
return formatter.DateHumanize(TimeUnit.Year, tense, years); | ||
} | ||
} | ||
} |
Oops, something went wrong.