// From http://stackoverflow.com/questions/15211052/what-is-the-system-timezoneinfo-isdaylightsavingtime-equivalent-in-nodatime // Thanks to Matt Johnson public static bool IsDaylightSavingsTime(ZonedDateTime zonedDateTime) { Instant instant = zonedDateTime.ToInstant(); ZoneInterval zoneInterval = zonedDateTime.Zone.GetZoneInterval(instant); return(zoneInterval.Savings != Offset.Zero); }
public void FakeDaylightSavingTime() { // .NET Core on Unix loses data from rules provided to CreateCustomTimeZone :( // (It assumes the rules have been created from tzif files, which isn't the case here.) Ignore.When(TestHelper.IsRunningOnDotNetCoreUnix, ".NET Core on Unix mangles custom time zones"); // Linux time zones on Mono can have a strange situation with a "0 savings" adjustment rule to represent // "we want to change standard time but we can't". // See https://github.com/nodatime/nodatime/issues/746 // Normally the odd rule would only be in place for a year, but it's simplest to just make it all the time. // We go into daylight savings at midday on March 10th, and out again at midday on September 25. // We should be able to use DateTime.MaxValue for dateEnd, but not in .NET 4.5 apparently. var rule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(DateTime.MinValue, new DateTime(9999, 12, 31), TimeSpan.Zero, TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 12, 0, 0), 3, 10), TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 12, 0, 0), 9, 25)); var bclZone = TimeZoneInfo.CreateCustomTimeZone("Nasty", TimeSpan.FromHours(4), "Display", "Standard", "Daylight", new[] { rule }); var nodaZone = BclDateTimeZone.FromTimeZoneInfo(bclZone); var winterInterval = nodaZone.GetZoneInterval(Instant.FromUtc(2017, 2, 1, 0, 0)); var summerInterval = nodaZone.GetZoneInterval(Instant.FromUtc(2017, 6, 1, 0, 0)); var expectedWinter = new ZoneInterval("Standard", Instant.FromUtc(2016, 9, 25, 8, 0), Instant.FromUtc(2017, 3, 10, 8, 0), Offset.FromHours(4), Offset.Zero); var expectedSummer = new ZoneInterval("Daylight", Instant.FromUtc(2017, 3, 10, 8, 0), Instant.FromUtc(2017, 9, 25, 8, 0), Offset.FromHours(4), Offset.FromHours(1)); Assert.AreEqual(expectedWinter, winterInterval); Assert.AreEqual(expectedSummer, summerInterval); }
public void GetPeriod_AfterLastTransition() { var may1981 = DateTimeZone.Utc.AtExactly(new LocalDateTime(1981, 5, 1, 0, 0, 1)).ToInstant(); var actual = Algiers.GetZoneInterval(may1981); var expected = new ZoneInterval("CET", new Instant(3575232000000000L), Instant.MaxValue, Offset.FromMilliseconds(NodaConstants.MillisecondsPerHour), Offset.Zero); Assert.AreEqual(expected, actual); }
public void GetPeriod_BeforeLast() { Instant april1981 = Instant.FromUtc(1981, 4, 1, 0, 0); var actual = Algiers.GetZoneInterval(april1981); var expected = new ZoneInterval("WET", Instant.FromUnixTimeTicks(3418020000000000L), Instant.FromUnixTimeTicks(3575232000000000L), Offset.Zero, Offset.Zero); 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 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 GetPeriod_AfterLastTransition() { var may1981 = DateTimeZone.Utc.AtStrictly(new LocalDateTime(1981, 5, 1, 0, 0, 1)).ToInstant(); var actual = Algiers.GetZoneInterval(may1981); var expected = new ZoneInterval("CET", Instant.FromUnixTimeTicks(3575232000000000L), null, Offset.FromSeconds(NodaConstants.SecondsPerHour), Offset.Zero); Assert.AreEqual(expected, actual); }
/// <summary> /// Dumps the contents of a provider to a writer. This is used in both unit tests and TzdbCompiler, so we can validate /// that changes to time zone code don't change behaviour. It's not expected to be exposed publicly; this used to /// be in NodaTime.CheckZones, but that didn't really prove its worth. It's currently very fix in format and options; /// we can make it more flexible later if we want. /// </summary> internal static void Dump([NotNull] this IDateTimeZoneProvider provider, [NotNull] TextWriter writer) { Preconditions.CheckNotNull(provider, nameof(provider)); Preconditions.CheckNotNull(writer, nameof(writer)); writer.WriteLine("TZDB version: {0}", provider.VersionId); foreach (var id in provider.Ids) { var zone = provider[id]; writer.WriteLine($"Zone: {zone.Id}"); var start = Instant.FromUtc(1800, 1, 1, 0, 0); var end = Instant.FromUtc(2100, 1, 1, 0, 0); ZoneInterval lastDisplayed = null; foreach (var interval in zone.GetZoneIntervals(start, end)) { writer.WriteLine(interval); lastDisplayed = interval; } // This will never be null; every interval has at least one zone interval. if (lastDisplayed.HasEnd) { writer.WriteLine("..."); writer.WriteLine(zone.GetZoneInterval(Instant.MaxValue)); } writer.WriteLine(); } }
public void IsoLocalStartAndEnd_Infinite() { var interval = new ZoneInterval("All time", null, null, Offset.Zero, Offset.Zero); Assert.Throws <InvalidOperationException>(() => interval.IsoLocalStart.ToString()); Assert.Throws <InvalidOperationException>(() => interval.IsoLocalEnd.ToString()); }
/// <summary> /// Creates a zone with a single transition between two offsets. /// </summary> /// <param name="transitionPoint">The transition point as an <see cref="Instant"/>.</param> /// <param name="offsetBefore">The offset of local time from UTC before the transition.</param> /// <param name="offsetAfter">The offset of local time from UTC before the transition.</param> /// <param name="id">ID for the newly created time zone.</param> public SingleTransitionDateTimeZone(Instant transitionPoint, Offset offsetBefore, Offset offsetAfter, string id) : base(id, false, Offset.Min(offsetBefore, offsetAfter), Offset.Max(offsetBefore, offsetAfter)) { EarlyInterval = new ZoneInterval(id + "-Early", null, transitionPoint, offsetBefore, Offset.Zero); LateInterval = new ZoneInterval(id + "-Late", transitionPoint, null, offsetAfter, Offset.Zero); }
/// <summary> /// Creates a zone with a single transition between two offsets. /// </summary> /// <param name="transitionPoint">The transition point as an <see cref="Instant"/>.</param> /// <param name="offsetBefore">The offset of local time from UTC before the transition.</param> /// <param name="offsetAfter">The offset of local time from UTC before the transition.</param> public SingleTransitionZone(Instant transitionPoint, Offset offsetBefore, Offset offsetAfter) : base("Single", false, Offset.Min(offsetBefore, offsetAfter), Offset.Max(offsetBefore, offsetAfter)) { earlyInterval = new ZoneInterval("Single-Early", Instant.MinValue, transitionPoint, offsetBefore, Offset.Zero); lateInterval = new ZoneInterval("Single-Late", transitionPoint, Instant.MaxValue, offsetAfter, Offset.Zero); }
private void CheckMapping(LocalDateTime localDateTime, ZoneInterval earlyInterval, ZoneInterval lateInterval, int count) { var mapping = TestZone.MapLocal(localDateTime); Assert.AreEqual(earlyInterval, mapping.EarlyInterval); Assert.AreEqual(lateInterval, mapping.LateInterval); Assert.AreEqual(count, mapping.Count); }
public void IsoLocalStartAndEnd_OutOfRange() { var interval = new ZoneInterval("All time", Instant.MinValue, null, Offset.FromHours(-1), Offset.Zero); Assert.Throws <OverflowException>(() => interval.IsoLocalStart.ToString()); interval = new ZoneInterval("All time", null, Instant.MaxValue, Offset.FromHours(11), Offset.Zero); Assert.Throws <OverflowException>(() => interval.IsoLocalEnd.ToString()); }
public void Contains_LocalInstant_WholeOfTime() { ZoneInterval interval = new ZoneInterval("All Time", Instant.BeforeMinValue, Instant.AfterMaxValue, Offset.FromHours(9), Offset.FromHours(1)); Assert.IsTrue(interval.Contains(SampleStart.Plus(Offset.Zero))); Assert.IsTrue(interval.Contains(Instant.MinValue.Plus(Offset.Zero))); Assert.IsTrue(interval.Contains(Instant.MaxValue.Plus(Offset.Zero))); }
public void Contains_Instant_WholeOfTime_ViaSpecialInstants() { ZoneInterval interval = new ZoneInterval("All Time", Instant.BeforeMinValue, Instant.AfterMaxValue, Offset.FromHours(9), Offset.FromHours(1)); Assert.IsTrue(interval.Contains(SampleStart)); Assert.IsTrue(interval.Contains(Instant.MinValue)); Assert.IsTrue(interval.Contains(Instant.MaxValue)); }
public void Contains_Instant_WholeOfTime_ViaNullity() { ZoneInterval interval = new ZoneInterval("All Time", null, null, Offset.FromHours(9), Offset.FromHours(1)); Assert.IsTrue(interval.Contains(SampleStart)); Assert.IsTrue(interval.Contains(Instant.MinValue)); Assert.IsTrue(interval.Contains(Instant.MaxValue)); }
public void Contains_LocalInstant_WholeOfTime() { ZoneInterval interval = new ZoneInterval("All Time", Instant.MinValue, Instant.MaxValue, Offset.FromHours(9), Offset.FromHours(1)); Assert.IsTrue(interval.Contains(SampleStart.Plus(Offset.Zero))); Assert.IsTrue(interval.Contains(LocalInstant.MinValue)); Assert.IsTrue(interval.Contains(LocalInstant.MaxValue)); }
public void ZoneInterval() { DateTimeZone london = DateTimeZoneProviders.Tzdb["Europe/London"]; ZoneInterval interval = london.GetZoneInterval(Instant.FromUtc(2010, 6, 19, 0, 0)); Assert.AreEqual("BST", interval.Name); Assert.AreEqual(Instant.FromUtc(2010, 3, 28, 1, 0), interval.Start); Assert.AreEqual(Instant.FromUtc(2010, 10, 31, 1, 0), interval.End); Assert.AreEqual(Offset.FromHours(1), interval.WallOffset); Assert.AreEqual(Offset.FromHours(1), interval.Savings); }
public IntervalRecurrencePattern(ZoneInterval interval) { Frequency = FrequencyType.Yearly; ByMonth.Add(interval.IsoLocalStart.Month); var date = interval.IsoLocalStart.ToDateTimeUnspecified(); var weekday = date.DayOfWeek; var num = DateUtil.WeekOfMonth(date); ByDay.Add(num != 5 ? new WeekDay(weekday, num) : new WeekDay(weekday, -1)); }
public void Contains_OutsideLocalInstantange() { ZoneInterval veryEarly = new ZoneInterval("Very early", Instant.BeforeMinValue, Instant.MinValue + Duration.FromHours(8), Offset.FromHours(-9), Offset.Zero); ZoneInterval veryLate = new ZoneInterval("Very late", Instant.MaxValue - Duration.FromHours(8), Instant.AfterMaxValue, Offset.FromHours(9), Offset.Zero); // The instants are contained... Assert.IsTrue(veryEarly.Contains(Instant.MinValue + Duration.FromHours(4))); Assert.IsTrue(veryLate.Contains(Instant.MaxValue - Duration.FromHours(4))); // But there are no valid local instants Assert.IsFalse(veryEarly.Contains(Instant.MinValue.Plus(Offset.Zero))); Assert.IsFalse(veryLate.Contains(Instant.MaxValue.Plus(Offset.Zero))); }
public static bool IsStandardTime(Instant instant, DateTimeZone zone) { ZoneInterval zoneInterval = zone.GetZoneInterval(instant); Offset offset = zoneInterval.Savings; if (offset.Seconds > 0) { return(false); } return(true); }
static ZonedDateTime ShiftForward( LocalDateTime local, DateTimeZone zone, ZoneInterval intervalBefore, ZoneInterval intervalAfter) { var instant = new OffsetDateTime(local, intervalBefore.WallOffset) .WithOffset(intervalAfter.WallOffset) .ToInstant(); return(new ZonedDateTime(instant, zone)); }
/// <summary> /// Validates that all the periods before the tail zone make sense. We have to start at the beginning of time, /// and then have adjoining periods. This is only called in the constructors. /// </summary> /// <remarks>This is only called from the constructors, but is internal to make it easier to test.</remarks> /// <exception cref="ArgumentException">The periods specified are invalid.</exception> internal static void ValidatePeriods(ZoneInterval[] periods, IZoneIntervalMap tailZone) { Preconditions.CheckArgument(periods.Length > 0, nameof(periods), "No periods specified in precalculated time zone"); Preconditions.CheckArgument(!periods[0].HasStart, nameof(periods), "Periods in precalculated time zone must start with the beginning of time"); for (int i = 0; i < periods.Length - 1; i++) { // Safe to use End here: there can't be a period *after* an endless one. Likewise it's safe to use Start on the next // period, as there can't be a period *before* one which goes back to the start of time. Preconditions.CheckArgument(periods[i].End == periods[i + 1].Start, nameof(periods), "Non-adjoining ZoneIntervals for precalculated time zone"); } Preconditions.CheckArgument(tailZone != null || periods[periods.Length - 1].RawEnd == Instant.AfterMaxValue, nameof(tailZone), "Null tail zone given but periods don't cover all of time"); }
/// <summary> /// Generates a POSIX time zone string from an IANA time zone name, for the given year. /// </summary> /// <param name="timeZoneName">The IANA time zone name.</param> /// <param name="year">The reference year.</param> /// <returns>A POSIX time zone string.</returns> public static string FromIanaTimeZoneName(string timeZoneName, int year) { DateTimeZone tz = DateTimeZoneProviders.Tzdb[timeZoneName]; ZonedDateTime jan = new LocalDate(year, 1, 1).AtStartOfDayInZone(tz); ZonedDateTime jul = new LocalDate(year, 7, 1).AtStartOfDayInZone(tz); ZoneInterval janInterval = tz.GetZoneInterval(jan.ToInstant()); ZoneInterval julInterval = tz.GetZoneInterval(jul.ToInstant()); ZoneInterval stdInterval = janInterval.Savings == Offset.Zero ? janInterval : julInterval; ZoneInterval dltInterval = janInterval.Savings != Offset.Zero ? janInterval : julInterval.Savings != Offset.Zero ? julInterval : null; var sb = new StringBuilder(); string stdAbbreviation = GetPosixAbbreviation(stdInterval.Name); sb.Append(stdAbbreviation); string stdOffsetString = GetPosixOffsetString(stdInterval.WallOffset); sb.Append(stdOffsetString); if (dltInterval != null) { string dltAbbreviation = GetPosixAbbreviation(dltInterval.Name); sb.Append(dltAbbreviation); if (dltInterval.Savings != Offset.FromHours(1)) { string dltOffsetString = GetPosixOffsetString(dltInterval.WallOffset); sb.Append(dltOffsetString); } string stdTransitionString = GetPosixTransitionString(stdInterval, tz); sb.Append("," + stdTransitionString); string dltTransitionString = GetPosixTransitionString(dltInterval, tz); sb.Append("," + dltTransitionString); } return(sb.ToString()); }
/// <summary> /// Gets the zone interval's start time in DateTimeKind.Utc ticks /// </summary> private static long GetDateTimeUtcTicks(ZoneInterval zoneInterval) { // can't convert these values directly to date times, so just shortcut these here // we set the min value to one since the logic in the ctor will decrement this value to // determine the last instant BEFORE the discontinuity if (zoneInterval.Start == Instant.MinValue) { return(1); } if (zoneInterval.Start == Instant.MaxValue) { return(DateTime.MaxValue.Ticks); } return(zoneInterval.Start.ToDateTimeUtc().Ticks); }
private static PrecalculatedDateTimeZone CreatePrecalculatedDateTimeZone(string id, IList <ZoneTransition> transitions, Instant tailZoneStart, DateTimeZone tailZone) { // Convert the transitions to intervals int size = transitions.Count; var intervals = new ZoneInterval[size]; for (int i = 0; i < size; i++) { var transition = transitions[i]; var endInstant = i == size - 1 ? tailZoneStart : transitions[i + 1].Instant; intervals[i] = new ZoneInterval(transition.Name, transition.Instant, endInstant, transition.WallOffset, transition.Savings); } return(new PrecalculatedDateTimeZone(id, intervals, tailZone)); }
internal PrecalculatedDateTimeZone([NotNull] string id, [NotNull] ZoneInterval[] intervals, IZoneIntervalMapWithMinMax tailZone) : base(id, false, ComputeOffset(intervals, tailZone, Offset.Min), ComputeOffset(intervals, tailZone, Offset.Max)) { this.tailZone = tailZone; this.periods = intervals; this.tailZone = tailZone; this.tailZoneStart = intervals[intervals.Length - 1].RawEnd; // We want this to be AfterMaxValue for tail-less zones. if (tailZone != null) { // Cache a "clamped" zone interval for use at the start of the tail zone. firstTailZoneInterval = tailZone.GetZoneInterval(tailZoneStart).WithStart(tailZoneStart); } ValidatePeriods(intervals, tailZone); }
private ZoneInterval GetIntervalAfterGap(LocalInstant localInstant) { Instant guess = new Instant(localInstant.Ticks); ZoneInterval guessInterval = GetZoneInterval(guess); // If the local interval occurs before the zone interval we're looking at starts, // it's the one we're looking for. Otherwise, we need to find the next interval. if (localInstant.Minus(guessInterval.WallOffset) < guessInterval.Start) { return(guessInterval); } else { return(GetZoneInterval(guessInterval.End)); } }
private static string GetPosixTransitionString(ZoneInterval interval, DateTimeZone tz) { if (!interval.HasEnd) { return("J365/25"); } LocalDateTime transition = interval.IsoLocalEnd; int transitionOccurrence = (transition.Day - 1) / 7 + 1; // return "last occurrence" (5) when appropriate if (transitionOccurrence == 4) { for (var i = 1; i <= 7; i++) { var futureInstant = interval.IsoLocalEnd.PlusYears(i).InZoneLeniently(tz).ToInstant(); ZoneInterval futureInterval = tz.GetZoneInterval(futureInstant); int occurrence = (futureInterval.IsoLocalEnd.Day - 1) / 7 + 1; if (occurrence < 4) { transitionOccurrence = 4; break; } if (occurrence == 5) { transitionOccurrence = 5; } } } var datePart = $"M{transition.Month}.{transitionOccurrence}.{(int)transition.DayOfWeek.ToDayOfWeek()}"; if (transition.TimeOfDay == new LocalTime(2, 0)) { return(datePart); } if (transition.Minute == 0 && transition.Second == 0) { return($"{datePart}/{transition.Hour}"); } if (transition.Second == 0) { return($"{datePart}/{transition.ToString("H:mm", CultureInfo.InvariantCulture)}"); } return($"{datePart}/{transition.ToString("H:mm:ss", CultureInfo.InvariantCulture)}"); }
internal ZonedDateTime ResolveLocal(LocalDateTime localDateTime, [Trusted][NotNull] AmbiguousTimeResolver ambiguousResolver, [Trusted][NotNull] SkippedTimeResolver skippedResolver) { Preconditions.DebugCheckNotNull(ambiguousResolver, nameof(ambiguousResolver)); Preconditions.DebugCheckNotNull(skippedResolver, nameof(skippedResolver)); LocalInstant localInstant = localDateTime.ToLocalInstant(); Instant firstGuess = localInstant.MinusZeroOffset(); ZoneInterval interval = GetZoneInterval(firstGuess); // Most of the time we'll go into here... the local instant and the instant // are close enough that we've found the right instant. if (interval.Contains(localInstant)) { ZonedDateTime guessZoned = new ZonedDateTime(localDateTime.WithOffset(interval.WallOffset), this); ZoneInterval earlier = GetEarlierMatchingInterval(interval, localInstant); if (earlier != null) { ZonedDateTime earlierZoned = new ZonedDateTime(localDateTime.WithOffset(earlier.WallOffset), this); return(ambiguousResolver(earlierZoned, guessZoned)); } ZoneInterval later = GetLaterMatchingInterval(interval, localInstant); if (later != null) { ZonedDateTime laterZoned = new ZonedDateTime(localDateTime.WithOffset(later.WallOffset), this); return(ambiguousResolver(guessZoned, laterZoned)); } return(guessZoned); } else { // Our first guess was wrong. Either we need to change interval by one (either direction) // or we're in a gap. ZoneInterval earlier = GetEarlierMatchingInterval(interval, localInstant); if (earlier != null) { return(new ZonedDateTime(localDateTime.WithOffset(earlier.WallOffset), this)); } ZoneInterval later = GetLaterMatchingInterval(interval, localInstant); if (later != null) { return(new ZonedDateTime(localDateTime.WithOffset(later.WallOffset), this)); } return(skippedResolver(localDateTime, this, GetIntervalBeforeGap(localInstant), GetIntervalAfterGap(localInstant))); } }
private ZoneInterval GetIntervalAfterGap(LocalInstant localInstant) { Instant guess = localInstant.MinusZeroOffset(); ZoneInterval guessInterval = GetZoneInterval(guess); // If the local interval occurs before the zone interval we're looking at starts, // it's the one we're looking for. Otherwise, we need to find the next interval. if (localInstant.Minus(guessInterval.WallOffset) < guessInterval.RawStart) { return(guessInterval); } else { // Will definitely be valid - there can't be a gap after an infinite interval. return(GetZoneInterval(guessInterval.End)); } }
private ZoneInterval GetIntervalBeforeGap(LocalInstant localInstant) { Instant guess = localInstant.MinusZeroOffset(); ZoneInterval guessInterval = GetZoneInterval(guess); // If the local interval occurs before the zone interval we're looking at starts, // we need to find the earlier one; otherwise this interval must come after the gap, and // it's therefore the one we want. if (localInstant.Minus(guessInterval.WallOffset) < guessInterval.RawStart) { return(GetZoneInterval(guessInterval.Start - Duration.Epsilon)); } else { return(guessInterval); } }
/// <summary> /// Creates a new <see cref="BclDateTimeZone" /> from a <see cref="TimeZoneInfo"/> from the Base Class Library. /// </summary> /// <param name="bclZone">The original time zone to take information from.</param> /// <returns>A <see cref="BclDateTimeZone"/> wrapping the given <c>TimeZoneInfo</c>.</returns> public static BclDateTimeZone FromTimeZoneInfo([NotNull] TimeZoneInfo bclZone) { Preconditions.CheckNotNull(bclZone, nameof(bclZone)); Offset standardOffset = bclZone.BaseUtcOffset.ToOffset(); var rules = bclZone.GetAdjustmentRules(); if (!bclZone.SupportsDaylightSavingTime || rules.Length == 0) { var fixedInterval = new ZoneInterval(bclZone.StandardName, Instant.BeforeMinValue, Instant.AfterMaxValue, standardOffset, Offset.Zero); return new BclDateTimeZone(bclZone, standardOffset, standardOffset, new SingleZoneIntervalMap(fixedInterval)); } BclAdjustmentRule[] convertedRules = Array.ConvertAll(rules, rule => new BclAdjustmentRule(bclZone, rule)); Offset minRuleOffset = convertedRules.Aggregate(Offset.MaxValue, (min, rule) => Offset.Min(min, rule.Savings + rule.StandardOffset)); Offset maxRuleOffset = convertedRules.Aggregate(Offset.MinValue, (min, rule) => Offset.Max(min, rule.Savings + rule.StandardOffset)); IZoneIntervalMap uncachedMap = BuildMap(convertedRules, standardOffset, bclZone.StandardName); IZoneIntervalMap cachedMap = CachingZoneIntervalMap.CacheMap(uncachedMap, CachingZoneIntervalMap.CacheType.Hashtable); return new BclDateTimeZone(bclZone, Offset.Min(standardOffset, minRuleOffset), Offset.Max(standardOffset, maxRuleOffset), cachedMap); }
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()); }
private static IEnumerable <ZoneInterval> GetBoundIntervals(DateTimeZone zone, Instant start, Instant end) { var intervals = zone.GetZoneIntervals(start, end).ToList(); var first = intervals.First(); if (!first.HasStart || first.Start < start) { intervals[0] = new ZoneInterval(first.Name, start, first.HasEnd ? first.End : (Instant?)null, first.WallOffset, first.Savings); } var last = intervals.Last(); if (!last.HasEnd || last.End > end) { intervals[intervals.Count - 1] = new ZoneInterval(last.Name, last.HasStart ? last.Start : (Instant?)null, end, last.WallOffset, last.Savings); } return(intervals); }
/// <summary> /// Returns the next interval after this one, if it contains the given local instant, or null otherwise. /// </summary> private ZoneInterval GetLaterMatchingInterval(ZoneInterval interval, LocalInstant localInstant) { // Micro-optimization to avoid fetching interval.End multiple times. Seems // to give a performance improvement on x86 at least... Instant intervalEnd = interval.End; if (intervalEnd == Instant.MaxValue) { return(null); } if (intervalEnd.Ticks + minOffsetTicks <= localInstant.Ticks) { ZoneInterval candidate = GetZoneInterval(intervalEnd); if (candidate.Contains(localInstant)) { return(candidate); } } return(null); }
private static IEnumerable <ZoneInterval> GetBoundIntervals(DateTimeZone zone, Instant start, Instant end) { var intervals = zone.GetZoneIntervals(start, end).ToList(); var first = intervals.First(); if (first.Start == Instant.MinValue || first.Start < start) { intervals[0] = new ZoneInterval(first.Name, start, first.End, first.WallOffset, first.Savings); } var last = intervals.Last(); if (last.End == Instant.MaxValue || last.End > end) { intervals[intervals.Count - 1] = new ZoneInterval(last.Name, last.Start, end, last.WallOffset, last.Savings); } return(intervals); }
/// <summary> /// Builds a PartialZoneIntervalMap wrapping the given zone interval, taking its start and end as the start and end of /// the portion of the time line handled by the partial map. /// </summary> internal static PartialZoneIntervalMap ForZoneInterval(ZoneInterval interval) => new PartialZoneIntervalMap(interval.RawStart, interval.RawEnd, new SingleZoneIntervalMap(interval));
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 next interval after this one, if it contains the given local instant, or null otherwise. /// </summary> private ZoneInterval GetLaterMatchingInterval(ZoneInterval interval, LocalInstant localInstant) { // Micro-optimization to avoid fetching interval.End multiple times. Seems // to give a performance improvement on x86 at least... Instant intervalEnd = interval.End; if (intervalEnd == Instant.MaxValue) { return null; } if (intervalEnd.Ticks + minOffsetTicks <= localInstant.Ticks) { ZoneInterval candidate = GetZoneInterval(intervalEnd); if (candidate.Contains(localInstant)) { return candidate; } } return null; }
private ZonedDateTime BuildZonedDateTime(ZoneInterval interval) => new ZonedDateTime(LocalDateTime.WithOffset(interval.WallOffset), Zone);
public void IsoLocalStartAndEnd_OutOfRange() { var interval = new ZoneInterval("All time", Instant.MinValue, null, Offset.FromHours(-1), Offset.Zero); Assert.Throws<OverflowException>(() => interval.IsoLocalStart.ToString()); interval = new ZoneInterval("All time", null, Instant.MaxValue, Offset.FromHours(11), Offset.Zero); Assert.Throws<OverflowException>(() => interval.IsoLocalEnd.ToString()); }
/// <summary> /// Initializes a new instance of the <see cref="HashCacheNode"/> class. /// </summary> /// <param name="interval">The zone interval.</param> /// <param name="period"></param> /// <param name="previous">The previous <see cref="HashCacheNode"/> node.</param> private HashCacheNode(ZoneInterval interval, int period, HashCacheNode previous) { this.Period = period; this.Interval = interval; this.Previous = previous; }
private static PrecalculatedDateTimeZone CreatePrecalculatedDateTimeZone(string id, IList<ZoneTransition> transitions, Instant tailZoneStart, DateTimeZone tailZone) { // Convert the transitions to intervals int size = transitions.Count; var intervals = new ZoneInterval[size]; for (int i = 0; i < size; i++) { var transition = transitions[i]; var endInstant = i == size - 1 ? tailZoneStart : transitions[i + 1].Instant; intervals[i] = new ZoneInterval(transition.Name, transition.Instant, endInstant, transition.WallOffset, transition.Savings); } return new PrecalculatedDateTimeZone(id, intervals, tailZone); }
internal SingleZoneIntervalMap(ZoneInterval interval) { this.interval = interval; }
/// <summary> /// Gets the zone interval's start time in DateTimeKind.Utc ticks /// </summary> private static long GetDateTimeUtcTicks(ZoneInterval zoneInterval) { // can't convert these values directly to date times, so just shortcut these here // we set the min value to one since the logic in the ctor will decrement this value to // determine the last instant BEFORE the discontinuity if (zoneInterval.Start == Instant.MinValue) return 1; if (zoneInterval.Start == Instant.MaxValue) return DateTime.MaxValue.Ticks; return zoneInterval.Start.ToDateTimeUtc().Ticks; }
/// <summary> /// Initializes a new instance of the <see cref="FixedDateTimeZone"/> class. /// </summary> /// <remarks>The name (for the <see cref="ZoneInterval"/>) is deemed to be the same as the ID.</remarks> /// <param name="id">The id.</param> /// <param name="offset">The offset.</param> /// <param name="name">The name to use in the sole <see cref="ZoneInterval"/> in this zone.</param> public FixedDateTimeZone([NotNull] string id, Offset offset, [NotNull] string name) : base(id, true, offset, offset) { interval = new ZoneInterval(name, Instant.BeforeMinValue, Instant.AfterMaxValue, offset, Offset.Zero); }
/// <summary> /// Reads a time zone from the specified reader. /// </summary> /// <param name="reader">The reader.</param> /// <param name="id">The id.</param> /// <returns>The time zone.</returns> internal static DateTimeZone Read([Trusted] [NotNull] IDateTimeZoneReader reader, [Trusted] [NotNull] string id) { Preconditions.DebugCheckNotNull(reader, nameof(reader)); Preconditions.DebugCheckNotNull(id, nameof(id)); int size = reader.ReadCount(); var periods = new ZoneInterval[size]; // It's not entirely clear why we don't just assume that the first zone interval always starts at Instant.BeforeMinValue // (given that we check that later) but we don't... and changing that now could cause compatibility issues. var start = reader.ReadZoneIntervalTransition(null); for (int i = 0; i < size; i++) { var name = reader.ReadString(); var offset = reader.ReadOffset(); var savings = reader.ReadOffset(); var nextStart = reader.ReadZoneIntervalTransition(start); periods[i] = new ZoneInterval(name, start, nextStart, offset, savings); start = nextStart; } var tailZone = reader.ReadByte() == 1 ? StandardDaylightAlternatingMap.Read(reader) : null; return new PrecalculatedDateTimeZone(id, periods, tailZone); }
internal SkippedMappingResult(ZoneInterval beforeTransition, ZoneInterval afterTransition) : base(ResultType.Skipped) { this.beforeTransition = beforeTransition; this.afterTransition = afterTransition; }
public void IsoLocalStartAndEnd_Infinite() { var interval = new ZoneInterval("All time", null, null, Offset.Zero, Offset.Zero); Assert.Throws<InvalidOperationException>(() => interval.IsoLocalStart.ToString()); Assert.Throws<InvalidOperationException>(() => interval.IsoLocalEnd.ToString()); }
private void CheckMapping(ZoneLocalMapping mapping, ZoneInterval earlyInterval, ZoneInterval lateInterval, int count) { Assert.AreEqual(earlyInterval, mapping.EarlyInterval); Assert.AreEqual(lateInterval, mapping.LateInterval); Assert.AreEqual(count, mapping.Count); }
internal static ZoneLocalMapping SkippedResult(ZoneInterval beforeTransition, ZoneInterval afterTransition) { return new SkippedMappingResult(beforeTransition, afterTransition); }
/// <summary> /// Returns the interval before this one, if it contains the given local instant, or null otherwise. /// </summary> private ZoneInterval GetEarlierMatchingInterval(ZoneInterval interval, LocalInstant localInstant) { // Micro-optimization to avoid fetching interval.Start multiple times. Seems // to give a performance improvement on x86 at least... Instant intervalStart = interval.Start; if (intervalStart == Instant.MinValue) { return null; } // If the tick before this interval started *could* map to a later local instant, let's // get the interval and check whether it actually includes the one we want. Instant endOfPrevious = intervalStart; if (endOfPrevious.Ticks + maxOffsetTicks > localInstant.Ticks) { ZoneInterval candidate = GetZoneInterval(endOfPrevious - Duration.One); if (candidate.Contains(localInstant)) { return candidate; } } return null; }