< Summary

Information
Class: MyNet.Temporal.Decomposition.TimeSpanDecomposer
Assembly: MyNet.Temporal
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Temporal/Decomposition/TimeSpanDecomposer.cs
Tag: 323_28699572109
Line coverage
95%
Covered lines: 59
Uncovered lines: 3
Coverable lines: 62
Total lines: 182
Line coverage: 95.1%
Branch coverage
82%
Covered branches: 28
Total branches: 34
Branch coverage: 82.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
Decompose(...)66.66%9990%
DecomposeHierarchical(...)100%66100%
DecomposeFlat(...)100%11100%
DecomposeLargestOnly(...)100%11100%
DecomposeSmallestOnly(...)100%11100%
ApplyPostProcessing(...)75%88100%
GetAbsoluteTicks(...)50%22100%
GetUnitTicks(...)88.88%9991.66%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Temporal/Decomposition/TimeSpanDecomposer.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="TimeSpanDecomposer.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Generic;
 9using System.Linq;
 10using MyNet.Primitives;
 11using MyNet.Primitives.Temporal;
 12
 13namespace MyNet.Temporal.Decomposition;
 14
 15/// <summary>
 16/// Decomposes a <see cref="TimeSpan"/> into ordered unit values using strict arithmetic.
 17/// </summary>
 18public sealed class TimeSpanDecomposer : ITimeSpanDecomposer
 19{
 20    /// <summary>
 21    /// Gets the default decomposer instance.
 22    /// </summary>
 623    public static TimeSpanDecomposer Default { get; } = new();
 24
 25    /// <inheritdoc />
 26    public IReadOnlyList<TimeUnitValue> Decompose(TimeSpan timeSpan, TimeSpanDecompositionOptions options)
 27    {
 34228        ArgumentNullException.ThrowIfNull(options);
 34229        ArgumentNullException.ThrowIfNull(options.RuleEngine);
 30
 34231        var ticks = GetAbsoluteTicks(timeSpan);
 32
 34233        var units = options.RuleEngine
 34234            .Select(timeSpan, options.MinUnit, options.MaxUnit)
 34235            .Distinct()
 34236            .OrderByDescending(GetUnitTicks)
 34237            .ToList();
 38
 34239        if (units.Count == 0)
 040            return [new(0, options.MinUnit)];
 41
 34242        var result = options.Mode switch
 34243        {
 32144            TimeSpanDecompositionMode.Hierarchical => DecomposeHierarchical(ticks, units),
 345            TimeSpanDecompositionMode.Flat => DecomposeFlat(ticks, units),
 1246            TimeSpanDecompositionMode.LargestUnitOnly => DecomposeLargestOnly(ticks, units),
 647            TimeSpanDecompositionMode.SmallestUnitOnly => DecomposeSmallestOnly(ticks, units),
 048            _ => DecomposeHierarchical(ticks, units)
 34249        };
 50
 34251        result = ApplyPostProcessing(result, options);
 52
 34253        return result.Count == 0 ? [new(0, options.MinUnit)] : result;
 54    }
 55
 56    // --------------------------------------------------
 57    // Core decomposition strategies
 58    // --------------------------------------------------
 59
 60    /// <summary>
 61    /// Decomposes the given ticks into unit values for all specified units using a hierarchical subtraction approach. E
 62    /// </summary>
 63    /// <param name="ticks">The number of ticks to decompose.</param>
 64    /// <param name="units">The list of time units to consider.</param>
 65    /// <returns>A list of TimeUnitValue representing the timespan in each specified unit.</returns>
 66    private static List<TimeUnitValue> DecomposeHierarchical(long ticks, List<TimeUnit> units)
 67    {
 32168        var remaining = ticks;
 32169        var result = new List<TimeUnitValue>(units.Count);
 70
 171671        foreach (var unit in units)
 72        {
 69373            var unitTicks = GetUnitTicks(unit);
 69374            if (unitTicks <= 0)
 75                continue;
 76
 69377            var value = (int)(remaining / unitTicks);
 69378            result.Add(new(value, unit));
 79
 69380            remaining -= value * unitTicks;
 69381            if (remaining <= 0)
 31282                break;
 83        }
 84
 32185        return result;
 86    }
 87
 88    /// <summary>
 89    /// Decomposes the given ticks into unit values for all specified units without performing hierarchical subtraction.
 90    /// </summary>
 91    /// <param name="ticks">The number of ticks to decompose.</param>
 92    /// <param name="units">The list of time units to consider.</param>
 93    /// <returns>A list of TimeUnitValue representing the timespan in each specified unit.</returns>
 94    private static List<TimeUnitValue> DecomposeFlat(long ticks, List<TimeUnit> units)
 95    {
 396        var result = new List<TimeUnitValue>(units.Count);
 397        result.AddRange(from unit in units let unitTicks = GetUnitTicks(unit) where unitTicks > 0 let value = (int)(tick
 98
 399        return result;
 100    }
 101
 102    /// <summary>
 103    /// Decomposes the given ticks into a single unit value based on the largest unit in the provided list. This strateg
 104    /// </summary>
 105    /// <param name="ticks">The number of ticks to decompose.</param>
 106    /// <param name="units">The list of time units to consider.</param>
 107    /// <returns>A list containing a single TimeUnitValue representing the entire timespan in the largest unit.</returns
 108    private static List<TimeUnitValue> DecomposeLargestOnly(long ticks, List<TimeUnit> units)
 109    {
 12110        var unit = units[0];
 12111        var value = (int)(ticks / GetUnitTicks(unit));
 112
 12113        return [new(value, unit)];
 114    }
 115
 116    /// <summary>
 117    /// Decomposes the given ticks into a single unit value based on the smallest unit in the provided list. This strate
 118    /// </summary>
 119    /// <param name="ticks">The number of ticks to decompose.</param>
 120    /// <param name="units">The list of time units to consider.</param>
 121    /// <returns>A list containing a single TimeUnitValue representing the entire timespan in the smallest unit.</return
 122    private static List<TimeUnitValue> DecomposeSmallestOnly(long ticks, List<TimeUnit> units)
 123    {
 6124        var unit = units[^1];
 6125        var value = (int)(ticks / GetUnitTicks(unit));
 126
 6127        return [new(value, unit)];
 128    }
 129
 130    /// <summary>
 131    /// Applies post-processing steps such as quantization, zero-unit filtering, and component limiting to the decompose
 132    /// </summary>
 133    /// <param name="input">The list of decomposed time unit values.</param>
 134    /// <param name="options">The decomposition options to apply.</param>
 135    /// <returns>The post-processed list of time unit values.</returns>
 136    private static List<TimeUnitValue> ApplyPostProcessing(List<TimeUnitValue> input, TimeSpanDecompositionOptions optio
 137    {
 342138        var result = input;
 139
 342140        if (options.Quantizer is not null)
 9141            result = [.. options.Quantizer.Quantize(result)];
 142
 342143        if (!options.IncludeZeroUnits)
 333144            result = [.. result.Where(x => x.Value != 0)];
 145
 342146        if (options.MaxComponents is > 0)
 318147            result = [.. result.Take(options.MaxComponents.Value)];
 148
 342149        return result;
 150    }
 151
 152    // --------------------------------------------------
 153    // Helpers
 154    // --------------------------------------------------
 155
 156    /// <summary>
 157    /// Gets the absolute value of ticks, treating long.MinValue as long.MaxValue to avoid overflow issues.
 158    /// </summary>
 159    /// <param name="timeSpan">The TimeSpan to get the absolute ticks from.</param>
 160    /// <returns>The absolute value of ticks.</returns>
 342161    private static long GetAbsoluteTicks(TimeSpan timeSpan) => timeSpan.Ticks == long.MinValue ? long.MaxValue : Math.Ab
 162
 163    /// <summary>
 164    /// Gets the number of ticks corresponding to a given time unit, using fixed average values for variable-length unit
 165    /// </summary>
 166    /// <param name="unit">The time unit.</param>
 167    /// <returns>The number of ticks for the specified time unit.</returns>
 168    private static long GetUnitTicks(TimeUnit unit)
 1743169        => unit switch
 1743170        {
 36171            TimeUnit.Year => (long)(DateTimeHelper.DaysPerYear * TimeSpan.TicksPerDay),
 153172            TimeUnit.Month => (long)(DateTimeHelper.DaysPerMonth * TimeSpan.TicksPerDay),
 72173            TimeUnit.Week => TimeSpan.TicksPerDay * DateTimeHelper.DaysPerWeek,
 273174            TimeUnit.Day => TimeSpan.TicksPerDay,
 354175            TimeUnit.Hour => TimeSpan.TicksPerHour,
 450176            TimeUnit.Minute => TimeSpan.TicksPerMinute,
 390177            TimeUnit.Second => TimeSpan.TicksPerSecond,
 15178            TimeUnit.Millisecond => TimeSpan.TicksPerMillisecond,
 0179            _ => 1L
 1743180        };
 181}
 182