Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Exceptionless.DateTimeExtensions/TimeUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ public static bool TryParse(string value, out TimeSpan? time)

// compare using the original value as uppercase M could mean months.
string normalized = value.Trim();

// Handle years (y) - using average days in a year
if (value.EndsWith("y") && Int32.TryParse(normalized.Substring(0, normalized.Length - 1), out int years))
return new TimeSpan((int)(years * TimeSpanExtensions.AvgDaysInAYear), 0, 0, 0);

// Handle months (M) - using average days in a month, case-sensitive uppercase M
if (value.EndsWith("M") && Int32.TryParse(normalized.Substring(0, normalized.Length - 1), out int months))
return new TimeSpan((int)(months * TimeSpanExtensions.AvgDaysInAMonth), 0, 0, 0);

// Handle weeks (w)
if (value.EndsWith("w") && Int32.TryParse(normalized.Substring(0, normalized.Length - 1), out int weeks))
return new TimeSpan(weeks * 7, 0, 0, 0);

// Handle minutes (m) - lowercase m for minutes
if (value.EndsWith("m") && Int32.TryParse(normalized.Substring(0, normalized.Length - 1), out int minutes))
return new TimeSpan(0, minutes, 0);

Expand Down
63 changes: 63 additions & 0 deletions tests/Exceptionless.DateTimeExtensions.Tests/TimeUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ public class TimeUnitTests
["10m", new TimeSpan(0, 10, 0)],
["10h", new TimeSpan(10, 0, 0)],
["10d", new TimeSpan(10, 0, 0, 0)],
["1w", new TimeSpan(7, 0, 0, 0)],
["2w", new TimeSpan(14, 0, 0, 0)],
["-1w", new TimeSpan(-7, 0, 0, 0)],
["1M", new TimeSpan((int)TimeSpanExtensions.AvgDaysInAMonth, 0, 0, 0)],
["2M", new TimeSpan((int)(2 * TimeSpanExtensions.AvgDaysInAMonth), 0, 0, 0)],
["-1M", new TimeSpan((int)(-1 * TimeSpanExtensions.AvgDaysInAMonth), 0, 0, 0)],
["1y", new TimeSpan((int)TimeSpanExtensions.AvgDaysInAYear, 0, 0, 0)],
["2y", new TimeSpan((int)(2 * TimeSpanExtensions.AvgDaysInAYear), 0, 0, 0)],
["-1y", new TimeSpan((int)(-1 * TimeSpanExtensions.AvgDaysInAYear), 0, 0, 0)],
};

[Theory]
Expand Down Expand Up @@ -50,6 +59,15 @@ public void VerifyParseFailure(string value)
[InlineData("10m", true)]
[InlineData("10h", true)]
[InlineData("10d", true)]
[InlineData("1w", true)]
[InlineData("2w", true)]
[InlineData("-1w", true)]
[InlineData("1M", true)]
[InlineData("2M", true)]
[InlineData("-1M", true)]
[InlineData("1y", true)]
[InlineData("2y", true)]
[InlineData("-1y", true)]
[InlineData(null, false)]
[InlineData("1.234h", false)] // fractional time
[InlineData("1234", false)] // missing unit
Expand All @@ -61,4 +79,49 @@ public void VerifyTryParse(string value, bool expected)
bool success = TimeUnit.TryParse(value, out var result);
Assert.Equal(expected, success);
}

[Fact]
public void VerifyMonthsVsMinutesCaseSensitive()
{
// Uppercase M should be months
var monthResult = TimeUnit.Parse("1M");
var expectedMonthDays = (int)TimeSpanExtensions.AvgDaysInAMonth;
Assert.Equal(new TimeSpan(expectedMonthDays, 0, 0, 0), monthResult);

// Lowercase m should be minutes
var minuteResult = TimeUnit.Parse("1m");
Assert.Equal(new TimeSpan(0, 1, 0), minuteResult);

// Verify they are different
Assert.NotEqual(monthResult, minuteResult);
}

[Theory]
[InlineData("1y", 365)] // Approximately 365 days in a year
[InlineData("1M", 30)] // Approximately 30 days in a month
[InlineData("1w", 7)] // Exactly 7 days in a week
public void VerifyNewTimeUnitsConvertCorrectly(string input, int expectedApproxDays)
{
var result = TimeUnit.Parse(input);

// For years and months, check approximate values due to fractional constants
if (input.EndsWith("y"))
{
Assert.True(Math.Abs(result.TotalDays - TimeSpanExtensions.AvgDaysInAYear) < 1,
$"Year conversion should be close to {TimeSpanExtensions.AvgDaysInAYear} days, got {result.TotalDays}");
Assert.True(Math.Abs(result.TotalDays - expectedApproxDays) < 10,
$"Year conversion should be approximately {expectedApproxDays} days, got {result.TotalDays}");
}
else if (input.EndsWith("M"))
{
Assert.True(Math.Abs(result.TotalDays - TimeSpanExtensions.AvgDaysInAMonth) < 1,
$"Month conversion should be close to {TimeSpanExtensions.AvgDaysInAMonth} days, got {result.TotalDays}");
Assert.True(Math.Abs(result.TotalDays - expectedApproxDays) < 5,
$"Month conversion should be approximately {expectedApproxDays} days, got {result.TotalDays}");
}
else if (input.EndsWith("w"))
{
Assert.Equal(expectedApproxDays, result.TotalDays);
}
}
}