From 7789a72ffbe5d26c600cfc1b779d4759d5ac530b Mon Sep 17 00:00:00 2001 From: Alessio Gogna <5177307+alecsg77@users.noreply.github.com> Date: Mon, 3 May 2021 21:26:43 +0200 Subject: [PATCH] [enhancement] Date parser handle dates without a year. resolves #11219 (#11656) --- src/Jackett.Common/Utils/DateTimeUtil.cs | 44 ++++++++++++------ .../Common/Utils/DateTimeUtilTests.cs | 45 +++++++++++-------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/Jackett.Common/Utils/DateTimeUtil.cs b/src/Jackett.Common/Utils/DateTimeUtil.cs index 7067dee8b..55ceb1c12 100644 --- a/src/Jackett.Common/Utils/DateTimeUtil.cs +++ b/src/Jackett.Common/Utils/DateTimeUtil.cs @@ -42,11 +42,13 @@ namespace Jackett.Common.Utils } // ex: "2 hours 1 day" - public static DateTime FromTimeAgo(string str) + public static DateTime FromTimeAgo(string str, DateTime? relativeFrom = null) { str = str.ToLowerInvariant(); + var now = relativeFrom ?? DateTime.Now; + if (str.Contains("now")) - return DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); + return DateTime.SpecifyKind(now, DateTimeKind.Local); str = str.Replace(",", ""); str = str.Replace("ago", ""); @@ -80,7 +82,7 @@ namespace Jackett.Common.Utils throw new Exception("TimeAgo parsing failed, unknown unit: " + unit); } - return DateTime.SpecifyKind(DateTime.Now - timeAgo, DateTimeKind.Local); + return DateTime.SpecifyKind(now - timeAgo, DateTimeKind.Local); } // Uses the DateTimeRoutines library to parse the date @@ -97,14 +99,23 @@ namespace Jackett.Common.Utils throw new Exception("FromFuzzyTime parsing failed"); } + + private static DateTime FromFuzzyPastTime(string str, string format, DateTime now) + { + var result = FromFuzzyTime(str, format); + if (result > now) + result = result.AddYears(-1); + return result; + } - public static DateTime FromUnknown(string str, string format = null) + public static DateTime FromUnknown(string str, string format = null, DateTime? relativeFrom = null) { try { str = ParseUtil.NormalizeSpace(str); + var now = relativeFrom ?? DateTime.Now; if (str.ToLower().Contains("now")) - return DateTime.Now; + return now; // ... ago var match = _TimeAgoRegexp.Match(str); @@ -119,7 +130,7 @@ namespace Jackett.Common.Utils if (match.Success) { var time = str.Replace(match.Groups[0].Value, ""); - var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified); + var dt = DateTime.SpecifyKind(now.Date, DateTimeKind.Unspecified); dt += ParseTimeSpan(time); return dt; } @@ -129,7 +140,7 @@ namespace Jackett.Common.Utils if (match.Success) { var time = str.Replace(match.Groups[0].Value, ""); - var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified); + var dt = DateTime.SpecifyKind(now.Date, DateTimeKind.Unspecified); dt += ParseTimeSpan(time); dt -= TimeSpan.FromDays(1); return dt; @@ -140,7 +151,7 @@ namespace Jackett.Common.Utils if (match.Success) { var time = str.Replace(match.Groups[0].Value, ""); - var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified); + var dt = DateTime.SpecifyKind(now.Date, DateTimeKind.Unspecified); dt += ParseTimeSpan(time); dt += TimeSpan.FromDays(1); return dt; @@ -151,7 +162,7 @@ namespace Jackett.Common.Utils if (match.Success) { var time = str.Replace(match.Groups[0].Value, ""); - var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified); + var dt = DateTime.SpecifyKind(now.Date, DateTimeKind.Unspecified); dt += ParseTimeSpan(time); DayOfWeek dow; @@ -188,8 +199,9 @@ namespace Jackett.Common.Utils if (match.Success) { var date = match.Groups[1].Value; - var newDate = DateTime.Now.Year + "-" + date; + var newDate = now.Year + "-" + date; str = str.Replace(date, newDate); + return FromFuzzyPastTime(str, format, now); } // add missing year 2 @@ -198,7 +210,8 @@ namespace Jackett.Common.Utils { var date = match.Groups[1].Value; var time = match.Groups[2].Value; - str = date + " " + DateTime.Now.Year + " " + time; + str = date + " " + now.Year + " " + time; + return FromFuzzyPastTime(str, format, now); } return FromFuzzyTime(str, format); @@ -210,8 +223,10 @@ namespace Jackett.Common.Utils } // converts a date/time string to a DateTime object using a GoLang layout - public static DateTime ParseDateTimeGoLang(string date, string layout) + public static DateTime ParseDateTimeGoLang(string date, string layout, DateTime? relativeFrom = null) { + var now = relativeFrom ?? DateTime.Now; + date = ParseUtil.NormalizeSpace(date); var pattern = layout; @@ -278,7 +293,10 @@ namespace Jackett.Common.Utils try { - return DateTime.ParseExact(date, pattern, CultureInfo.InvariantCulture); + var dateTime = DateTime.ParseExact(date, pattern, CultureInfo.InvariantCulture); + if (!pattern.Contains("yy") && dateTime > now) + dateTime = dateTime.AddYears(-1); + return dateTime; } catch (FormatException ex) { diff --git a/src/Jackett.Test/Common/Utils/DateTimeUtilTests.cs b/src/Jackett.Test/Common/Utils/DateTimeUtilTests.cs index c3fc6075a..6bd4abfb0 100644 --- a/src/Jackett.Test/Common/Utils/DateTimeUtilTests.cs +++ b/src/Jackett.Test/Common/Utils/DateTimeUtilTests.cs @@ -88,7 +88,7 @@ namespace Jackett.Test.Common.Utils public void FromUnknownTest() { var now = DateTime.Now; - var today = DateTime.UtcNow.Date; + var today = now.ToUniversalTime().Date; var yesterday = today.AddDays(-1); var tomorrow = today.AddDays(1); var testCases = new Dictionary @@ -105,27 +105,28 @@ namespace Jackett.Test.Common.Utils }; foreach (var testCase in testCases) - Assert.AreEqual(testCase.Value, DateTimeUtil.FromUnknown(testCase.Key)); + Assert.AreEqual(testCase.Value, DateTimeUtil.FromUnknown(testCase.Key, relativeFrom: now)); - AssertSimilarDates(now, DateTimeUtil.FromUnknown("now")); - AssertSimilarDates(now.AddHours(-3), DateTimeUtil.FromUnknown("3 hours ago")); + Assert.AreEqual(now, DateTimeUtil.FromUnknown("now", relativeFrom: now)); + AssertSimilarDates(now.AddHours(-3), DateTimeUtil.FromUnknown("3 hours ago", relativeFrom: now)); - Assert.True((now - DateTimeUtil.FromUnknown("monday at 10:20 am")).TotalSeconds <= 3600 * 24 * 7); // 7 days - Assert.True((now - DateTimeUtil.FromUnknown("Tuesday at 22:20")).TotalSeconds <= 3600 * 24 * 7); - Assert.True((now - DateTimeUtil.FromUnknown("wednesday at \n 22:20")).TotalSeconds <= 3600 * 24 * 7); - Assert.True((now - DateTimeUtil.FromUnknown("\n thursday \n at 22:20")).TotalSeconds <= 3600 * 24 * 7); - Assert.True((now - DateTimeUtil.FromUnknown("friday at 22:20")).TotalSeconds <= 3600 * 24 * 7); - Assert.True((now - DateTimeUtil.FromUnknown("Saturday at 00:20")).TotalSeconds <= 3600 * 24 * 7); - Assert.True((now - DateTimeUtil.FromUnknown("sunday at 22:00")).TotalSeconds <= 3600 * 24 * 7); + Assert.True((now - DateTimeUtil.FromUnknown("monday at 10:20 am", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); // 7 days + Assert.True((now - DateTimeUtil.FromUnknown("Tuesday at 22:20", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); + Assert.True((now - DateTimeUtil.FromUnknown("wednesday at \n 22:20", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); + Assert.True((now - DateTimeUtil.FromUnknown("\n thursday \n at 22:20", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); + Assert.True((now - DateTimeUtil.FromUnknown("friday at 22:20", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); + Assert.True((now - DateTimeUtil.FromUnknown("Saturday at 00:20", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); + Assert.True((now - DateTimeUtil.FromUnknown("sunday at 22:00", relativeFrom: now)).TotalSeconds <= 3600 * 24 * 7); Assert.AreEqual(new DateTime(2020, 10, 31, 3, 8, 27, DateTimeKind.Utc).ToLocalTime(), - DateTimeUtil.FromUnknown("1604113707")); + DateTimeUtil.FromUnknown("1604113707", relativeFrom: now)); - Assert.AreEqual(new DateTime(now.Year, 2, 1), DateTimeUtil.FromUnknown("02-01")); - Assert.AreEqual(new DateTime(now.Year, 2, 1), DateTimeUtil.FromUnknown("2-1")); - Assert.AreEqual(new DateTime(now.Year, 1, 2, 10, 30, 0), DateTimeUtil.FromUnknown("2 Jan 10:30")); + var refDate = new DateTime(2021, 03, 12, 12, 00, 00, DateTimeKind.Local); + Assert.AreEqual(new DateTime(refDate.Year, 2, 1), DateTimeUtil.FromUnknown("02-01", relativeFrom: refDate)); + Assert.AreEqual(new DateTime(refDate.Year, 2, 1), DateTimeUtil.FromUnknown("2-1", relativeFrom: refDate)); + Assert.AreEqual(new DateTime(refDate.Year, 1, 2, 10, 30, 0), DateTimeUtil.FromUnknown("2 Jan 10:30", relativeFrom: refDate)); - Assert.AreEqual(new DateTime(2005, 6, 10, 10, 30, 0), + Assert.AreEqual(new DateTime(2005, 6, 10, 10, 30, 0), DateTimeUtil.FromUnknown("June 10, 2005 10:30AM")); // bad cases @@ -138,6 +139,13 @@ namespace Jackett.Test.Common.Utils { // ignored } + + Assert.AreEqual(new DateTime(refDate.Year - 1, 5, 2), DateTimeUtil.FromUnknown("05-02", relativeFrom: refDate)); + Assert.AreEqual(new DateTime(refDate.Year - 1, 5, 2), DateTimeUtil.FromUnknown("5-2", relativeFrom: refDate)); + Assert.AreEqual(new DateTime(refDate.Year - 1, 5, 2, 10, 30, 0), DateTimeUtil.FromUnknown("2 May 10:30", relativeFrom: refDate)); + + Assert.AreEqual(new DateTime(2020, 12, 31, 23, 59, 0), DateTimeUtil.FromUnknown("12-31 23:59", relativeFrom: new DateTime(2021, 12, 31, 23, 58, 59, DateTimeKind.Local))); + Assert.AreEqual(new DateTime(2020, 1, 1, 0, 1, 0), DateTimeUtil.FromUnknown("1-1 00:01", relativeFrom: new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Local))); } [Test] @@ -149,8 +157,9 @@ namespace Jackett.Test.Common.Utils DateTimeUtil.ParseDateTimeGoLang("21-06-2010 04:20:19 -04:00", "02-01-2006 15:04:05 -07:00")); Assert.AreEqual(new DateTimeOffset(2010, 6, 21, 0, 0, 0, new TimeSpan(-5, -30, 0)).ToLocalTime().DateTime, DateTimeUtil.ParseDateTimeGoLang("2010-06-21 -05:30", "2006-01-02 -07:00")); - Assert.AreEqual(new DateTime(now.Year, 9, 14, 7, 0, 0), - DateTimeUtil.ParseDateTimeGoLang("7am Sep. 14", "3pm Jan. 2")); + var refDate = new DateTime(2021, 03, 12, 12, 00, 00, DateTimeKind.Local); + Assert.AreEqual(new DateTime(refDate.Year - 1, 9, 14, 7, 0, 0), + DateTimeUtil.ParseDateTimeGoLang("7am Sep. 14", "3pm Jan. 2", relativeFrom:refDate)); // bad cases try