{"id":2694,"date":"2019-10-23T14:31:04","date_gmt":"2019-10-23T13:31:04","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=2694"},"modified":"2022-07-25T08:33:25","modified_gmt":"2022-07-25T07:33:25","slug":"c-timerange","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/c-timerange\/","title":{"rendered":"C# TimeRange"},"content":{"rendered":"\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public interface ITimeRange \n{ \n    DateTime? Start { get; }\n    DateTime? End { get; }\n}\npublic enum OverlapType\n{ \n\tInvalid = 0,\n\tBefore = 1,\n\tAfter = 2,\n\tContains = 3,\n\tContained = 4,\n\tStartOverlaps = 5,\n\tEndOverlaps = 6\n}\n\npublic static class TimeRangeExtensions\n{\n\tpublic static bool HasOverlap(this ITimeRange timeRange, ITimeRange outer) => (int)GetOverlapType(timeRange, outer) > 3;\n\n\tpublic static TimeSpan GetOverlap(this ITimeRange timeRange, ITimeRange outer) => DateRangeOverlap(timeRange.Start, timeRange.End, outer.Start, outer.End);\n\n\tprivate static TimeSpan DateRangeOverlap(DateTime? min1, DateTime? max1, DateTime? min2, DateTime? max2)\n\t{\n\t\treturn DateRangeOverlap(\n\t\t\tmin1 ?? DateTime.MinValue,\n\t\t\tmax1 ?? DateTime.MaxValue,\n\t\t\tmin2 ?? DateTime.MinValue,\n\t\t\tmax2 ?? DateTime.MaxValue);\n\t}\n\n\tprivate static TimeSpan DateRangeOverlap(DateTime min1, DateTime max1, DateTime min2, DateTime max2)\n\t{\n\t\tif (min1 > max1 || min2 > max2)\n\t\t\treturn new TimeSpan(0);  \/\/ throw new Exception(\"Invalid date range\");\n\n\t\tif (max2 &lt;= min1 || min2 >= max1)\n\t\t\treturn new TimeSpan(0);\n\t\t\n\t\tif (min1 &lt;= min2 &amp;&amp; max1 >= max2)\n\t\t\treturn (max2 - min2).Duration();\n\n\t\tif (min1 >= min2 &amp;&amp; max1 &lt;= max2)\n\t\t\treturn (max1 - min1).Duration();\n\n\t\tif (min1 >= min2 &amp;&amp; max1 >= max2 &amp;&amp; max2 >= min1)\n\t\t\treturn (max2 - min1).Duration();\n\n\t\tif (min1 &lt;= min2 &amp;&amp; max1 >= min2 &amp;&amp; max2 >= max1)\n\t\t\treturn (max1 - min2).Duration();\n\n\t\treturn new TimeSpan(0);\n\t}\n\n\tpublic static OverlapType GetOverlapType(this ITimeRange timeRange, ITimeRange outer) => DateRangeOverlapType(timeRange.Start, timeRange.End, outer.Start, outer.End);\n\n\tprivate static OverlapType DateRangeOverlapType(DateTime? min1, DateTime? max1, DateTime? min2, DateTime? max2)\n\t{\n\t\treturn DateRangeOverlapType(\n\t\t\tmin1 ?? DateTime.MinValue,\n\t\t\tmax1 ?? DateTime.MaxValue,\n\t\t\tmin2 ?? DateTime.MinValue,\n\t\t\tmax2 ?? DateTime.MaxValue);\n\t}\n\n\tprivate static OverlapType DateRangeOverlapType(DateTime min1, DateTime max1, DateTime min2, DateTime max2)\n\t{\n\t\tif (min1 > max1 || min2 > max2)\n\t\t\treturn OverlapType.Invalid;\n\n\t\tif (max2 &lt;= min1)\n\t\t\treturn OverlapType.Before;\n\n\t\tif(min2 >= max1)\n\t\t\treturn OverlapType.After;\n\n\t\tif (min1 &lt;= min2 &amp;&amp; max1 >= max2)\n\t\t\treturn OverlapType.Contains;\n\n\t\tif (min1 >= min2 &amp;&amp; max1 &lt;= max2)\n\t\t\treturn OverlapType.Contained;\n\n\t\tif (min1 >= min2 &amp;&amp; max1 >= max2 &amp;&amp; max2 >= min1)\n\t\t\treturn OverlapType.StartOverlaps;\n\n\t\tif (min1 &lt;= min2 &amp;&amp; max1 >= min2 &amp;&amp; max2 >= max1)\n\t\t\treturn OverlapType.EndOverlaps;\n\n\t\treturn OverlapType.Invalid; \/\/ not possible\n\t}\n}\n\npublic sealed class TimeRange : ITimeRange\n{\n\tprivate TimeRange(DateTime? start, DateTime? end)\n\t{\n\t\tStart = start;\n\t\tEnd = end;\n\t}\n\n\tpublic DateTime? Start { get; private set; }\n\n\tpublic DateTime? End { get; private set; }\n\n\tpublic static ITimeRange Today => new TimeRange(DateTime.Today, DateTime.Today.AddDays(1).AddSeconds(-1));\n\n\tpublic static ITimeRange GetRelativeYear(int relative = 0)\n\t{\n\t\tvar month = DateTime.Now.AddYears(relative);\n\t\tvar start = new DateTime(month.Year, 1, 1);\n\t\tvar end = start.AddYears(1).AddSeconds(-1);\n\t\treturn new TimeRange(start, end);\n\t}\n\n\tpublic static ITimeRange GetRelativeMonth(int relative = 0)\n\t{\n\t\tvar month = DateTime.Now.AddMonths(relative);\n\t\tvar start = new DateTime(month.Year, month.Month, 1);\n\t\tvar end = start.AddMonths(1).AddSeconds(-1);\n\t\treturn new TimeRange(start, end);\n\t}\n\n\tpublic static ITimeRange GetRelativeWeek(int relative)\n\t{\n\t\tvar date = DateTime.Now;\n\t\twhile (date.DayOfWeek != DayOfWeek.Monday)\n\t\t\tdate = date.AddDays(-1);\n\t\tdate = date.AddDays(relative * 7);\n\t\treturn new TimeRange(date, date.AddDays(6));\n\t}\n\n\tpublic static ITimeRange GetRelativeDay(int relative)\n\t{\n\t\tvar date = DateTime.Today;\n\t\tdate = date.AddDays(relative);\n\t\treturn new TimeRange(date, date.AddDays(1).AddSeconds(-1));\n\t}\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/\/ Old code\n\nusing System;\n\nnamespace TimeRanges\n{\n    public interface ITimeRange\n    {\n        DateTime Start { get; }\n        DateTime End { get; }\n    }\n    public class TimeRange : ITimeRange\n    {\n        private TimeRange(DateTime start, DateTime end)\n        {\n            Start = start;\n            End = end;\n        }\n\n        public DateTime Start { get; }\n\n        public DateTime End { get; }\n\n        public static ITimeRange GetTimeRange(DateTime start, DateTime end)\n        {\n            return new TimeRange(start, end);\n        }\n\n        public static bool IsInRange(ITimeRange timeRange, DateTime? date)\n        {\n            return date.HasValue &amp;&amp; date >= timeRange.Start &amp;&amp; date &lt;= timeRange.End;\n        }\n\n        public static ITimeRange GetPreviousMonth()\n        {\n            var prevMonth = DateTime.Now.AddMonths(-1);\n            var start = new DateTime(prevMonth.Year, prevMonth.Month, 1);\n            var end = start.AddMonths(1).AddSeconds(-1);\n            return GetTimeRange(start, end);\n        }\n\n        public static int ISO8601WeekNumber(DateTime fromDate)\n        {\n            \/\/ Get jan 1st of the year\n            DateTime startOfYear = fromDate.AddDays(-fromDate.Day + 1).AddMonths(-fromDate.Month + 1);\n            \/\/ Get dec 31st of the year\n            DateTime endOfYear = startOfYear.AddYears(1).AddDays(-1);\n            \/\/ ISO 8601 weeks start with Monday \n            \/\/ The first week of a year includes the first Thursday \n            \/\/ DayOfWeek returns 0 for sunday up to 6 for saterday\n            int[] iso8601Correction = { 6, 7, 8, 9, 10, 4, 5 };\n            int nds = fromDate.Subtract(startOfYear).Days + iso8601Correction[(int)startOfYear.DayOfWeek];\n            int wk = nds \/ 7;\n            switch (wk)\n            {\n                case 0:\n                    \/\/ Return weeknumber of dec 31st of the previous year\n                    return ISO8601WeekNumber(startOfYear.AddDays(-1));\n                case 53:\n                    \/\/ If dec 31st falls before thursday it is week 01 of next year\n                    if (endOfYear.DayOfWeek &lt; DayOfWeek.Thursday)\n                        return 1;\n                    else\n                        return wk;\n                default: return wk;\n            }\n        }\n\n        public static ITimeRange GetRelativeMonth(int relative = 0)\n        {\n            var month = DateTime.Now.AddMonths(relative);\n            var start = new DateTime(month.Year, month.Month, 1);\n            var end = start.AddMonths(1).AddSeconds(-1);\n            return GetTimeRange(start, end);\n        }\n\n        public static ITimeRange GetRelativeWeek(int relative)\n        {\n            var date = DateTime.Now;\n            while (date.DayOfWeek != DayOfWeek.Monday)\n                date = date.AddDays(-1);\n\n            date = date.AddDays(relative * 7);\n            return GetTimeRange(date, date.AddDays(6));\n        }\n\n        public static string GetWeekDescription(DateTime dateTime)\n        {\n            var week = ISO8601WeekNumber(dateTime);\n            var year = dateTime.Year;\n            if (week == 1)\n            {\n                year = dateTime.AddMonths(1).Year;\n            }\n            else if (week >= 52)\n            {\n                year = dateTime.AddMonths(-1).Year;\n            }\n            return $\"Week {week} {year}\";\n        }\n    }\n}\n<\/pre>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"csharp\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">public interface IWithValidTime\n{\n    IDateTimeRange ValidTime { get; }\n}\n\npublic interface IDateTimeRange\n{\n    DateTime? Min { get; }\n    DateTime? Max { get; }\n}\n\npublic class TimeRange : IDateTimeRange\n{\n    public DateTime? Min { get; }\n    public DateTime? Max { get; }\n    private TimeRange(DateTime? min, DateTime? max)\n    {\n        Min = min;\n        Max = max;\n    }\n\n    public static IDateTimeRange From(DateTime? min, DateTime? max)\n    {\n        return new TimeRange(min, max);\n    }\n\n    public static IDateTimeRange Now()\n    {\n        return new TimeRange(DateTime.Now, DateTime.Now);\n    }\n\n    public static IDateTimeRange FromDates(DateTime? from, DateTime? to)\n    {\n        var fromDate = from?.Date;\n        var toDate = to.HasValue ? to.Value.Date + new TimeSpan(23, 59, 59) : to;\n        return From(fromDate, toDate);\n    }\n\n    public enum OverlapType\n    {\n        Invalid = 0,\n        Before = 1,\n        After = 2,\n        Contains = 3,\n        Contained = 4,\n        StartOverlaps = 5,\n        EndOverlaps = 6\n    }\n    public static bool ObjectIsValid(IWithValidTime obj, IDateTimeRange inTime) => HasOverlap(obj.ValidTime, inTime);\n    public static bool ObjectIsValidNow(IWithValidTime obj) => HasOverlap(obj.ValidTime, Now());\n    public static bool HasOverlap(IDateTimeRange timeRange, IDateTimeRange outer) => (int)GetOverlapType(timeRange, outer) >= 3;\n    public static OverlapType GetOverlapType(IDateTimeRange timeRange, IDateTimeRange outer) => DateRangeOverlapType(timeRange.Min, timeRange.Max, outer.Min, outer.Max);\n    private static OverlapType DateRangeOverlapType(DateTime? min1, DateTime? max1, DateTime? min2, DateTime? max2)\n    {\n        return DateRangeOverlapType(\n            min1 ?? DateTime.MinValue,\n            max1 ?? DateTime.MaxValue,\n            min2 ?? DateTime.MinValue,\n            max2 ?? DateTime.MaxValue);\n    }\n    private static OverlapType DateRangeOverlapType(DateTime min1, DateTime max1, DateTime min2, DateTime max2)\n    {\n        if (min1 > max1 || min2 > max2)\n            return OverlapType.Invalid;\n        if (max2 &lt;= min1)\n            return OverlapType.Before;\n        if (min2 >= max1)\n            return OverlapType.After;\n        if (min1 &lt;= min2 &amp;&amp; max1 >= max2)\n            return OverlapType.Contains;\n        if (min1 >= min2 &amp;&amp; max1 &lt;= max2)\n            return OverlapType.Contained;\n        if (min1 >= min2 &amp;&amp; max1 >= max2 &amp;&amp; max2 >= min1)\n            return OverlapType.StartOverlaps;\n        if (min1 &lt;= min2 &amp;&amp; max1 >= min2 &amp;&amp; max2 >= max1)\n            return OverlapType.EndOverlaps;\n        return OverlapType.Invalid; \/\/ not possible\n    }\n}<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[6,4,1],"tags":[],"class_list":["post-2694","post","type-post","status-publish","format-standard","hentry","category-dotnet","category-programming","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2694","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=2694"}],"version-history":[{"count":6,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2694\/revisions"}],"predecessor-version":[{"id":6403,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/2694\/revisions\/6403"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=2694"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=2694"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=2694"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}