Time Period Library

repository·master·Indexed 18 days ago

https://github.com/giannoudis/timeperiodlibrary

A generic .NET library for high-accuracy time period calculations supporting standard, fiscal, school, accounting, and broadcast calendars. It extends DateTime and TimeSpan via ITimePeriod to handle ranges, blocks, and intervals with open/closed boundaries. Features include time period containers (collections and chains), calendar configuration via TimeCalendarConfig, ISO 8601 compliance, and specialized tools for calculating gaps, intersections, and date arithmetic with inclusion/exclusion rules.

Tokens
6.8K
Snippets
19
Records
25
Agent score
14%

What's inside timeperiodlibrary

  1. Available library versions and platforms

    master

    The Time Period library is distributed in four versions to support different .NET environments:

    • Library for .NET 2.0: Includes Unit Tests.
    • Library for .NET for Silverlight 4
    • Library for .NET for Windows Phone 7
    • Portable Class Library (PCL): Supports Windows Store, .NET 4, Silverlight 4, and Windows Phone 7. Unit Tests are available for this version and the complete .NET Framework.
  2. How Time Period Containers work

    master

    The library provides containers to group multiple ITimePeriod objects and operate on them as a single unit. All containers implement the ITimePeriod interface, meaning a container itself represents a time period (e.g., it has a start and an end) and can be used in calculations alongside individual periods like ITimeRange.

    All containers derive from ITimePeriodContainer, which extends IList<ITimePeriod> to provide list functionality.

  3. Use TimePeriodChain to create continuous time sequences

    master

    ITimePeriodChain (implemented by TimePeriodChain) connects multiple ITimePeriod objects in a sequence, ensuring that no gaps exist between successive periods.

    Important Constraints:

    • No Read-Only Periods: Because the chain may adjust the position of elements to maintain continuity, you cannot add read-only time periods. Attempting to do so results in a NotSupportedException.
    • Continuity: When adding or inserting periods, the chain manages the boundaries to ensure a seamless sequence.

    Use .Add() to append to the chain and .Insert(int index, ITimePeriod period) to place a period at a specific position.

    TimePeriodChain timePeriods = new TimePeriodChain();
    DateTime testDay = new DateTime( 2010, 7, 23 );
    
    // Add periods that will be chained together
    timePeriods.Add( new TimeBlock( TimeTrim.Hour( testDay, 8 ), Duration.Hours( 2 ) ) );
    timePeriods.Add( new TimeBlock( DateTime.Now, Duration.Hours( 1, 30 ) ) );
    
    // Insert a period at a specific index
    timePeriods.Insert( 2, new TimeBlock( DateTime.Now, Duration.Minutes( 45 ) ) );
  4. Handle calendar period boundary offsets with ITimePeriodMapper

    master

    In calendar calculations, the end of a time period is not exactly the start of the next. There is typically a gap of at least 1 Tick (100 nanoseconds). The ITimePeriodMapper interface allows you to convert moments between these boundaries using StartOffset (default = 0) and EndOffset (default = -1 Tick).

    Use MapStart(DateTime) to get the inclusive start of a period and MapEnd(DateTime) to get the inclusive end of a period (which lies a moment before the next period starts).

    TimeCalendar timeCalendar = new TimeCalendar();
    DateTime start = new DateTime( 2011, 3, 1, 13, 0, 0 );
    DateTime end = new DateTime( 2011, 3, 1, 14, 0, 0 );
    
    // Mapped start: 13:00:00.0000000
    var mappedStart = timeCalendar.MapStart( start );
    
    // Mapped end: 13:59:59.9999999
    var mappedEnd = timeCalendar.MapEnd( end );
  5. How time periods and their relations work

    master

    The library extends .NET's DateTime and TimeSpan by providing classes for handling periods characterized by a Start, a Duration, and an End.

    Core Abstractions

    All time periods are based on the ITimePeriod interface, which provides:

    • Start, End, and Duration properties.
    • HasStart: true if the Start time is defined (not DateTime.MinValue).
    • HasEnd: true if the End time is defined (not DateTime.MaxValue).
    • IsAnytime: true if neither Start nor End are defined.
    • IsMoment: true if Start and End are identical.
    • IsReadOnly: true for immutable time periods.

    Period Relations

    The relationship between two periods is described by the PeriodRelation enumeration. You can query these relations using convenience methods:

    • IsSamePeriod
    • HasInside
    • OverlapsWith
    • IntersectsWith
    • GetRelation(otherPeriod)
  6. Use TimePeriodCollection to manage arbitrary time periods

    master

    A TimePeriodCollection (implementing ITimePeriodCollection) holds multiple ITimePeriod elements.

    Key Behaviors:

    • Automatic Bounds: The collection's own time period is defined by the earliest start and the latest end of all contained elements.
    • Intersection by Moment: You can find which periods in the collection overlap with a specific DateTime using IntersectionPeriods(DateTime moment).
    • Intersection by Period: You can find which periods in the collection overlap with another ITimePeriod using IntersectionPeriods(ITimePeriod period).

    Both intersection methods return an ITimePeriodCollection containing the resulting overlapping periods.

    TimePeriodCollection timePeriods = new TimePeriodCollection();
    DateTime testDay = new DateTime( 2010, 7, 23 );
    
    // Add various periods
    timePeriods.Add( new TimeRange( TimeTrim.Hour( testDay, 8 ), TimeTrim.Hour( testDay, 11 ) ) );
    timePeriods.Add( new TimeBlock( TimeTrim.Hour( testDay, 10 ), Duration.Hours( 3 ) ) );
    
    // Find intersections with a specific moment
    DateTime intersectionMoment = new DateTime( 2010, 7, 23, 10, 30, 0 );
    ITimePeriodCollection momentIntersections = timePeriods.IntersectionPeriods( intersectionMoment );
    
    // Find intersections with another period
    TimeRange intersectionPeriod = new TimeRange( TimeTrim.Hour( testDay, 9 ), TimeTrim.Hour( testDay, 14, 30 ) );
    ITimePeriodCollection periodIntersections = timePeriods.IntersectionPeriods( intersectionPeriod );
  7. Configure ISO 8601 vs .NET Week numbering

    master

    The library allows you to explicitly choose between standard .NET week calculation and the ISO 8601 standard using the YearWeekType enumeration in TimeCalendarConfig. This is important because .NET's Calendar.GetWeekOfYear can deviate from ISO 8601.

    Use YearWeekType.Iso8601 to ensure compliance with international standards.

    // ISO 8601 calendar week
    TimeCalendar calendarIso8601 = new TimeCalendar( 
        new TimeCalendarConfig { YearWeekType = YearWeekType.Iso8601 } 
    );
    DateTime testDate = new DateTime( 2007, 12, 31 );
    int isoWeek = new Week( testDate, calendarIso8601 ).WeekOfYear;
  8. Implement Accounting/Fiscal Calendars (4-4-5)

    master

    For industries using accounting calendars (like the 4-4-5 calendar), you can configure ITimeCalendar to group weeks into specific patterns.

    Supported FiscalQuarterGrouping values:

    • FiscalQuarterGrouping.FourFourFiveWeeks (4-4-5)
    • FiscalQuarterGrouping.FourFiveFourWeeks (4-5-4)
    • FiscalQuarterGrouping.FiveFourFourWeeks (5-4-4)

    You can also control how the year aligns with month ends using FiscalYearAlignment.LastDay or FiscalYearAlignment.NearestDay.

    ITimeCalendar calendar = new TimeCalendar( new TimeCalendarConfig
    {
      YearType = YearType.FiscalYear,
      YearBaseMonth = YearMonth.September,
      FiscalFirstDayOfYear = DayOfWeek.Sunday,
      FiscalYearAlignment = FiscalYearAlignment.LastDay,
      FiscalQuarterGrouping = FiscalQuarterGrouping.FourFourFiveWeeks
    } );
  9. Configure a TimeCalendar with TimeCalendarConfig

    master

    The ITimeCalendar interface manages calendar-specific logic including CultureInfo, period boundary mapping, base months, week interpretation, and accounting/fiscal year definitions. You configure these behaviors using TimeCalendarConfig passed to the TimeCalendar constructor.

    Key configuration properties:

    • YearBaseMonth: Sets the starting month of the year (e.g., YearMonth.October). Affects Year, Halfyear, and Quarter.
    • YearWeekType: Determines week numbering (e.g., YearWeekType.Iso8601).
    • YearType: Defines if years are treated as standard or YearType.FiscalYear.
    • FiscalFirstDayOfYear: The DayOfWeek on which a fiscal year begins.
    • FiscalYearAlignment: How the year aligns with month ends (FiscalYearAlignment.LastDay or FiscalYearAlignment.NearestDay).
    • FiscalQuarterGrouping: The week grouping pattern (e.g., FiscalQuarterGrouping.FourFourFiveWeeks).
    var config = new TimeCalendarConfig
    {
      YearBaseMonth = YearMonth.October,
      YearWeekType = YearWeekType.Iso8601,
      YearType = YearType.FiscalYear
    };
    TimeCalendar calendar = new TimeCalendar(config);
  10. Use specialized Calendar Element classes

    master

    The library provides specialized classes for common time periods. All calendar elements derive from CalendarTimeRange (which is read-only) and implement ITimePeriod.

    Time periodSingle periodMultiple periodsRefers to year's base month
    YearYearYearsYes
    Broadcast yearBroadcastYear-No
    Half yearHalfyearHalfyearsYes
    QuarterQuarterQuartersYes
    MonthMonthMonthsNo
    Broadcast monthBroadcastMonth-No
    WeekWeekWeeksNo
    Broadcast weekBroadcastWeek-No
    DayDayDaysNo
    HourHourHoursNo
    MinuteMinuteMinutesNo

    Most elements can be instantiated with a DateTime moment or a specific value (like a year integer). To access sub-elements, use methods like GetQuarters() on a Year object.

    DateTime moment = new DateTime( 2011, 8, 15 );
    
    // Single periods
    var year = new Year( moment );
    var quarter = new Quarter( moment );
    var month = new Month( moment );
    
    // Accessing sub-elements
    ITimePeriodCollection quarters = year.GetQuarters();
    foreach ( Quarter q in quarters ) { /* ... */ }