/// <summary> /// Creates an <see cref="IComparer{T}"/> for periods, using the given "base" local date/time. /// </summary> /// <remarks> /// Certain periods can't naturally be compared without more context - how "one month" compares to /// "30 days" depends on where you start. In order to compare two periods, the returned comparer /// effectively adds both periods to the "base" specified by <paramref name="baseDateTime"/> and compares /// the results. In some cases this arithmetic isn't actually required - when two periods can be /// converted to durations, the comparer uses that conversion for efficiency. /// </remarks> /// <param name="baseDateTime">The base local date/time to use for comparisons.</param> /// <returns>The new comparer.</returns> public static IComparer <Period> CreateComparer(LocalDateTime baseDateTime) { return(new PeriodComparer(baseDateTime)); }
/// <summary> /// Returns the exact difference between two date/times. /// </summary> /// <remarks> /// If <paramref name="end"/> is before <paramref name="start" />, each property in the returned period /// will be negative. /// </remarks> /// <param name="start">Start date/time</param> /// <param name="end">End date/time</param> /// <returns>The period between the two date and time values, using all units.</returns> public static Period Between(LocalDateTime start, LocalDateTime end) { return(Between(start, end, PeriodUnits.DateAndTime)); }
/// <summary> /// To LocalDateTime /// </summary> /// <param name="lt"></param> /// <param name="calendar"></param> /// <returns></returns> public static LocalDateTime ToLocalDateTime(this LocalTime lt, CalendarSystem calendar) => LocalDateTime.FromDateTime(DateTime.Today, calendar).SetTime(lt);
/// <summary> /// Internal constructor used by other code that has already validated and /// computed the appropriate field values. No further validation is performed. /// </summary> internal ZonedDateTime(LocalDateTime localDateTime, Offset offset, DateTimeZone zone) { this.localDateTime = localDateTime; this.offset = offset; this.zone = zone; }
/// <summary> /// Constructs a new offset date/time with the given local date and time, and the given offset from UTC. /// </summary> /// <param name="localDateTime">Local date and time to represent</param> /// <param name="offset">Offset from UTC</param> public OffsetDateTime(LocalDateTime localDateTime, Offset offset) : this(localDateTime.Date.YearMonthDayCalendar, CombineNanoOfDayAndOffset(localDateTime.NanosecondOfDay, offset)) { }
/// <summary> /// Constructs a new offset date/time with the given local date and time, and the given offset from UTC. /// </summary> /// <param name="localDateTime">Local date and time to represent</param> /// <param name="offset">Offset from UTC</param> public OffsetDateTime(LocalDateTime localDateTime, Offset offset) : this(localDateTime.Date, new OffsetTime(localDateTime.NanosecondOfDay, offset.Seconds)) { }
public static Period Between(LocalDateTime start, LocalDateTime end) => Between(start, end, PeriodUnits.DateAndTime);
public OffsetDateTime WithOffset(Offset offset) { LocalDateTime newLocalDateTime = new LocalDateTime(LocalDateTime.LocalInstant.Minus(this.Offset).Plus(offset), Calendar); return(new OffsetDateTime(newLocalDateTime, offset)); }
/// <summary> /// Creates an <see cref="IComparer{T}"/> for periods, using the given "base" local date/time. /// </summary> /// <remarks> /// Certain periods can't naturally be compared without more context - how "one month" compares to /// "30 days" depends on where you start. In order to compare two periods, the returned comparer /// effectively adds both periods to the "base" specified by <paramref name="baseDateTime"/> and compares /// the results. In some cases this arithmetic isn't actually required - when two periods can be /// converted to durations, the comparer uses that conversion for efficiency. /// </remarks> /// <param name="baseDateTime">The base local date/time to use for comparisons.</param> /// <returns>The new comparer.</returns> public static IComparer <Period?> CreateComparer(LocalDateTime baseDateTime) => new PeriodComparer(baseDateTime);
/// <summary> /// Returns the period between a start and an end date/time, using only the given units. /// </summary> /// <remarks> /// If <paramref name="end"/> is before <paramref name="start" />, each property in the returned period /// will be negative. If the given set of units cannot exactly reach the end point (e.g. finding /// the difference between 1am and 3:15am in hours) the result will be such that adding it to <paramref name="start"/> /// will give a value between <paramref name="start"/> and <paramref name="end"/>. In other words, /// any rounding is "towards start"; this is true whether the resulting period is negative or positive. /// </remarks> /// <param name="start">Start date/time</param> /// <param name="end">End date/time</param> /// <param name="units">Units to use for calculations</param> /// <exception cref="ArgumentException"><paramref name="units"/> is empty or contained unknown values.</exception> /// <exception cref="ArgumentException"><paramref name="start"/> and <paramref name="end"/> use different calendars.</exception> /// <returns>The period between the given date/times, using the given units.</returns> public static Period Between(LocalDateTime start, LocalDateTime end, PeriodUnits units) { Preconditions.CheckArgument(units != 0, nameof(units), "Units must not be empty"); Preconditions.CheckArgument((units & ~PeriodUnits.AllUnits) == 0, nameof(units), "Units contains an unknown value: {0}", units); CalendarSystem calendar = start.Calendar; Preconditions.CheckArgument(calendar.Equals(end.Calendar), nameof(end), "start and end must use the same calendar system"); if (start == end) { return(Zero); } // Adjust for situations like "days between 5th January 10am and 7th January 5am" which should be one // day, because if we actually reach 7th January with date fields, we've overshot. // The date adjustment will always be valid, because it's just moving it towards start. // We need this for all date-based period fields. We could potentially optimize by not doing this // in cases where we've only got time fields... LocalDate endDate = end.Date; if (start < end) { if (start.TimeOfDay > end.TimeOfDay) { endDate = endDate.PlusDays(-1); } } else if (start > end && start.TimeOfDay < end.TimeOfDay) { endDate = endDate.PlusDays(1); } // Optimization for single field switch (units) { case PeriodUnits.Years: return(FromYears(DatePeriodFields.YearsField.UnitsBetween(start.Date, endDate))); case PeriodUnits.Months: return(FromMonths(DatePeriodFields.MonthsField.UnitsBetween(start.Date, endDate))); case PeriodUnits.Weeks: return(FromWeeks(DatePeriodFields.WeeksField.UnitsBetween(start.Date, endDate))); case PeriodUnits.Days: return(FromDays(InternalDaysBetween(start.Date, endDate))); case PeriodUnits.Hours: return(FromHours(TimePeriodField.Hours.UnitsBetween(start, end))); case PeriodUnits.Minutes: return(FromMinutes(TimePeriodField.Minutes.UnitsBetween(start, end))); case PeriodUnits.Seconds: return(FromSeconds(TimePeriodField.Seconds.UnitsBetween(start, end))); case PeriodUnits.Milliseconds: return(FromMilliseconds(TimePeriodField.Milliseconds.UnitsBetween(start, end))); case PeriodUnits.Ticks: return(FromTicks(TimePeriodField.Ticks.UnitsBetween(start, end))); case PeriodUnits.Nanoseconds: return(FromNanoseconds(TimePeriodField.Nanoseconds.UnitsBetween(start, end))); } // Multiple fields LocalDateTime remaining = start; int years = 0, months = 0, weeks = 0, days = 0; if ((units & PeriodUnits.AllDateUnits) != 0) { LocalDate remainingDate = DateComponentsBetween( start.Date, endDate, units, out years, out months, out weeks, out days); remaining = new LocalDateTime(remainingDate, start.TimeOfDay); } if ((units & PeriodUnits.AllTimeUnits) == 0) { return(new Period(years, months, weeks, days)); } // The remainder of the computation is with fixed-length units, so we can do it all with // Duration instead of Local* values. We don't know for sure that this is small though - we *could* // be trying to find the difference between 9998 BC and 9999 CE in nanoseconds... // Where we can optimize, do everything with long arithmetic (as we do for Between(LocalTime, LocalTime)). // Otherwise (rare case), use duration arithmetic. long hours, minutes, seconds, milliseconds, ticks, nanoseconds; var duration = end.ToLocalInstant().TimeSinceLocalEpoch - remaining.ToLocalInstant().TimeSinceLocalEpoch; if (duration.IsInt64Representable) { TimeComponentsBetween(duration.ToInt64Nanoseconds(), units, out hours, out minutes, out seconds, out milliseconds, out ticks, out nanoseconds); } else { hours = UnitsBetween(PeriodUnits.Hours, TimePeriodField.Hours); minutes = UnitsBetween(PeriodUnits.Minutes, TimePeriodField.Minutes); seconds = UnitsBetween(PeriodUnits.Seconds, TimePeriodField.Seconds); milliseconds = UnitsBetween(PeriodUnits.Milliseconds, TimePeriodField.Milliseconds); ticks = UnitsBetween(PeriodUnits.Ticks, TimePeriodField.Ticks); nanoseconds = UnitsBetween(PeriodUnits.Ticks, TimePeriodField.Nanoseconds); } return(new Period(years, months, weeks, days, hours, minutes, seconds, milliseconds, ticks, nanoseconds)); long UnitsBetween(PeriodUnits mask, TimePeriodField timeField) { if ((mask & units) == 0) { return(0); } long value = timeField.GetUnitsInDuration(duration); duration -= timeField.ToDuration(value); return(value); } }
/// <summary> /// Constructs a single-point date time interval that starts and ends at the given <see cref="LocalDateTime"/>. /// </summary> /// <param name="point">The single date time included in the interval.</param> /// <returns>A single-point date time interval only including the given date time.</returns> public DateTimeInterval(LocalDateTime point) { Start = End = point; }
/// <summary> /// Maps the given <see cref="LocalDateTime"/> to the corresponding <see cref="ZonedDateTime"/> in a lenient /// manner: ambiguous values map to the later of the alternatives, and "skipped" values map to the start of the /// zone interval after the "gap". /// </summary> /// <remarks> /// See <see cref="AtStrictly"/> and <see cref="ResolveLocal"/> for alternative ways to map a local time to a /// specific instant. /// </remarks> /// <param name="localDateTime">The local date/time to map.</param> /// <returns>The unambiguous mapping if there is one, the later result if the mapping is ambiguous, /// or the start of the later zone interval if the given local date/time is skipped.</returns> public ZonedDateTime AtLeniently(LocalDateTime localDateTime) { return(ResolveLocal(localDateTime, Resolvers.LenientResolver)); }
/// <summary> /// Maps the given <see cref="LocalDateTime"/> to the corresponding <see cref="ZonedDateTime"/>, if and only if /// that mapping is unambiguous in this time zone. Otherwise, <see cref="SkippedTimeException"/> or /// <see cref="AmbiguousTimeException"/> is thrown, depending on whether the mapping is ambiguous or the local /// date/time is skipped entirely. /// </summary> /// <remarks> /// See <see cref="AtLeniently"/> and <see cref="ResolveLocal"/> for alternative ways to map a local time to a /// specific instant. /// </remarks> /// <param name="localDateTime">The local date and time to map into this time zone.</param> /// <exception cref="SkippedTimeException">The given local date/time is skipped in this time zone.</exception> /// <exception cref="AmbiguousTimeException">The given local date/time is ambiguous in this time zone.</exception> /// <returns>The unambiguous matching <see cref="ZonedDateTime"/> if it exists.</returns> public ZonedDateTime AtStrictly(LocalDateTime localDateTime) { return(MapLocal(localDateTime).Single()); }
internal PeriodComparer(LocalDateTime baseDateTime) { this.baseDateTime = baseDateTime; }
/// <summary> /// Creates a new instance for the given local date/time and time zone. /// </summary> /// <remarks> /// User code is unlikely to need to deliberately call this constructor except /// possibly for testing. /// </remarks> /// <param name="localDateTime">The local date/time which is skipped in the specified time zone.</param> /// <param name="zone">The time zone in which the local date/time does not exist.</param> public SkippedTimeException(LocalDateTime localDateTime, DateTimeZone zone) : base("Local time " + localDateTime + " is invalid in time zone " + zone.Id) { this.localDateTime = localDateTime; this.zone = zone; }
public static OffsetDateTime FromDateTimeOffset(DateTimeOffset dateTimeOffset) { return(new OffsetDateTime(LocalDateTime.FromDateTime(dateTimeOffset.DateTime), Offset.FromTimeSpan(dateTimeOffset.Offset))); }
// Visible for extension methods. internal LocalDate(LocalDateTime localTime) { this.localTime = localTime; }
/// <summary> /// Constructs a new offset date/time with the given local date and time, and the given offset from UTC. /// </summary> /// <param name="localDateTime">Local date and time to represent</param> /// <param name="offset">Offset from UTC</param> public OffsetDateTime(LocalDateTime localDateTime, Offset offset) { this.localDateTime = localDateTime; this.offset = offset; }
/// <summary> /// Maps the given <see cref="LocalDateTime"/> to the corresponding <see cref="ZonedDateTime"/>, following /// the given <see cref="ZoneLocalMappingResolver"/> to handle ambiguity and skipped times. /// </summary> /// <remarks> /// <para> /// This is a convenience method for calling <see cref="MapLocal"/> and passing the result to the resolver. /// Common options for resolvers are provided in the static <see cref="Resolvers"/> class. /// </para> /// <para> /// See <see cref="AtStrictly"/> and <see cref="AtLeniently"/> for alternative ways to map a local time to a /// specific instant. /// </para> /// </remarks> /// <param name="localDateTime">The local date and time to map in this time zone.</param> /// <param name="resolver">The resolver to apply to the mapping.</param> /// <returns>The result of resolving the mapping.</returns> public ZonedDateTime ResolveLocal(LocalDateTime localDateTime, [NotNull] ZoneLocalMappingResolver resolver) { Preconditions.CheckNotNull(resolver, nameof(resolver)); return(resolver(MapLocal(localDateTime))); }
/// <summary> /// Creates a new instance for the given local date/time and time zone. /// </summary> /// <remarks> /// User code is unlikely to need to deliberately call this constructor except /// possibly for testing. /// </remarks> /// <param name="localDateTime">The local date/time which is skipped in the specified time zone.</param> /// <param name="zone">The time zone in which the local date/time does not exist.</param> public SkippedTimeException(LocalDateTime localDateTime, DateTimeZone zone) : base("Local time " + localDateTime + " is invalid in time zone " + Preconditions.CheckNotNull(zone, nameof(zone)).Id) { this.LocalDateTime = localDateTime; this.Zone = zone; }
/// <summary> /// Maps the given <see cref="LocalDateTime"/> to the corresponding <see cref="ZonedDateTime"/>, if and only if /// that mapping is unambiguous in this time zone. Otherwise, <see cref="SkippedTimeException"/> or /// <see cref="AmbiguousTimeException"/> is thrown, depending on whether the mapping is ambiguous or the local /// date/time is skipped entirely. /// </summary> /// <remarks> /// See <see cref="AtLeniently"/> and <see cref="ResolveLocal(LocalDateTime, ZoneLocalMappingResolver)"/> for alternative ways to map a local time to a /// specific instant. /// </remarks> /// <param name="localDateTime">The local date and time to map into this time zone.</param> /// <exception cref="SkippedTimeException">The given local date/time is skipped in this time zone.</exception> /// <exception cref="AmbiguousTimeException">The given local date/time is ambiguous in this time zone.</exception> /// <returns>The unambiguous matching <see cref="ZonedDateTime"/> if it exists.</returns> public ZonedDateTime AtStrictly(LocalDateTime localDateTime) => ResolveLocal(localDateTime, Resolvers.StrictResolver);
/// <summary> /// To LocalDateTime /// </summary> /// <param name="lt"></param> /// <returns></returns> public static LocalDateTime ToLocalDateTime(this LocalTime lt) => LocalDateTime.FromDateTime(DateTime.Today).SetTime(lt);
/// <summary> /// Maps the given <see cref="LocalDateTime"/> to the corresponding <see cref="ZonedDateTime"/> in a lenient /// manner: ambiguous values map to the earlier of the alternatives, and "skipped" values are shifted forward /// by the duration of the "gap". /// </summary> /// <remarks> /// See <see cref="AtStrictly"/> and <see cref="ResolveLocal(LocalDateTime, ZoneLocalMappingResolver)"/> for alternative ways to map a local time to a /// specific instant. /// <para>Note: The behavior of this method was changed in version 2.0 to fit the most commonly seen real-world /// usage pattern. Previous versions returned the later instance of ambiguous values, and returned the start of /// the zone interval after the gap for skipped value. The previous functionality can still be used if desired, /// by using <see cref="ResolveLocal(LocalDateTime, AmbiguousTimeResolver, SkippedTimeResolver)"/> and passing the /// <see cref="Resolvers.ReturnLater"/> and <see cref="Resolvers.ReturnStartOfIntervalAfter"/> resolvers.</para> /// </remarks> /// <param name="localDateTime">The local date/time to map.</param> /// <returns>The unambiguous mapping if there is one, the earlier result if the mapping is ambiguous, /// or the forward-shifted value if the given local date/time is skipped.</returns> public ZonedDateTime AtLeniently(LocalDateTime localDateTime) => ResolveLocal(localDateTime, Resolvers.LenientResolver);
public void Deconstruct(out LocalDateTime localDateTime, out DateTimeZone dateTimeZone, out Offset offset) { localDateTime = LocalDateTime; dateTimeZone = Zone; offset = Offset; }
public static int DaysOff(string date, string time, string rpfTimeZone, string ingestTimeZone) { int tensPlace = 0; DateTime localDt = DateTime.Parse(string.Format("{0} {1}", date, time)); LocalTime localTime = new LocalTime(localDt.Hour, localDt.Minute, localDt.Second); int hour = localDt.Hour; LocalDateTime localDateTime = LocalDateTime.FromDateTime(localDt); DateTimeZoneProviders dtzp = new DateTimeZoneProviders(); DateTimeZone rpfZone = dtzp.Tzdb[rpfTimeZone]; DateTimeZone ingestZone = dtzp.Tzdb[ingestTimeZone]; ZonedDateTime zonedDateTime = localDateTime.InZoneLeniently(rpfZone); Offset rpfOffset = rpfZone.GetUtcOffset(zonedDateTime.ToInstant()); Offset ingestOffset = ingestZone.GetUtcOffset(zonedDateTime.ToInstant()); ZonedDateTime zeroTime = new LocalDateTime(LocalDate.FromDateTime(localDt), new LocalTime(0, 15)).InZoneLeniently(ingestZone); ZonedDateTime elevenTime = new LocalDateTime(LocalDate.FromDateTime(localDt), new LocalTime(11, 15)).InZoneLeniently(ingestZone); ZonedDateTime zeroTimeRpf = new LocalDateTime(LocalDate.FromDateTime(localDt), new LocalTime(0, 15)).InZoneLeniently(rpfZone); ZonedDateTime elevenTimeRpf = new LocalDateTime(LocalDate.FromDateTime(localDt), new LocalTime(11, 15)).InZoneLeniently(rpfZone); Offset zeroOffset = zeroTimeRpf.Offset - zeroTime.Offset; Offset elevenOffset = elevenTimeRpf.Offset - elevenTime.Offset; if (Math.Abs(zeroOffset.Milliseconds) < Math.Abs(elevenOffset.Milliseconds)) { var zeroClock = new LocalTime(0, 0); var lowTime = zeroClock.PlusMilliseconds(-zeroOffset.Milliseconds); var highTime = zeroClock.PlusMilliseconds(-elevenOffset.Milliseconds); if (localTime >= lowTime && localTime < highTime) { tensPlace = 10; } } /* * * at0 get offsets and subtract rpfoffat0 - ingestOffsetat0 (-4) * at11 get offsets and subtract rpfoffat11 - ingestoffat11 (-5) * * if these are different * range is (0 in time) - at0 or (0 - -4) = 4 * to (0 in time) -at11 or (0 - -5) = 5 * * */ var offsetDelta = rpfOffset - ingestOffset; var hrsToAdd = offsetDelta.Milliseconds / NodaConstants.MillisecondsPerHour; var newHour = (hour + hrsToAdd); if (newHour < 0) { return(-1 - tensPlace); } else if (newHour > 24) { return(1 + tensPlace); } else { return(0 + tensPlace); } }