public void SafeMinus_NormalTime() { var start = new LocalInstant(0, 0); var end = start.SafeMinus(Offset.FromHours(1)); Assert.AreEqual(Duration.FromHours(-1), end.TimeSinceEpoch); }
/// <summary> /// Returns the first transition which occurs strictly after the given instant. /// </summary> /// <remarks> /// If the given instant is before the starting year, the year of the given instant is /// adjusted to the beginning of the starting year. The first transition after the /// adjusted instant is determined. If the next adjustment is after the ending year, this /// method returns null; otherwise the next transition is returned. /// </remarks> /// <param name="instant">The <see cref="Instant"/> lower bound for the next transition.</param> /// <param name="standardOffset">The <see cref="Offset"/> standard offset.</param> /// <param name="previousSavings">The <see cref="Offset"/> savings adjustment at the given Instant.</param> /// <returns>The next transition, or null if there is no next transition. The transition may be /// infinite, i.e. after the end of representable time.</returns> internal Transition?Next(Instant instant, Offset standardOffset, Offset previousSavings) { Offset ruleOffset = YearOffset.GetRuleOffset(standardOffset, previousSavings); Offset newOffset = standardOffset + Savings; LocalInstant safeLocal = instant.SafePlus(ruleOffset); int targetYear; if (safeLocal < minLocalInstant) { // Asked for a transition after some point before the first transition: crop to first year (so we get the first transition) targetYear = FromYear; } else if (safeLocal >= maxLocalInstant) { // Asked for a transition after our final transition... or both are beyond the end of time (in which case // we can return an infinite transition). This branch will always be taken for transitions beyond the end // of time. return(maxLocalInstant == LocalInstant.AfterMaxValue ? new Transition(Instant.AfterMaxValue, newOffset) : (Transition?)null); } else if (safeLocal == LocalInstant.BeforeMinValue) { // We've been asked to find the next transition after some point which is a valid instant, but is before the // start of valid local time after applying the rule offset. For example, passing Instant.MinValue for a rule which says // "transition uses wall time, which is UTC-5". Proceed as if we'd been asked for something in -9998. // I *think* that works... targetYear = GregorianYearMonthDayCalculator.MinGregorianYear; } else { // Simple case: we were asked for a "normal" value in the range of years for which this recurrence is valid. int ignoredDayOfYear; targetYear = CalendarSystem.Iso.YearMonthDayCalculator.GetYear(safeLocal.DaysSinceEpoch, out ignoredDayOfYear); } LocalInstant transition = YearOffset.GetOccurrenceForYear(targetYear); Instant safeTransition = transition.SafeMinus(ruleOffset); if (safeTransition > instant) { return(new Transition(safeTransition, newOffset)); } // We've got a transition earlier than we were asked for. Try next year. // Note that this will stil be within the FromYear/ToYear range, otherwise // safeLocal >= maxLocalInstant would have been triggered earlier. targetYear++; // Handle infinite transitions if (targetYear > GregorianYearMonthDayCalculator.MaxGregorianYear) { return(new Transition(Instant.AfterMaxValue, newOffset)); } // It's fine for this to be "end of time", and it can't be "start of time" because we're at least finding a transition in -9997. safeTransition = YearOffset.GetOccurrenceForYear(targetYear).SafeMinus(ruleOffset); return(new Transition(safeTransition, newOffset)); }
/// <summary> /// Returns the last transition which occurs before or on the given instant. /// </summary> /// <param name="instant">The <see cref="Instant"/> lower bound for the next trasnition.</param> /// <param name="standardOffset">The <see cref="Offset"/> standard offset.</param> /// <param name="previousSavings">The <see cref="Offset"/> savings adjustment at the given Instant.</param> /// <returns>The previous transition, or null if there is no previous transition. The transition may be /// infinite, i.e. before the start of representable time.</returns> internal Transition?PreviousOrSame(Instant instant, Offset standardOffset, Offset previousSavings) { Offset ruleOffset = YearOffset.GetRuleOffset(standardOffset, previousSavings); Offset newOffset = standardOffset + Savings; LocalInstant safeLocal = instant.SafePlus(ruleOffset); int targetYear; if (safeLocal > maxLocalInstant) { // Asked for a transition before some point after our last year: crop to last year. targetYear = ToYear; } // Deliberately < here; "previous or same" means if safeLocal==minLocalInstant, we should compute it for this year. else if (safeLocal < minLocalInstant) { // Asked for a transition before our first one return(null); } else if (!safeLocal.IsValid) { if (safeLocal == LocalInstant.BeforeMinValue) { // We've been asked to find the next transition before some point which is a valid instant, but is before the // start of valid local time after applying the rule offset. It's possible that the next transition *would* // be representable as an instant (e.g. 1pm Dec 31st -9999 with an offset of -5) but it's reasonable to // just return an infinite transition. return(new Transition(Instant.BeforeMinValue, newOffset)); } else { // We've been asked to find the next transition before some point which is a valid instant, but is after the // end of valid local time after applying the rule offset. For example, passing Instant.MaxValue for a rule which says // "transition uses wall time, which is UTC+5". Proceed as if we'd been asked for something in 9999. // I *think* that works... targetYear = GregorianYearMonthDayCalculator.MaxGregorianYear; } } else { // Simple case: we were asked for a "normal" value in the range of years for which this recurrence is valid. targetYear = CalendarSystem.Iso.YearMonthDayCalculator.GetYear(safeLocal.DaysSinceEpoch, out int ignoredDayOfYear); } LocalInstant transition = YearOffset.GetOccurrenceForYear(targetYear); Instant safeTransition = transition.SafeMinus(ruleOffset); if (safeTransition <= instant) { return(new Transition(safeTransition, newOffset)); } // We've got a transition later than we were asked for. Try next year. // Note that this will stil be within the FromYear/ToYear range, otherwise // safeLocal < minLocalInstant would have been triggered earlier. targetYear--; // Handle infinite transitions if (targetYear < GregorianYearMonthDayCalculator.MinGregorianYear) { return(new Transition(Instant.BeforeMinValue, newOffset)); } // It's fine for this to be "start of time", and it can't be "end of time" because we're at latest finding a transition in 9998. safeTransition = YearOffset.GetOccurrenceForYear(targetYear).SafeMinus(ruleOffset); return(new Transition(safeTransition, newOffset)); }