Example #1
0
        private DateTimeOffset?GetOccurrenceByZonedTimes(DateTimeOffset from, TimeZoneInfo zone, bool inclusive)
        {
            var fromLocal = from.DateTime;

            if (TimeZoneHelper.IsAmbiguousTime(zone, fromLocal))
            {
                var currentOffset  = from.Offset;
                var standardOffset = zone.BaseUtcOffset;

                if (standardOffset != currentOffset)
                {
                    var daylightOffset       = TimeZoneHelper.GetDaylightOffset(zone, fromLocal);
                    var daylightTimeLocalEnd = TimeZoneHelper.GetDaylightTimeEnd(zone, fromLocal, daylightOffset).DateTime;

                    // Early period, try to find anything here.
                    var foundInDaylightOffset = FindOccurrence(fromLocal.Ticks, daylightTimeLocalEnd.Ticks, inclusive);
                    if (foundInDaylightOffset != NotFound)
                    {
                        return(new DateTimeOffset(foundInDaylightOffset, daylightOffset));
                    }

                    fromLocal = TimeZoneHelper.GetStandardTimeStart(zone, fromLocal, daylightOffset).DateTime;
                    inclusive = true;
                }

                // Skip late ambiguous interval.
                var ambiguousIntervalLocalEnd = TimeZoneHelper.GetAmbiguousIntervalEnd(zone, fromLocal).DateTime;

                if (HasFlag(CronExpressionFlag.Interval))
                {
                    var foundInStandardOffset = FindOccurrence(fromLocal.Ticks, ambiguousIntervalLocalEnd.Ticks - 1, inclusive);
                    if (foundInStandardOffset != NotFound)
                    {
                        return(new DateTimeOffset(foundInStandardOffset, standardOffset));
                    }
                }

                fromLocal = ambiguousIntervalLocalEnd;
                inclusive = true;
            }

            var occurrenceTicks = FindOccurrence(fromLocal.Ticks, inclusive);

            if (occurrenceTicks == NotFound)
            {
                return(null);
            }

            var occurrence = new DateTime(occurrenceTicks);

            if (zone.IsInvalidTime(occurrence))
            {
                var nextValidTime = TimeZoneHelper.GetDaylightTimeStart(zone, occurrence);
                return(nextValidTime);
            }

            if (TimeZoneHelper.IsAmbiguousTime(zone, occurrence))
            {
                var daylightOffset = TimeZoneHelper.GetDaylightOffset(zone, occurrence);
                return(new DateTimeOffset(occurrence, daylightOffset));
            }

            return(new DateTimeOffset(occurrence, zone.GetUtcOffset(occurrence)));
        }
Example #2
0
        private DateTimeOffset?GetOccurrenceConsideringTimeZone(DateTimeOffset fromUtc, TimeZoneInfo zone, bool inclusive)
        {
            if (!DateTimeHelper.IsRound(fromUtc))
            {
                // Rarely, if fromUtc is very close to DST transition, `TimeZoneInfo.ConvertTime` may not convert it correctly on Windows.
                // E.g., In Jordan Time DST started 2017-03-31 00:00 local time. Clocks jump forward from `2017-03-31 00:00 +02:00` to `2017-03-31 01:00 +3:00`.
                // But `2017-03-30 23:59:59.9999000 +02:00` will be converted to `2017-03-31 00:59:59.9999000 +03:00` instead of `2017-03-30 23:59:59.9999000 +02:00` on Windows.
                // It can lead to skipped occurrences. To avoid such errors we floor fromUtc to seconds:
                // `2017-03-30 23:59:59.9999000 +02:00` will be floored to `2017-03-30 23:59:59.0000000 +02:00` and will be converted to `2017-03-30 23:59:59.0000000 +02:00`.
                fromUtc   = DateTimeHelper.FloorToSeconds(fromUtc);
                inclusive = false;
            }

            var from = TimeZoneInfo.ConvertTime(fromUtc, zone);

            var fromLocal = from.DateTime;

            if (TimeZoneHelper.IsAmbiguousTime(zone, fromLocal))
            {
                var currentOffset  = from.Offset;
                var standardOffset = zone.BaseUtcOffset;

                if (standardOffset != currentOffset)
                {
                    var daylightOffset       = TimeZoneHelper.GetDaylightOffset(zone, fromLocal);
                    var daylightTimeLocalEnd = TimeZoneHelper.GetDaylightTimeEnd(zone, fromLocal, daylightOffset).DateTime;

                    // Early period, try to find anything here.
                    var foundInDaylightOffset = FindOccurrence(fromLocal.Ticks, daylightTimeLocalEnd.Ticks, inclusive);
                    if (foundInDaylightOffset != NotFound)
                    {
                        return(new DateTimeOffset(foundInDaylightOffset, daylightOffset));
                    }

                    fromLocal = TimeZoneHelper.GetStandardTimeStart(zone, fromLocal, daylightOffset).DateTime;
                    inclusive = true;
                }

                // Skip late ambiguous interval.
                var ambiguousIntervalLocalEnd = TimeZoneHelper.GetAmbiguousIntervalEnd(zone, fromLocal).DateTime;

                if (HasFlag(CronExpressionFlag.Interval))
                {
                    var foundInStandardOffset = FindOccurrence(fromLocal.Ticks, ambiguousIntervalLocalEnd.Ticks - 1, inclusive);
                    if (foundInStandardOffset != NotFound)
                    {
                        return(new DateTimeOffset(foundInStandardOffset, standardOffset));
                    }
                }

                fromLocal = ambiguousIntervalLocalEnd;
                inclusive = true;
            }

            var occurrenceTicks = FindOccurrence(fromLocal.Ticks, inclusive);

            if (occurrenceTicks == NotFound)
            {
                return(null);
            }

            var occurrence = new DateTime(occurrenceTicks);

            if (zone.IsInvalidTime(occurrence))
            {
                var nextValidTime = TimeZoneHelper.GetDaylightTimeStart(zone, occurrence);
                return(nextValidTime);
            }

            if (TimeZoneHelper.IsAmbiguousTime(zone, occurrence))
            {
                var daylightOffset = TimeZoneHelper.GetDaylightOffset(zone, occurrence);
                return(new DateTimeOffset(occurrence, daylightOffset));
            }

            return(new DateTimeOffset(occurrence, zone.GetUtcOffset(occurrence)));
        }