/// <summary> /// Parses a daylight savings rule and returns the Rule object. /// </summary> /// <remarks> /// # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S /// </remarks> /// <param name="tokens">The tokens to parse.</param> /// <returns>The Rule object.</returns> internal RuleLine ParseRule(Tokens tokens) { var name = NextString(tokens, "GetName"); int fromYear = NextYear(tokens, 0); // This basically doesn't happen these days, but if we have any recurrent rules // which start at the dawn of time, make them effective from 1900. This matches // zic behaviour in the only cases of this that we've seen, e.g. the systemv rules // prior to 2001a. if (fromYear == int.MinValue) { fromYear = 1900; } int toYear = NextYear(tokens, fromYear); if (toYear < fromYear) { throw new ArgumentException($"To year cannot be before the from year in a Rule: {toYear} < {fromYear}"); } var type = NextOptional(tokens, "Type"); var yearOffset = ParseDateTimeOfYear(tokens, true); var savings = NextOffset(tokens, "SaveMillis"); var daylightSavingsIndicator = NextOptional(tokens, "LetterS"); // The name of the zone recurrence is currently the name of the rule. Later (in ZoneRule.GetRecurrences) // it will be replaced with the formatted name. It's not ideal, but it avoids a lot of duplication. var recurrence = new ZoneRecurrence(name, savings, yearOffset, fromYear, toYear); return(new RuleLine(recurrence, daylightSavingsIndicator, type)); }
public void ZoneRecurrenceToString() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("name", Offset.FromHours(1), yearOffset, 1900, 2000); Assert.AreEqual("name +01 ZoneYearOffset[mode:Utc monthOfYear:10 dayOfMonth:31 dayOfWeek:3 advance:True timeOfDay:00:00:00 addDay:False] [1900-2000]", recurrence.ToString()); }
public void Extremes() { ZoneRecurrence winter = new ZoneRecurrence("Winter", Offset.Zero, new ZoneYearOffset(TransitionMode.Wall, 10, 5, 0, false, new LocalTime(2, 0)), int.MinValue, int.MaxValue); ZoneRecurrence summer = new ZoneRecurrence("Summer", Offset.FromHours(1), new ZoneYearOffset(TransitionMode.Wall, 3, 10, 0, false, new LocalTime(1, 0)), int.MinValue, int.MaxValue); var zone = new StandardDaylightAlternatingMap(Offset.Zero, winter, summer); var firstSpring = Instant.FromUtc(-9998, 3, 10, 1, 0); var firstAutumn = Instant.FromUtc(-9998, 10, 5, 1, 0); // 1am UTC = 2am wall var lastSpring = Instant.FromUtc(9999, 3, 10, 1, 0); var lastAutumn = Instant.FromUtc(9999, 10, 5, 1, 0); // 1am UTC = 2am wall var dstOffset = Offset.FromHours(1); // Check both year -9998 and 9999, both the infinite interval and the next one in var firstWinter = new ZoneInterval("Winter", Instant.BeforeMinValue, firstSpring, Offset.Zero, Offset.Zero); var firstSummer = new ZoneInterval("Summer", firstSpring, firstAutumn, dstOffset, dstOffset); var lastSummer = new ZoneInterval("Summer", lastSpring, lastAutumn, dstOffset, dstOffset); var lastWinter = new ZoneInterval("Winter", lastAutumn, Instant.AfterMaxValue, Offset.Zero, Offset.Zero); Assert.AreEqual(firstWinter, zone.GetZoneInterval(Instant.MinValue)); Assert.AreEqual(firstWinter, zone.GetZoneInterval(Instant.FromUtc(-9998, 2, 1, 0, 0))); Assert.AreEqual(firstSummer, zone.GetZoneInterval(firstSpring)); Assert.AreEqual(firstSummer, zone.GetZoneInterval(Instant.FromUtc(-9998, 5, 1, 0, 0))); Assert.AreEqual(lastSummer, zone.GetZoneInterval(lastSpring)); Assert.AreEqual(lastSummer, zone.GetZoneInterval(Instant.FromUtc(9999, 5, 1, 0, 0))); Assert.AreEqual(lastWinter, zone.GetZoneInterval(lastAutumn)); Assert.AreEqual(lastWinter, zone.GetZoneInterval(Instant.FromUtc(9999, 11, 1, 0, 0))); Assert.AreEqual(lastWinter, zone.GetZoneInterval(Instant.MaxValue)); }
public void TestSerialization() { var dio = DtzIoHelper.CreateNoStringPool(); var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var expected = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); dio.TestZoneRecurrence(expected); }
public void NextOrFail_Fail() { var afterRecurrenceEnd = Instant.FromUtc(1980, 1, 1, 0, 0); var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Assert.Throws <InvalidOperationException>(() => recurrence.NextOrFail(afterRecurrenceEnd, Offset.Zero, Offset.Zero)); }
public void Equality() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); var actual = new RuleLine(recurrence, "D", null); var expected = new RuleLine(recurrence, "D", null); Assert.AreEqual(expected, actual); }
public void Next_FirstYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, Offset.Zero); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.Next(Instant.UnixEpoch, Offset.Zero, Offset.Zero); Transition? expected = new Transition(new Instant(Instant.UnixEpoch.Ticks + (1 * TicksPerStandardYear)), Offset.Zero, Offset.Zero); Assert.AreEqual(expected, actual); }
public void Next_BeforeFirstYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.Next(Instant.MinValue, Offset.Zero, Offset.Zero); Transition? expected = new Transition(NodaConstants.UnixEpoch, Offset.Zero); Assert.AreEqual(expected, actual); }
public void WriteRead() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, Offset.Zero); var recurrence = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); var actual = new ZoneRule(recurrence, "D"); var expected = new ZoneRule(recurrence, "D"); Assert.AreEqual(expected, actual); }
public void PreviousOrSame_AfterLastYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.PreviousOrSame(Instant.MaxValue, Offset.Zero, Offset.Zero); Transition? expected = new Transition(Instant.FromUtc(1972, 1, 1, 0, 0), Offset.Zero); Assert.AreEqual(expected, actual); }
public void PreviousOrSame_LastYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.PreviousOrSame(Instant.FromUtc(1971, 1, 1, 0, 0) - Duration.Epsilon, Offset.Zero, Offset.Zero); Transition? expected = new Transition(NodaConstants.UnixEpoch, Offset.Zero); Assert.AreEqual(expected, actual); }
public void PreviousOrSameOrFail_Fail() { var beforeRecurrenceStart = Instant.FromUtc(1960, 1, 1, 0, 0); var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Assert.Throws <InvalidOperationException>(() => recurrence.PreviousOrSameOrFail(beforeRecurrenceStart, Offset.Zero, Offset.Zero)); }
public void RenameAppend() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, Offset.Zero); var old = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); var actual = old.RenameAppend("-Summer"); var expected = new ZoneRecurrence("bob-Summer", Offset.Zero, yearOffset, 1971, 2009); Assert.AreEqual(expected, actual); }
public void PreviousOrSame_LastYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.PreviousOrSame(Instant.FromUtc(1971, 1, 1, 0, 0) - Duration.Epsilon, Offset.Zero, Offset.Zero); Transition?expected = new Transition(NodaConstants.UnixEpoch, Offset.Zero); Assert.AreEqual(expected, actual); }
public void PreviousOrSame_AfterLastYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.PreviousOrSame(Instant.MaxValue, Offset.Zero, Offset.Zero); Transition?expected = new Transition(Instant.FromUtc(1972, 1, 1, 0, 0), Offset.Zero); Assert.AreEqual(expected, actual); }
public void PreviousOrSame_IncludesGivenInstant() { var january10thMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 10, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("x", Offset.Zero, january10thMidnight, 2000, 3000); var transition = Instant.FromUtc(2500, 1, 10, 0, 0); var next = recurrence.PreviousOrSame(transition, Offset.Zero, Offset.Zero); Assert.AreEqual(transition, next.Value.Instant); }
public void Previous_LastYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.Previous(new Instant(NodaConstants.UnixEpoch.Ticks + (1 * TicksPerStandardYear)), Offset.Zero, Offset.Zero); Transition?expected = new Transition(NodaConstants.UnixEpoch, Offset.Zero, Offset.Zero); Assert.AreEqual(expected, actual); }
public void Next_ExcludesGivenInstant() { var january10thMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 10, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("x", Offset.Zero, january10thMidnight, 2000, 3000); var transition = Instant.FromUtc(2500, 1, 10, 0, 0); var next = recurrence.Next(transition, Offset.Zero, Offset.Zero); Assert.AreEqual(2501, next !.Value.Instant.InUtc().Year); }
public void Previous_BeforeFirstYear_null() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.Previous(NodaConstants.UnixEpoch - Duration.Epsilon, Offset.Zero, Offset.Zero); Transition?expected = null; Assert.AreEqual(expected, actual); }
public void Next_BeforeFirstYear() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.Next(Instant.MinValue, Offset.Zero, Offset.Zero); Transition?expected = new Transition(NodaConstants.UnixEpoch, Offset.Zero, Offset.Zero); Assert.AreEqual(expected, actual); }
public void TestZoneRecurrence(ZoneRecurrence expected) { Reset(); expected.Write(Writer); var actual = ZoneRecurrence.Read(Reader); Assert.AreEqual(expected, actual); ioStream.AssertEndOfStream(); }
/// <summary> /// Adds a recurring daylight saving time rule. /// </summary> /// <param name="recurrence">The zone recurrence defining the recurrening savings.</param> /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns> public DateTimeZoneBuilder AddRecurringSavings(ZoneRecurrence recurrence) { Preconditions.CheckNotNull(recurrence, "recurrence"); if (recurrence.FromYear <= recurrence.ToYear) { LastRuleSet.AddRule(recurrence); } return(this); }
public void Next_BeyondLastYear_null() { var afterRecurrenceEnd = Instant.FromUtc(1980, 1, 1, 0, 0); var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.Next(afterRecurrenceEnd, Offset.Zero, Offset.Zero); Transition? expected = null; Assert.AreEqual(expected, actual); }
public void Next_BeyondLastYear_null() { var afterRecurrenceEnd = Instant.FromUtc(1980, 1, 1, 0, 0); var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.Next(afterRecurrenceEnd, Offset.Zero, Offset.Zero); Transition?expected = null; Assert.AreEqual(expected, actual); }
public void IEquatable_Tests() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var value = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); var equalValue = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); var unequalValue = new ZoneRecurrence("foo", Offset.Zero, yearOffset, 1971, 2009); TestHelper.TestEqualsClass(value, equalValue, unequalValue); }
public void PreviousOrSame_OnFirstYear_null() { // Transition is on January 2nd, but we're asking for January 1st. var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 2, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition?actual = recurrence.PreviousOrSame(NodaConstants.UnixEpoch, Offset.Zero, Offset.Zero); Transition?expected = null; Assert.AreEqual(expected, actual); }
public void FormatName_Slash_Daylight() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int) IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("ignored", Offset.FromHours(1), yearOffset, 1971, 2009); var rule = new RuleLine(recurrence, "!", null); var zone = GetZone("X/Y", Offset.FromHours(1)); var zoneRecurrence = rule.GetRecurrences(zone).Single(); Assert.AreEqual("Y", zoneRecurrence.Name); }
public void FormatName_PercentS() { // Note that the offset is irrelevant here - the "daylight saving indicator" is replaced either way. var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int) IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("ignored", Offset.Zero, yearOffset, 1971, 2009); var rule = new RuleLine(recurrence, "!", null); var zone = GetZone("X%sY", Offset.Zero); var zoneRecurrence = rule.GetRecurrences(zone).Single(); Assert.AreEqual("X!Y", zoneRecurrence.Name); }
public void December31st2400_MaxYear_UtcTransition() { // Each year, the transition is at the midnight at the *end* of December 31st... var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 12, 31, 0, true, LocalTime.Midnight, true); // ... and the recurrence is valid for the whole of time var recurrence = new ZoneRecurrence("awkward", Offset.FromHours(1), yearOffset, GregorianYearMonthDayCalculator.MinGregorianYear, GregorianYearMonthDayCalculator.MaxGregorianYear); var next = recurrence.Next(Instant.FromUtc(9999, 6, 1, 0, 0), Offset.Zero, Offset.Zero); Assert.AreEqual(Instant.AfterMaxValue, next.Value.Instant); }
public void FormatName_Slash_Daylight() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("ignored", Offset.FromHours(1), yearOffset, 1971, 2009); var rule = new RuleLine(recurrence, "!", null); var zone = GetZone("X/Y", Offset.FromHours(1)); var zoneRecurrence = rule.GetRecurrences(zone).Single(); Assert.AreEqual("Y", zoneRecurrence.Name); }
public void FormatName_PercentS() { // Note that the offset is irrelevant here - the "daylight saving indicator" is replaced either way. var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("ignored", Offset.Zero, yearOffset, 1971, 2009); var rule = new RuleLine(recurrence, "!", null); var zone = GetZone("X%sY", Offset.Zero); var zoneRecurrence = rule.GetRecurrences(zone).Single(); Assert.AreEqual("X!Y", zoneRecurrence.Name); }
public void WithName() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var original = new ZoneRecurrence("original", Offset.FromHours(1), yearOffset, 1900, 2000); var renamed = original.WithName("renamed"); Assert.AreEqual("renamed", renamed.Name); Assert.AreEqual(original.Savings, renamed.Savings); Assert.AreEqual(original.YearOffset, renamed.YearOffset); Assert.AreEqual(original.FromYear, renamed.FromYear); Assert.AreEqual(original.ToYear, renamed.ToYear); }
public void ForSingleYear() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var original = new ZoneRecurrence("original", Offset.FromHours(1), yearOffset, 1900, 2000); var singleYear = original.ForSingleYear(2017); Assert.AreEqual(original.Name, singleYear.Name); Assert.AreEqual(original.Savings, singleYear.Savings); Assert.AreEqual(original.YearOffset, singleYear.YearOffset); Assert.AreEqual(2017, singleYear.FromYear); Assert.AreEqual(2017, singleYear.ToYear); }
public void InvalidMap_SimultaneousTransition() { // Two recurrences with different savings, but which occur at the same instant in time every year. ZoneRecurrence r1 = new ZoneRecurrence("Recurrence1", Offset.Zero, new ZoneYearOffset(TransitionMode.Utc, 10, 5, 0, false, new LocalTime(2, 0)), int.MinValue, int.MaxValue); ZoneRecurrence r2 = new ZoneRecurrence("Recurrence2", Offset.FromHours(1), new ZoneYearOffset(TransitionMode.Utc, 10, 5, 0, false, new LocalTime(2, 0)), int.MinValue, int.MaxValue); var map = new StandardDaylightAlternatingMap(Offset.Zero, r1, r2); Assert.Throws <InvalidOperationException>(() => map.GetZoneInterval(Instant.FromUtc(2017, 8, 25, 0, 0, 0))); }
private ZoneTransition GetNext(Instant nextInstant) { // Find next matching rule. ZoneRecurrence nextRule = null; Instant nextTicks = Instant.MaxValue; for (int i = 0; i < rules.Count; i++) { ZoneRecurrence rule = rules[i]; Transition? nextTransition = rule.Next(nextInstant, ruleSet.StandardOffset, savings); Instant? next = nextTransition == null ? (Instant?)null : nextTransition.Value.Instant; if (!next.HasValue || next.Value <= nextInstant) { rules.RemoveAt(i); i--; continue; } // Even if next is same as previous next, choose the rule in order for more // recently added rules to override. if (next.Value <= nextTicks) { // Found a better match. nextRule = rule; nextTicks = next.Value; } } if (nextRule == null) { return(null); } // Stop precalculating if year reaches some arbitrary limit. We can cheat in the // conversion because it is an approximation anyway. if (calendar.GetYear(nextTicks.Plus(Offset.Zero)) >= YearLimit) { return(null); } // Check if upper limit reached or passed. if (ruleSet.upperYear < Int32.MaxValue) { Instant upperTicks = ruleSet.upperYearOffset.MakeInstant(ruleSet.upperYear, ruleSet.StandardOffset, savings); if (nextTicks >= upperTicks) { // At or after upper limit. return(null); } } return(new ZoneTransition(nextTicks, nextRule.Name, ruleSet.StandardOffset, nextRule.Savings)); }
public void FormatName_PercentZ() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int) IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("Rule", Offset.FromHoursAndMinutes(5, 30), yearOffset, 1971, 2009); var rule = new RuleLine(recurrence, "D", null); var zoneRecurrence = rule.GetRecurrences(GetZone("X%zY", Offset.Zero)).Single(); Assert.AreEqual("X+0530Y", zoneRecurrence.Name); zoneRecurrence = rule.GetRecurrences(GetZone("X%zY", Offset.FromHoursAndMinutes(0, 30))).Single(); Assert.AreEqual("X+06Y", zoneRecurrence.Name); zoneRecurrence = rule.GetRecurrences(GetZone("X%zY", Offset.FromHoursAndMinutes(-6, -30))).Single(); Assert.AreEqual("X-01Y", zoneRecurrence.Name); }
public void FormatName_PercentZ() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("Rule", Offset.FromHoursAndMinutes(5, 30), yearOffset, 1971, 2009); var rule = new RuleLine(recurrence, "D", null); var zoneRecurrence = rule.GetRecurrences(GetZone("X%zY", Offset.Zero)).Single(); Assert.AreEqual("X+0530Y", zoneRecurrence.Name); zoneRecurrence = rule.GetRecurrences(GetZone("X%zY", Offset.FromHoursAndMinutes(0, 30))).Single(); Assert.AreEqual("X+06Y", zoneRecurrence.Name); zoneRecurrence = rule.GetRecurrences(GetZone("X%zY", Offset.FromHoursAndMinutes(-6, -30))).Single(); Assert.AreEqual("X-01Y", zoneRecurrence.Name); }
public void December31st2400_AskAtNanoBeforeLastTransition() { // The transition occurs after the end of the maximum // Each year, the transition is at the midnight at the *end* of December 31st... var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 12, 31, 0, true, LocalTime.Midnight, true); // ... and the recurrence is valid for the whole of time var recurrence = new ZoneRecurrence("awkward", Offset.FromHours(1), yearOffset, 1, 5000); // We can find the final transition var finalTransition = Instant.FromUtc(5001, 1, 1, 0, 0); var next = recurrence.Next(finalTransition - Duration.Epsilon, Offset.Zero, Offset.Zero); Transition?expected = new Transition(finalTransition, Offset.FromHours(1)); Assert.AreEqual(expected, next); // But we correctly reject anything after that Assert.IsNull(recurrence.Next(finalTransition, Offset.Zero, Offset.Zero)); }
/// <summary> /// If there are only two rules left and they are both infinite rules then a <see /// cref="DateTimeZone"/> implementation is returned that encapsulates those rules, /// otherwise null is returned. /// </summary> /// <param name="id">The id of the new <see cref="DateTimeZone"/>.</param> /// <returns>The new <see cref="DateTimeZone"/> or null.</returns> internal DateTimeZone BuildTailZone(String id) { if (rules.Count == 2) { ZoneRecurrence startRule = rules[0]; ZoneRecurrence endRule = rules[1]; if (startRule.IsInfinite && endRule.IsInfinite) { // With exactly two infinitely recurring rules left, a simple DaylightSavingsTimeZone can be // formed. // The order of rules can come in any order, and it doesn't really matter // which rule was chosen the 'start' and which is chosen the 'end'. DaylightSavingsTimeZone // works properly either way. return(new DaylightSavingsDateTimeZone(id, ruleSet.StandardOffset, startRule, endRule)); } } return(null); }
/// <summary> /// Parses a daylight savings rule and returns the Rule object. /// </summary> /// <remarks> /// # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S /// </remarks> /// <param name="tokens">The tokens to parse.</param> /// <returns>The Rule object.</returns> internal ZoneRule ParseRule(Tokens tokens) { var name = NextString(tokens, "GetName"); int fromYear = NextYear(tokens, "FromYear", 0); int toYear = NextYear(tokens, "ToYear", fromYear); if (toYear < fromYear) { throw new ArgumentException("To year cannot be before the from year in a Rule: " + toYear + " < " + fromYear); } /* string type = */ NextOptional(tokens, "Type"); var yearOffset = ParseDateTimeOfYear(tokens, true); var savings = NextOffset(tokens, "SaveMillis"); var letterS = NextOptional(tokens, "LetterS"); var recurrence = new ZoneRecurrence(name, savings, yearOffset, fromYear, toYear); return(new ZoneRule(recurrence, letterS)); }
/// <summary> /// Initializes a new instance of the <see cref="DaylightSavingsDateTimeZone"/> class. /// </summary> /// <remarks> /// At least one of the recurrences (it doesn't matter which) must be a "standard", i.e. not have any savings /// applied. The other may still not have any savings (e.g. for America/Resolute) or (for BCL compatibility) may /// even have negative daylight savings. /// </remarks> /// <param name="id">The id.</param> /// <param name="standardOffset">The standard offset.</param> /// <param name="startRecurrence">The start recurrence.</param> /// <param name="endRecurrence">The end recurrence.</param> internal DaylightSavingsDateTimeZone([NotNull] String id, Offset standardOffset, ZoneRecurrence startRecurrence, ZoneRecurrence endRecurrence) : base(id, false, standardOffset + Offset.Min(startRecurrence.Savings, endRecurrence.Savings), standardOffset + Offset.Max(startRecurrence.Savings, endRecurrence.Savings)) { this.standardOffset = standardOffset; // Treat the recurrences as if they extended to the start of time. startRecurrence = startRecurrence.ToStartOfTime(); endRecurrence = endRecurrence.ToStartOfTime(); Preconditions.CheckArgument(startRecurrence.IsInfinite, nameof(startRecurrence), "Start recurrence must extend to the end of time"); Preconditions.CheckArgument(endRecurrence.IsInfinite, nameof(endRecurrence), "End recurrence must extend to the end of time"); var dst = startRecurrence; var standard = endRecurrence; if (startRecurrence.Savings == Offset.Zero) { dst = endRecurrence; standard = startRecurrence; } Preconditions.CheckArgument(standard.Savings == Offset.Zero, nameof(startRecurrence), "At least one recurrence must not have savings applied"); dstRecurrence = dst; standardRecurrence = standard; }
internal static DaylightSavingsDateTimeZone Read([NotNull] IDateTimeZoneReader reader, [NotNull] string id) { Preconditions.CheckNotNull(reader, nameof(reader)); Offset standardOffset = reader.ReadOffset(); string standardName = reader.ReadString(); ZoneYearOffset standardYearOffset = ZoneYearOffset.Read(reader); string daylightName = reader.ReadString(); ZoneYearOffset daylightYearOffset = ZoneYearOffset.Read(reader); Offset savings = reader.ReadOffset(); ZoneRecurrence standardRecurrence = new ZoneRecurrence(standardName, Offset.Zero, standardYearOffset, int.MinValue, int.MaxValue); ZoneRecurrence dstRecurrence = new ZoneRecurrence(daylightName, savings, daylightYearOffset, int.MinValue, int.MaxValue); return new DaylightSavingsDateTimeZone(id, standardOffset, standardRecurrence, dstRecurrence); }
/// <summary> /// Parses a daylight savings rule and returns the Rule object. /// </summary> /// <remarks> /// # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S /// </remarks> /// <param name="tokens">The tokens to parse.</param> /// <returns>The Rule object.</returns> internal ZoneRule ParseRule(Tokens tokens) { var name = NextString(tokens, "GetName"); int fromYear = NextYear(tokens, "FromYear", 0); int toYear = NextYear(tokens, "ToYear", fromYear); if (toYear < fromYear) { throw new ArgumentException("To year cannot be before the from year in a Rule: " + toYear + " < " + fromYear); } /* string type = */ NextOptional(tokens, "Type"); var yearOffset = ParseDateTimeOfYear(tokens); var savings = NextOffset(tokens, "SaveMillis"); var letterS = NextOptional(tokens, "LetterS"); var recurrence = new ZoneRecurrence(name, savings, yearOffset, fromYear, toYear); return new ZoneRule(recurrence, letterS); }
public void December31st2400_AskAtNanoBeforeLastTransition() { // The transition occurs after the end of the maximum // Each year, the transition is at the midnight at the *end* of December 31st... var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 12, 31, 0, true, LocalTime.Midnight, true); // ... and the recurrence is valid for the whole of time var recurrence = new ZoneRecurrence("awkward", Offset.FromHours(1), yearOffset, 1, 5000); // We can find the final transition var finalTransition = Instant.FromUtc(5001, 1, 1, 0, 0); var next = recurrence.Next(finalTransition - Duration.Epsilon, Offset.Zero, Offset.Zero); Transition? expected = new Transition(finalTransition, Offset.FromHours(1)); Assert.AreEqual(expected, next); // But we correctly reject anything after that Assert.IsNull(recurrence.Next(finalTransition, Offset.Zero, Offset.Zero)); }
public void Test() { var dio = new DtzIoHelper("ZoneRecurrence"); var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, Offset.Zero); var expected = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); dio.TestZoneRecurrence(expected); }
public void Previous_BeforeFirstYear_null() { var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 1, 0, true, Offset.Zero); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.Previous(Instant.UnixEpoch - Duration.One, Offset.Zero, Offset.Zero); Transition? expected = null; Assert.AreEqual(expected, actual); }
public void Extremes() { ZoneRecurrence winter = new ZoneRecurrence("Winter", Offset.Zero, new ZoneYearOffset(TransitionMode.Wall, 10, 5, 0, false, new LocalTime(2, 0)), int.MinValue, int.MaxValue); ZoneRecurrence summer = new ZoneRecurrence("Summer", Offset.FromHours(1), new ZoneYearOffset(TransitionMode.Wall, 3, 10, 0, false, new LocalTime(1, 0)), int.MinValue, int.MaxValue); var zone = new DaylightSavingsDateTimeZone("infinite", Offset.Zero, winter, summer); var firstSpring = Instant.FromUtc(-9998, 3, 10, 1, 0); var firstAutumn = Instant.FromUtc(-9998, 10, 5, 1, 0); // 1am UTC = 2am wall var lastSpring = Instant.FromUtc(9999, 3, 10, 1, 0); var lastAutumn = Instant.FromUtc(9999, 10, 5, 1, 0); // 1am UTC = 2am wall var dstOffset = Offset.FromHours(1); // Check both year -9998 and 9999, both the infinite interval and the next one in var firstWinter = new ZoneInterval("Winter", Instant.BeforeMinValue, firstSpring, Offset.Zero, Offset.Zero); var firstSummer = new ZoneInterval("Summer", firstSpring, firstAutumn, dstOffset, dstOffset); var lastSummer = new ZoneInterval("Summer", lastSpring, lastAutumn, dstOffset, dstOffset); var lastWinter = new ZoneInterval("Winter", lastAutumn, Instant.AfterMaxValue, Offset.Zero, Offset.Zero); Assert.AreEqual(firstWinter, zone.GetZoneInterval(Instant.MinValue)); Assert.AreEqual(firstWinter, zone.GetZoneInterval(Instant.FromUtc(-9998, 2, 1, 0, 0))); Assert.AreEqual(firstSummer, zone.GetZoneInterval(firstSpring)); Assert.AreEqual(firstSummer, zone.GetZoneInterval(Instant.FromUtc(-9998, 5, 1, 0, 0))); Assert.AreEqual(lastSummer, zone.GetZoneInterval(lastSpring)); Assert.AreEqual(lastSummer, zone.GetZoneInterval(Instant.FromUtc(9999, 5, 1, 0, 0))); Assert.AreEqual(lastWinter, zone.GetZoneInterval(lastAutumn)); Assert.AreEqual(lastWinter, zone.GetZoneInterval(Instant.FromUtc(9999, 11, 1, 0, 0))); Assert.AreEqual(lastWinter, zone.GetZoneInterval(Instant.MaxValue)); // And just for kicks, let's check we can get them all with GetZoneIntervals. IEnumerable<ZoneInterval> intervals = zone.GetZoneIntervals(new Interval(null, null)).ToList(); Assert.AreEqual(firstWinter, intervals.First()); Assert.AreEqual(firstSummer, intervals.Skip(1).First()); Assert.AreEqual(lastSummer, intervals.Reverse().Skip(1).First()); Assert.AreEqual(lastWinter, intervals.Last()); }
/// <summary> /// Returns the transition occurring strictly after the specified instant. The <paramref name="recurrence"/> /// parameter will be populated with the recurrence the transition goes *from*. /// </summary> /// <param name="instant">The instant after which to consider transitions.</param> /// <param name="recurrence">Receives the savings offset for the transition.</param> private Transition NextTransition(Instant instant, out ZoneRecurrence recurrence) { // Both recurrences are infinite, so they'll both have previous transitions (possibly at int.MinValue). Transition dstTransition = dstRecurrence.NextOrFail(instant, standardOffset, Offset.Zero); Transition standardTransition = standardRecurrence.NextOrFail(instant, standardOffset, dstRecurrence.Savings); var standardTransitionInstant = standardTransition.Instant; var dstTransitionInstant = dstTransition.Instant; if (standardTransitionInstant < dstTransitionInstant) { // Next transition is from DST to standard. recurrence = dstRecurrence; return standardTransition; } else if (standardTransitionInstant > dstTransitionInstant) { // Next transition is from standard to DST. recurrence = standardRecurrence; return dstTransition; } else { // Okay, the transitions happen at the same time. If they're not at infinity, we're stumped. if (standardTransitionInstant.IsValid) { throw new InvalidOperationException("Zone recurrence rules have identical transitions. This time zone is broken."); } // Okay, the two transitions must be to the end of time. Find which recurrence has the later *previous* transition... var previousDstTransition = dstRecurrence.PreviousOrSameOrFail(instant, standardOffset, Offset.Zero); var previousStandardTransition = standardRecurrence.PreviousOrSameOrFail(instant, standardOffset, dstRecurrence.Savings); // No point in checking for equality here... they can't go back from the end of time to the start... if (previousDstTransition.Instant > previousStandardTransition.Instant) { // The previous transition is from standard to DST. Therefore the next one is from DST to standard. recurrence = dstRecurrence; return standardTransition; } else { // The previous transition is from standard to DST. Therefore the next one is from DST to standard. recurrence = standardRecurrence; return dstTransition; } } }
public void PreviousOrSame_OnFirstYear_null() { // Transition is on January 2nd, but we're asking for January 1st. var januaryFirstMidnight = new ZoneYearOffset(TransitionMode.Utc, 1, 2, 0, true, LocalTime.Midnight); var recurrence = new ZoneRecurrence("bob", Offset.Zero, januaryFirstMidnight, 1970, 1972); Transition? actual = recurrence.PreviousOrSame(NodaConstants.UnixEpoch, Offset.Zero, Offset.Zero); Transition? expected = null; Assert.AreEqual(expected, actual); }
/// <summary> /// Parses a daylight savings rule and returns the Rule object. /// </summary> /// <remarks> /// # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S /// </remarks> /// <param name="tokens">The tokens to parse.</param> /// <returns>The Rule object.</returns> internal RuleLine ParseRule(Tokens tokens) { var name = NextString(tokens, "GetName"); int fromYear = NextYear(tokens, 0); // This basically doesn't happen these days, but if we have any recurrent rules // which start at the dawn of time, make them effective from 1900. This matches // zic behaviour in the only cases of this that we've seen, e.g. the systemv rules // prior to 2001a. if (fromYear == int.MinValue) { fromYear = 1900; } int toYear = NextYear(tokens, fromYear); if (toYear < fromYear) { throw new ArgumentException($"To year cannot be before the from year in a Rule: {toYear} < {fromYear}"); } var type = NextOptional(tokens, "Type"); var yearOffset = ParseDateTimeOfYear(tokens, true); var savings = NextOffset(tokens, "SaveMillis"); var daylightSavingsIndicator = NextOptional(tokens, "LetterS"); // The name of the zone recurrence is currently the name of the rule. Later (in ZoneRule.GetRecurrences) // it will be replaced with the formatted name. It's not ideal, but it avoids a lot of duplication. var recurrence = new ZoneRecurrence(name, savings, yearOffset, fromYear, toYear); return new RuleLine(recurrence, daylightSavingsIndicator, type); }
internal BclAdjustmentRule(TimeZoneInfo zone, TimeZoneInfo.AdjustmentRule rule) { // With .NET 4.6, adjustment rules can have their own standard offsets, allowing // a much more reasonable set of time zone data. Unfortunately, this isn't directly // exposed, but we can detect it by just finding the UTC offset for an arbitrary // time within the rule - the start, in this case - and then take account of the // possibility of that being in daylight saving time. Fortunately, we only need // to do this during the setup. var ruleStandardOffset = zone.GetUtcOffset(rule.DateStart); if (zone.IsDaylightSavingTime(rule.DateStart)) { ruleStandardOffset -= rule.DaylightDelta; } StandardOffset = ruleStandardOffset.ToOffset(); // Although the rule may have its own standard offset, the start/end is still determined // using the zone's standard offset. var zoneStandardOffset = zone.BaseUtcOffset.ToOffset(); // Note: this extends back from DateTime.MinValue to start of time, even though the BCL can represent // as far back as 1AD. This is in the *spirit* of a rule which goes back that far. Start = rule.DateStart == DateTime.MinValue ? Instant.BeforeMinValue : rule.DateStart.ToLocalDateTime().WithOffset(zoneStandardOffset).ToInstant(); // The end instant (exclusive) is the end of the given date, so we need to add a day. End = rule.DateEnd == MaxDate ? Instant.AfterMaxValue : rule.DateEnd.ToLocalDateTime().PlusDays(1).WithOffset(zoneStandardOffset).ToInstant(); Savings = rule.DaylightDelta.ToOffset(); // Some rules have DST start/end of "January 1st", to indicate that they're just in standard time. This is important // for rules which have a standard offset which is different to the standard offset of the zone itself. if (IsStandardOffsetOnlyRule(rule)) { PartialMap = PartialZoneIntervalMap.ForZoneInterval(zone.StandardName, Start, End, StandardOffset, Offset.Zero); } else { var daylightRecurrence = new ZoneRecurrence(zone.DaylightName, Savings, ConvertTransition(rule.DaylightTransitionStart), int.MinValue, int.MaxValue); var standardRecurrence = new ZoneRecurrence(zone.StandardName, Offset.Zero, ConvertTransition(rule.DaylightTransitionEnd), int.MinValue, int.MaxValue); var recurringMap = new DaylightSavingsDateTimeZone("ignored", StandardOffset, standardRecurrence, daylightRecurrence); PartialMap = new PartialZoneIntervalMap(Start, End, recurringMap); } }
/// <summary> /// Adds a recurring daylight saving time rule. /// </summary> /// <param name="recurrence">The zone recurrence defining the recurrening savings.</param> /// <returns>This <see cref="DateTimeZoneBuilder"/> for chaining.</returns> public DateTimeZoneBuilder AddRecurringSavings(ZoneRecurrence recurrence) { Preconditions.CheckNotNull(recurrence, "recurrence"); if (recurrence.FromYear <= recurrence.ToYear) { LastRuleSet.AddRule(recurrence); } return this; }
public void RenameAppend_nullSuffix() { var yearOffset = new ZoneYearOffset(TransitionMode.Utc, 10, 31, (int)IsoDayOfWeek.Wednesday, true, Offset.Zero); var old = new ZoneRecurrence("bob", Offset.Zero, yearOffset, 1971, 2009); Assert.Throws(typeof(ArgumentNullException), () => old.RenameAppend(null), "Null suffix"); }