FixAntenna/NetCore/FixEngine/Scheduler/Schedule.cs (81 lines of code) (raw):

// Copyright (c) 2022 EPAM Systems // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using System; using Epam.FixAntenna.NetCore.Common; namespace Epam.FixAntenna.NetCore.FixEngine.Scheduler { internal class Schedule { public MultipartCronExpression TradePeriodBegin { get; } public MultipartCronExpression TradePeriodEnd { get; } public TimeZoneInfo TimeZone { get; } public Schedule(string tradePeriodBegin, string tradePeriodEnd, TimeZoneInfo timeZone) { TradePeriodBegin = tradePeriodBegin == null ? null : new MultipartCronExpression(tradePeriodBegin, timeZone); TradePeriodEnd = tradePeriodEnd == null ? null : new MultipartCronExpression(tradePeriodEnd, timeZone); TimeZone = timeZone; } public bool IsTradingPeriodDefined() => TradePeriodBegin != null && TradePeriodEnd != null; public bool IsOnlyPeriodBeginDefined() => TradePeriodBegin != null && TradePeriodEnd == null; public bool IsInsideOrAtBeginningOfInterval(DateTimeOffset date) { AssertTradingPeriodDefined(); var isIntervalStart = TradePeriodBegin.IsSatisfiedBy(date); var isInsideInterval = IsInsideInterval(date); return isIntervalStart || isInsideInterval; } /// <summary> /// Check if a date is inside interval defined by the schedule. /// If a date satisfies TradePeriodBegin or TradePeriodEnd then it's not inside the interval /// </summary> public bool IsInsideInterval(DateTimeOffset date) { AssertTradingPeriodDefined(); var startLastExecutionDate = TradePeriodBegin.GetTimeBefore(date); var stopLastExecutionDate = TradePeriodEnd.GetTimeBefore(date); var isIntervalEnd = TradePeriodEnd.IsSatisfiedBy(date) || TradePeriodBegin.IsSatisfiedBy(date); if (startLastExecutionDate.HasValue && stopLastExecutionDate.HasValue) { return startLastExecutionDate > stopLastExecutionDate && !isIntervalEnd; } // there was a start but not end yet if (startLastExecutionDate.HasValue) { return !isIntervalEnd; } // no start was done return false; } public bool IsTimestampAfterTradingPeriodEnd(long utcTimestampInMilliseconds) { var nextMatchingTime = TradePeriodEnd.GetTimeAfter(DateTimeOffset.UtcNow); return nextMatchingTime != null && utcTimestampInMilliseconds > nextMatchingTime.Value.ToUniversalTime().TotalMilliseconds(); } public bool IsTimestampInTradingPeriod(long utcTimestampInMilliseconds) { if (TradePeriodBegin != null && TradePeriodEnd != null) { var now = DateTimeOffset.UtcNow; var prevPeriodBegin = TradePeriodBegin.GetTimeBefore(now); var prevPeriodEnd = TradePeriodEnd.GetTimeBefore(now); var nextPeriodEnd = TradePeriodEnd.GetTimeAfter(now); if (prevPeriodBegin != null && prevPeriodEnd != null && nextPeriodEnd != null) { return IsTimestampAfterOrSame(utcTimestampInMilliseconds, prevPeriodBegin.Value) && prevPeriodBegin > prevPeriodEnd && utcTimestampInMilliseconds < nextPeriodEnd.Value.ToUniversalTime().TotalMilliseconds(); } } return false; } public bool IsTimestampAfterTradingPeriodBegin(long utcTimestampInMilliseconds) { var prevMatchingTime = TradePeriodBegin.GetTimeBefore(DateTimeOffset.UtcNow); return prevMatchingTime != null && IsTimestampAfterOrSame(utcTimestampInMilliseconds, prevMatchingTime.Value); } private bool IsTimestampAfterOrSame(long utcTimestampInMilliseconds, DateTimeOffset date) { return utcTimestampInMilliseconds >= date.ToUniversalTime().TotalMilliseconds(); } private void AssertTradingPeriodDefined() { if (!IsTradingPeriodDefined()) { throw new InvalidOperationException("Schedule is not fully defined"); } } } }