BO4E.Extensions/BusinessObjects/Energiemenge/EnergiemengeExtension.cs
/// <summary>
///     Get a list of those time ranges within a reference, where no energieverbrauch entries are defined.
/// </summary>
/// <param name="em">Energiemenge</param>
/// <param name="reference">reference time frame</param>
/// <param name="wev">Wertermittlungsverfahren</param>
/// <param name="obis">OBIS-Kennzahl</param>
/// <param name="me">Mengeneinheit</param>
/// <returns></returns>
Get a list of those time ranges within a reference, where no energieverbrauch entries are defined.
Get a list of those time ranges within a reference where no energieverbrauch entries are defined .
public static List<TimeRange> GetMissingTimeRanges(this BO.Energiemenge em, ITimeRange reference,
            Wertermittlungsverfahren? wev, string obis, Mengeneinheit me)
        {
            using (MiniProfiler.Current.Step(nameof(GetMissingTimeRanges)))
            {
                IDictionary<Tuple<DateTimeOffset?, DateTimeOffset?>, Verbrauch> filteredVerbrauch;
                using (MiniProfiler.Current.Step(
                    $"Filtering energieverbrauch on OBIS={obis}, WEV={wev}, Mengeneinheit={me}"))
                {
                    filteredVerbrauch = em.Energieverbrauch
                        .Where(v => v.Wertermittlungsverfahren == wev && v.Obiskennzahl == obis && v.Einheit == me)
                        .ToDictionary(v => new Tuple<DateTimeOffset?, DateTimeOffset?>(v.Startdatum, v.Enddatum), v => v);
                }
                if (filteredVerbrauch.Count < 2)
                    throw new ArgumentException("Not enough entries in energieverbrauch to determine periodicity.");
                if (!IsEvenlySpaced(em, reference, wev, obis, me, true))
                    throw new ArgumentException(
                        "The provided Energiemenge is not evenly spaced although gaps are allowed.");
                var periodicity = GetTimeSpans(em, wev, obis, me).Min();
                if (
                    Math.Abs((reference.Start - em.GetMinDate()).TotalMilliseconds % periodicity.TotalMilliseconds) !=
                    0)
                    throw new ArgumentException(
                        $"The absolute difference between reference.start ({reference.Start}) and the minimal date time in the Energiemenge ({em.GetMinDate()}) has to be an integer multiple of the periodicity {periodicity.TotalMilliseconds} but was {(reference.Start - em.GetMinDate()).TotalMilliseconds}.");
                var duration = filteredVerbrauch.Values.Min(v => v.Enddatum) -
                               filteredVerbrauch.Values.Min(v => v.Startdatum);
                var result = new List<TimeRange>();
                using (MiniProfiler.Current.Step("Populating list with time slices in UTC"))
                {
                    for (var dt = reference.Start; dt < reference.End; dt += periodicity)
                    {
                        switch (dt.Kind)
                        {
                            case DateTimeKind.Local:
                                throw new ArgumentException("Local DateTime not supported!");
                            case DateTimeKind.Unspecified:
                                dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
                                break;
                            case DateTimeKind.Utc:
                                break;
                        }
                        if (!filteredVerbrauch.ContainsKey(
                            new Tuple<DateTimeOffset?, DateTimeOffset?>(dt,
                                dt + duration))) 
                            result.Add(new TimeRange(dt, (dt + duration).Value));
                    }
                }
                return result;
            }
        }
public static List < TimeRange > GetMissingTimeRanges ( this BO . Energiemenge em , ITimeRange reference , Wertermittlungsverfahren ? wev , string obis , Mengeneinheit me ) { using ( MiniProfiler . Current . Step ( nameof ( GetMissingTimeRanges ) ) ) { IDictionary < Tuple < DateTimeOffset ? , DateTimeOffset ? > , Verbrauch > filteredVerbrauch ; using ( MiniProfiler . Current . Step ( $" Filtering energieverbrauch on OBIS= { obis } , WEV= { wev } , Mengeneinheit= { me } " ) ) { filteredVerbrauch = em . Energieverbrauch . Where ( v => v . Wertermittlungsverfahren == wev && v . Obiskennzahl == obis && v . Einheit == me ) . ToDictionary ( v => new Tuple < DateTimeOffset ? , DateTimeOffset ? > ( v . Startdatum , v . Enddatum ) , v => v ) ; } if ( filteredVerbrauch . Count < 2 ) throw new ArgumentException ( " " ) ; if ( ! IsEvenlySpaced ( em , reference , wev , obis , me , true ) ) throw new ArgumentException ( " " ) ; var periodicity = GetTimeSpans ( em , wev , obis , me ) . Min ( ) ; if ( Math . Abs ( ( reference . Start - em . GetMinDate ( ) ) . TotalMilliseconds % periodicity . TotalMilliseconds ) != 0 ) throw new ArgumentException ( $" The absolute difference between reference.start ( { reference . Start } ) and the minimal date time in the Energiemenge ( { em . GetMinDate ( ) } ) has to be an integer multiple of the periodicity  { periodicity . TotalMilliseconds }  but was  { ( reference . Start - em . GetMinDate ( ) ) . TotalMilliseconds } . " ) ; var duration = filteredVerbrauch . Values . Min ( v => v . Enddatum ) - filteredVerbrauch . Values . Min ( v => v . Startdatum ) ; var result = new List < TimeRange > ( ) ; using ( MiniProfiler . Current . Step ( " " ) ) { for ( var dt = reference . Start ; dt < reference . End ; dt += periodicity ) { switch ( dt . Kind ) { case DateTimeKind . Local : throw new ArgumentException ( " " ) ; case DateTimeKind . Unspecified : dt = DateTime . SpecifyKind ( dt , DateTimeKind . Utc ) ; break ; case DateTimeKind . Utc : break ; } if ( ! filteredVerbrauch . ContainsKey ( new Tuple < DateTimeOffset ? , DateTimeOffset ? > ( dt , dt + duration ) ) ) result . Add ( new TimeRange ( dt , ( dt + duration ) . Value ) ) ; } } return result ; } }
Get a list of those time ranges within a reference, where no energieverbrauch entries are defined.
Get a list of those time ranges within a reference where no energieverbrauch entries are defined .
// since it's assured, that the energieverbrauch entries are evenly spaced it doesn't matter which entry we use to determine the duration. // use a strict '==' instead of overlap. This is justified because all the other cases are considered beforehand //using (MiniProfiler.Current.Step("linq where on filtered verbrauch")) //{ //   Where<Verbrauch>(v => v.startdatum == dt && v.enddatum == dt + duration).Any()) //}
None
BO.Energiemenge
this
reference
ITimeRange
reference time frame
reference time frame
wev
Wertermittlungsverfahren?
None
obis
string
None
me
Mengeneinheit
None
em
None