@@ -620,7 +620,7 @@ public static string ToPeriodStartString(this DateTime start) =>
620
620
/// <param name="end">The period end date</param>
621
621
/// <returns>The formatted period end date</returns>
622
622
public static string ToPeriodEndString ( this DateTime end ) =>
623
- IsMidnight ( end ) || IsLastMomentOfDay ( end ) ? $ "{ end . ToShortDateString ( ) } { end . ToShortTimeString ( ) } " : end . ToShortDateString ( ) ;
623
+ IsMidnight ( end ) || IsLastMomentOfDay ( end ) ? end . ToShortDateString ( ) : $ "{ end . ToShortDateString ( ) } { end . ToShortTimeString ( ) } ";
624
624
625
625
/// <summary>Test if the date is in UTC</summary>
626
626
/// <param name="dateTime">The source date time</param>
@@ -1388,6 +1388,20 @@ public static List<DatePeriod> Split(this DatePeriod period, List<DateTime> spli
1388
1388
return splitPeriods ;
1389
1389
}
1390
1390
1391
+ /// <summary>Get period days</summary>
1392
+ /// <param name="period">The period</param>
1393
+ public static List < DateTime > Days ( this DatePeriod period )
1394
+ {
1395
+ var days = new List < DateTime > ( ) ;
1396
+ var current = period . Start . Date ;
1397
+ while ( current <= period . End . Date )
1398
+ {
1399
+ days . Add ( current ) ;
1400
+ current = current . AddDays ( 1 ) ;
1401
+ }
1402
+ return days ;
1403
+ }
1404
+
1391
1405
/// <summary>Test if a specific time moment is the first day of a period</summary>
1392
1406
/// <param name="test">The moment to test</param>
1393
1407
/// <param name="period">The period </param>
@@ -1618,6 +1632,181 @@ public static List<DatePeriod> Intersections(this IEnumerable<DatePeriod> datePe
1618
1632
}
1619
1633
}
1620
1634
1635
+ /// <summary><see cref="TimePeriod">TimePeriod</see> extension methods</summary>
1636
+ public static class TimePeriodExtensions
1637
+ {
1638
+ /// <summary>Test if a specific time moment is before this period</summary>
1639
+ /// <param name="period">The period</param>
1640
+ /// <param name="test">The moment to test</param>
1641
+ /// <returns>True, if the moment is before this period</returns>
1642
+ public static bool IsBefore ( this TimePeriod period , decimal test ) =>
1643
+ test < period . Start ;
1644
+
1645
+ /// <summary>Test if a specific time period is before this period</summary>
1646
+ /// <param name="period">The period</param>
1647
+ /// <param name="testPeriod">The period to test</param>
1648
+ /// <returns>True, if the period is before this period</returns>
1649
+ public static bool IsBefore ( this TimePeriod period , TimePeriod testPeriod ) =>
1650
+ testPeriod . End < period . Start ;
1651
+
1652
+ /// <summary>Test if a specific time moment is after this period</summary>
1653
+ /// <param name="period">The period</param>
1654
+ /// <param name="test">The moment to test</param>
1655
+ /// <returns>True, if the moment is after this period</returns>
1656
+ public static bool IsAfter ( this TimePeriod period , decimal test ) =>
1657
+ test > period . End ;
1658
+
1659
+ /// <summary>Test if a specific time period is after this period</summary>
1660
+ /// <param name="period">The period</param>
1661
+ /// <param name="testPeriod">The period to test</param>
1662
+ /// <returns>True, if the period is after this period</returns>
1663
+ public static bool IsAfter ( this TimePeriod period , TimePeriod testPeriod ) =>
1664
+ testPeriod . Start > period . End ;
1665
+
1666
+ /// <summary>Test if a specific time moment is within the period, including open periods</summary>
1667
+ /// <param name="period">The period</param>
1668
+ /// <param name="test">The moment to test</param>
1669
+ /// <returns>True, if the moment is within this period</returns>
1670
+ public static bool IsWithin ( this TimePeriod period , decimal test ) =>
1671
+ test . IsWithin ( period . Start , period . End ) ;
1672
+
1673
+ /// <summary>Test if a specific time period is within the period, including open periods</summary>
1674
+ /// <param name="period">The period</param>
1675
+ /// <param name="testPeriod">The period to test</param>
1676
+ /// <returns>True, if the test period is within this period</returns>
1677
+ public static bool IsWithin ( this TimePeriod period , TimePeriod testPeriod ) =>
1678
+ IsWithin ( period , testPeriod . Start ) && IsWithin ( period , testPeriod . End ) ;
1679
+
1680
+ /// <summary>Test if a specific time moment is within or before the period, including open periods</summary>
1681
+ /// <param name="period">The period</param>
1682
+ /// <param name="test">The moment to test</param>
1683
+ /// <returns>True, if the moment is within or before this period</returns>
1684
+ public static bool IsWithinOrBefore ( this TimePeriod period , decimal test ) =>
1685
+ test <= period . End ;
1686
+
1687
+ /// <summary>Test if a specific time moment is within or after the period, including open periods</summary>
1688
+ /// <param name="period">The period</param>
1689
+ /// <param name="test">The moment to test</param>
1690
+ /// <returns>True, if the moment is within or after this period</returns>
1691
+ public static bool IsWithinOrAfter ( this TimePeriod period , decimal test ) =>
1692
+ test >= period . Start ;
1693
+
1694
+ /// <summary>Test if period is overlapping this period</summary>
1695
+ /// <param name="period">The period</param>
1696
+ /// <param name="testPeriod">The period to test</param>
1697
+ /// <returns>True, if the period is overlapping this period</returns>
1698
+ public static bool IsOverlapping ( this TimePeriod period , TimePeriod testPeriod ) =>
1699
+ testPeriod . Start < period . End && period . Start < testPeriod . End ;
1700
+
1701
+ /// <summary>Get the intersection of a time period with this period</summary>
1702
+ /// <param name="period">The period</param>
1703
+ /// <param name="intersectPeriod">The period to intersect</param>
1704
+ /// <returns>The intersecting time period, null if no intersection is present</returns>
1705
+ public static TimePeriod Intersect ( this TimePeriod period , TimePeriod intersectPeriod )
1706
+ {
1707
+ if ( ! IsOverlapping ( period , intersectPeriod ) )
1708
+ {
1709
+ return null ;
1710
+ }
1711
+ return new (
1712
+ Math . Max ( period . Start , intersectPeriod . Start ) ,
1713
+ Math . Min ( period . End , intersectPeriod . End ) ) ;
1714
+ }
1715
+
1716
+ /// <summary>Get the hours of intersection</summary>
1717
+ /// <param name="period">The period</param>
1718
+ /// <param name="intersectPeriod">The period to intersect</param>
1719
+ /// <returns>The intersecting duration in hours, 0 if no intersection is present</returns>
1720
+ public static decimal IntersectHours ( this TimePeriod period , TimePeriod intersectPeriod )
1721
+ {
1722
+ var intersect = Intersect ( period , intersectPeriod ) ;
1723
+ return intersect == null ? 0 : ( decimal ) intersect . Duration . TotalHours ;
1724
+ }
1725
+
1726
+ /// <summary>Total duration of all time periods</summary>
1727
+ /// <param name="timePeriods">The time periods</param>
1728
+ /// <returns>Accumulated total duration</returns>
1729
+ public static TimeSpan TotalDuration ( this IEnumerable < TimePeriod > timePeriods )
1730
+ {
1731
+ var duration = TimeSpan . Zero ;
1732
+ return timePeriods . Aggregate ( duration , ( current , period ) => current . Add ( period . Duration ) ) ;
1733
+ }
1734
+
1735
+ /// <summary>Test if any period is overlapping another period</summary>
1736
+ /// <param name="timePeriods">The time periods to test</param>
1737
+ /// <returns>True, if the period is overlapping this period</returns>
1738
+ public static bool HasOverlapping ( this IEnumerable < TimePeriod > timePeriods )
1739
+ {
1740
+ var periodList = timePeriods . ToList ( ) ;
1741
+ for ( var current = 1 ; current < periodList . Count ; current ++ )
1742
+ {
1743
+ for ( var remain = current + 1 ; remain < periodList . Count ; remain ++ )
1744
+ {
1745
+ if ( periodList [ remain ] . IsOverlapping ( periodList [ current ] ) )
1746
+ {
1747
+ return true ;
1748
+ }
1749
+ }
1750
+ }
1751
+ return false ;
1752
+ }
1753
+
1754
+ /// <summary>Test if a specific time moment is within any time period</summary>
1755
+ /// <param name="timePeriods">The time periods to test</param>
1756
+ /// <param name="test">The moment to test</param>
1757
+ /// <returns>True, if the moment is within this period</returns>
1758
+ public static bool IsWithinAny ( this IEnumerable < TimePeriod > timePeriods , decimal test ) =>
1759
+ timePeriods . Any ( periodValue => periodValue . IsWithin ( test ) ) ;
1760
+
1761
+ /// <summary>Test if a specific time period is within any time period</summary>
1762
+ /// <param name="timePeriods">The time periods to test</param>
1763
+ /// <param name="testPeriod">The period to test</param>
1764
+ /// <returns>True, if the test period is within this period</returns>
1765
+ public static bool IsWithinAny ( this IEnumerable < TimePeriod > timePeriods , TimePeriod testPeriod ) =>
1766
+ timePeriods . Any ( periodValue => periodValue . IsWithin ( testPeriod ) ) ;
1767
+
1768
+ /// <summary>Get limits period, from the earliest start to the latest end</summary>
1769
+ /// <param name="timePeriods">The time periods to evaluate</param>
1770
+ /// <returns>Time period including all time periods, an anytime period for empty collections</returns>
1771
+ public static TimePeriod Limits ( this IEnumerable < TimePeriod > timePeriods )
1772
+ {
1773
+ decimal ? start = null ;
1774
+ decimal ? end = null ;
1775
+ foreach ( var timePeriod in timePeriods )
1776
+ {
1777
+ // start
1778
+ if ( ! start . HasValue || timePeriod . Start < start . Value )
1779
+ {
1780
+ start = timePeriod . Start ;
1781
+ }
1782
+ // end
1783
+ if ( ! end . HasValue || timePeriod . End > end . Value )
1784
+ {
1785
+ end = timePeriod . End ;
1786
+ }
1787
+ }
1788
+ return new ( start , end ) ;
1789
+ }
1790
+
1791
+ /// <summary>Get all intersections of a time period with any time period</summary>
1792
+ /// <param name="timePeriods">The time periods to test</param>
1793
+ /// <param name="intersectPeriod">The period to intersect</param>
1794
+ /// <returns>List of intersecting time periods</returns>
1795
+ public static List < TimePeriod > Intersections ( this IEnumerable < TimePeriod > timePeriods , TimePeriod intersectPeriod )
1796
+ {
1797
+ var intersections = new List < TimePeriod > ( ) ;
1798
+ foreach ( var timePeriod in timePeriods )
1799
+ {
1800
+ var intersection = timePeriod . Intersect ( intersectPeriod ) ;
1801
+ if ( intersection != null )
1802
+ {
1803
+ intersections . Add ( intersection ) ;
1804
+ }
1805
+ }
1806
+ return intersections ;
1807
+ }
1808
+ }
1809
+
1621
1810
/// <summary><see cref="CaseValue">CaseValue</see> extension methods</summary>
1622
1811
public static class CaseValueExtensions
1623
1812
{
@@ -1693,6 +1882,23 @@ public static IEnumerable<DatePeriod> Periods(this IEnumerable<CaseValue> period
1693
1882
}
1694
1883
}
1695
1884
1885
+ /// <summary>Get first matching period containing the test date</summary>
1886
+ /// <param name="caseValues">The case period values</param>
1887
+ /// <param name="date">The date of the case value</param>
1888
+ /// <returns>Accumulated total duration</returns>
1889
+ public static CaseValue CaseValueWithin ( this IEnumerable < CaseValue > caseValues , DateTime date )
1890
+ {
1891
+ foreach ( var caseValue in caseValues )
1892
+ {
1893
+ var period = caseValue . Period ( ) ;
1894
+ if ( period . IsWithin ( date ) )
1895
+ {
1896
+ return caseValue ;
1897
+ }
1898
+ }
1899
+ return null ;
1900
+ }
1901
+
1696
1902
/// <summary>Get case period values grouped by value</summary>
1697
1903
/// <param name="periodValues">The case period values</param>
1698
1904
/// <returns>Case period values grouped by value</returns>
@@ -1710,7 +1916,7 @@ public static TimeSpan TotalDuration(this IEnumerable<CaseValue> periodValues) =
1710
1916
/// <param name="intersectPeriod">The period to intersect</param>
1711
1917
/// <returns>List of intersecting date periods</returns>
1712
1918
public static List < CaseValue > Intersections ( this IEnumerable < CaseValue > periodValues , DatePeriod intersectPeriod ) =>
1713
- [ ..periodValues . Where ( periodValue => periodValue . Period ( ) . IsOverlapping ( intersectPeriod ) ) ] ;
1919
+ [ .. periodValues . Where ( periodValue => periodValue . Period ( ) . IsOverlapping ( intersectPeriod ) ) ] ;
1714
1920
1715
1921
/// <summary>Get case period values matching a period predicate</summary>
1716
1922
/// <param name="periodValues">The time periods to test</param>
0 commit comments