Skip to content

Commit

Permalink
Fixed parsing of single/double digit years as an int
Browse files Browse the repository at this point in the history
  • Loading branch information
michal-ciechan committed Oct 14, 2024
1 parent 5af875b commit a2b8796
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 64 deletions.
81 changes: 81 additions & 0 deletions MikeysUtils3.UnitTests/DateTimeParsingSnapshotTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,87 @@ public async Task UniversaleSortableNoTNoMinutes()
"""
);
}

[Test]
public async Task DateIntSimple()

Check warning on line 139 in MikeysUtils3.UnitTests/DateTimeParsingSnapshotTests.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
var input = "20241023";

var results = Parse(input);

InlineSnapshot.Validate(
results,
"""
- DateTimeType: UnixSeconds
ParsedDateTime: 1970-08-23T06:30:23.0000000Z
Kind: Utc
- DateTimeType: UnixMilliseconds
ParsedDateTime: 1970-01-01T05:37:21.0230000Z
Kind: Utc
- DateTimeType: UnixNanoSeconds
ParsedDateTime: 1970-01-01T00:00:00.0202410Z
Kind: Utc
- DateTimeType: yyyyMMdd_DateBasic
ParsedDateTime: 2024-10-23T00:00:00.0000000
- DateTimeType: yMMdd_DateInt
ParsedDateTime: 2024-10-23T00:00:00.0000000
"""
);
}

[Test]
public async Task DateIntDoubleTripleDigitYear()
{
var input = "1691023";

var results = Parse(input);

InlineSnapshot.Validate(
results,
"""
- DateTimeType: DayNumber
ParsedDateTime: 4630-11-13T00:00:00.0000000
- DateTimeType: UnixSeconds
ParsedDateTime: 1970-01-20T13:43:43.0000000Z
Kind: Utc
- DateTimeType: UnixMilliseconds
ParsedDateTime: 1970-01-01T00:28:11.0230000Z
Kind: Utc
- DateTimeType: UnixNanoSeconds
ParsedDateTime: 1970-01-01T00:00:00.0016910Z
Kind: Utc
- DateTimeType: yMMdd_DateInt
ParsedDateTime: 0169-10-23T00:00:00.0000000
"""
);
}

[Test]
public async Task DateIntDoubleDigitYear()
{
var input = "691023";

var results = Parse(input);

InlineSnapshot.Validate(
results,
"""
- DateTimeType: DayNumber
ParsedDateTime: 1892-12-16T00:00:00.0000000
- DateTimeType: UnixSeconds
ParsedDateTime: 1970-01-08T23:57:03.0000000Z
Kind: Utc
- DateTimeType: UnixMilliseconds
ParsedDateTime: 1970-01-01T00:11:31.0230000Z
Kind: Utc
- DateTimeType: UnixNanoSeconds
ParsedDateTime: 1970-01-01T00:00:00.0006910Z
Kind: Utc
- DateTimeType: yMMdd_DateInt
ParsedDateTime: 0069-10-23T00:00:00.0000000
"""
);
}

private static List<Result> Parse(string input)
{
Expand Down
2 changes: 0 additions & 2 deletions MikeysUtils3.UnitTests/DateTimeTypeExtensionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ public async Task TryParseAsDateTime_Time_UTC_ISO_8601(string time, string expec
await Assert.That(result.Kind).IsEqualTo(kind);
}

// TODO: refactor tests to be based on input and all output

[Test]
[Arguments("2024-10-23 21:39:40", "2024-10-23T21:39:40.0000000", DateTimeKind.Unspecified)]
[Arguments("2024-10-23 21:39:40.1", "2024-10-23T21:39:40.1000000", DateTimeKind.Unspecified)]
Expand Down
151 changes: 89 additions & 62 deletions MikeysUtils3/Pages/DateTimePage/DateTimeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static bool TryParseAsDateTime(this string input, DateTimeType type, out
{
input = input.Trim();
dateTimeParsed = default;

switch (type)
{
case DateTimeType.Date_yyyy_MM_dd_ISO_8601:
Expand All @@ -86,56 +86,56 @@ public static bool TryParseAsDateTime(this string input, DateTimeType type, out
case DateTimeType.Time_UTC_ISO_8601:
// Extended format
return DateTime.TryParseExact(
input,
"THH:mm:ssZ",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THH:mm:sszzz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THH:mm:sszz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) ||
input,
"THH:mm:ssZ",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THH:mm:sszzz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THH:mm:sszz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) ||
// Basic Format
DateTime.TryParseExact(
input,
"THHmmssZ",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THHmmsszzz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THHmmsszz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THHmmss",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
);
input,
"THHmmssZ",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THHmmsszzz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THHmmsszz",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
) || DateTime.TryParseExact(
input,
"THHmmss",
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal,
out dateTimeParsed
);
case DateTimeType.DateTime_UTC_ISO_8601:
return DateTime.TryParseExact(
input, "O", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
Expand All @@ -146,7 +146,7 @@ out dateTimeParsed
out dateTimeParsed);
case DateTimeType.DateTime_UniversalSortable_yyyy_MM_dd_HH_mm_ss:
input = input.Trim(['.', ':']).Trim();

return DateTime.TryParseExact(
input, "yyyy-MM-dd HH:mm:ss.fffffff", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed) || DateTime.TryParseExact(
Expand Down Expand Up @@ -174,7 +174,7 @@ out dateTimeParsed
{
return false;
}

if (longParsed >= 0 && longParsed < DateOnly.MaxValue.DayNumber)
{
dateTimeParsed = DateOnly.FromDayNumber((int)longParsed).ToDateTime();
Expand All @@ -189,7 +189,7 @@ out dateTimeParsed
{
return false;
}

if (longParsed >= MinUnixTimestamp.TotalSeconds && longParsed <= MaxUnixTimestamp.TotalSeconds)
{
dateTimeParsed = DateTime.UnixEpoch.AddSeconds(longParsed);
Expand All @@ -204,8 +204,9 @@ out dateTimeParsed
{
return false;
}

if (longParsed >= MinUnixTimestamp.TotalMilliseconds && longParsed <= MaxUnixTimestamp.TotalMilliseconds)

if (longParsed >= MinUnixTimestamp.TotalMilliseconds &&
longParsed <= MaxUnixTimestamp.TotalMilliseconds)
{
dateTimeParsed = DateTime.UnixEpoch.AddMilliseconds(longParsed);
return true;
Expand All @@ -219,7 +220,7 @@ out dateTimeParsed
{
return false;
}

if (longParsed >= MinUnixTimestamp.TotalNanoseconds && longParsed <= MaxUnixTimestamp.TotalNanoseconds)
{
dateTimeParsed = DateTime.UnixEpoch.AddTicks(longParsed / 100);
Expand All @@ -229,16 +230,42 @@ out dateTimeParsed
return false;
}
case DateTimeType.yyyyMMdd_DateBasic:
break;
case DateTimeType.yMMdd_DateInt:
return DateTime.TryParseExact(
input, "yyyMMdd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed) || DateTime.TryParseExact(
input, "yMMdd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed) && dateTimeParsed.Year < 100;
input, "yyyyMMdd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed);
case DateTimeType.yMMdd_DateInt:
if (DateTime.TryParseExact(
input, "yyyyMMdd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed))
{
return true;
}

if (DateTime.TryParseExact(
input, "yyyMMdd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed))
{
return true;
}

if (DateTime.TryParseExact(
input, "yMMdd", DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None,
out dateTimeParsed))
{
if (dateTimeParsed.Year > 100)
{
// yMMdd parses single digit/2 digit values as 19xx, therefore need to trim to just xx by removing 1900
var centuryYears = -1 * dateTimeParsed.Year / 100 * 100;

dateTimeParsed = dateTimeParsed.AddYears(centuryYears);
}
return true;
}

return false;
case DateTimeType.None:
case DateTimeType.Unknown:
return false;
return false;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
Expand Down

0 comments on commit a2b8796

Please sign in to comment.