// Copyright (C) 2025, The Duplicati Team // https://duplicati.com, hello@duplicati.com // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #nullable enable using System; using System.Collections.Generic; using System.Linq; namespace Duplicati.Library.Utility; /// /// Adds the specified number of minutes to the given DateTime, taking into account the time zone's daylight saving time rules. /// A helper class to get the time zone information /// public static class TimeZoneHelper { /// /// A record to display the time zone /// /// The time zone id /// The display name of the time zone /// The base UTC offset of the time zone public record TimeZoneDisplay(string Id, string DisplayName, TimeSpan CurrentUtcOffset); /// /// Get the local (system) time zone /// /// The local time zone public static string GetLocalTimeZone() => TimeZoneInfo.Local.Id; /// /// Get the time zones on this system /// /// The time zones public static IEnumerable GetTimeZones() { foreach (var tz in TimeZoneInfo.GetSystemTimeZones()) yield return new TimeZoneDisplay(tz.Id, tz.DisplayName, tz.GetUtcOffset(DateTime.Now)); } /// /// Get the time zone by id /// /// The time zone id /// The time zone public static TimeZoneInfo? GetTimeZoneById(string id) { try { return TimeZoneInfo.FindSystemTimeZoneById(id); } catch { } return null; } /// /// Search for a time zone /// /// The search string /// The time zone public static TimeZoneInfo? FindTimeZone(string search) { var tzi = GetTimeZoneById(search); if (tzi != null) return tzi; return TimeZoneInfo.GetSystemTimeZones() .FirstOrDefault(tz => tz.Id.Equals(search, StringComparison.OrdinalIgnoreCase) || tz.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)); } /// /// Corrects the time for daylight saving time, by checking if the offset has changed /// /// The timezone to use for the correction /// The time before in UTC /// The time after in UTC /// The corrected time in UTC public static DateTime DSTAwareTimeAdjust(this TimeZoneInfo timeZoneInfo, DateTime before, DateTime after) { var beforeLocal = TimeZoneInfo.ConvertTime(new DateTimeOffset(before, TimeSpan.Zero), timeZoneInfo); var afterLocal = TimeZoneInfo.ConvertTime(new DateTimeOffset(after, TimeSpan.Zero), timeZoneInfo); if (beforeLocal.Offset == afterLocal.Offset) return after; var diff = beforeLocal.Offset - afterLocal.Offset; return after.Add(diff); } /// /// Adds seconds to a time in a specific time zone /// /// The time zone to use for the calculation /// The time to add seconds to /// The number of seconds to add /// The new time public static DateTime DSTAwareAddSeconds(this TimeZoneInfo timeZoneInfo, DateTime dt, long seconds) => DSTAwareTimeAdjust(timeZoneInfo, dt, dt.AddSeconds(seconds)); /// /// Adds the specified number of minutes to the given DateTime, taking into account the time zone's daylight saving time rules. /// /// The time zone information. /// The DateTime to which minutes will be added. /// The number of minutes to add. /// A DateTime that is the result of adding the specified number of minutes to the given DateTime, adjusted for daylight saving time. public static DateTime DSTAwareAddMinutes(this TimeZoneInfo timeZoneInfo, DateTime dt, int minutes) => DSTAwareTimeAdjust(timeZoneInfo, dt, dt.AddMinutes(minutes)); /// /// Adds the specified number of hours to the given DateTime, taking into account the time zone's daylight saving time rules. /// /// The time zone information. /// The DateTime to which hours will be added. /// The number of hours to add. /// A DateTime that is the result of adding the specified number of hours to the given DateTime, adjusted for daylight saving time. public static DateTime DSTAwareAddHours(this TimeZoneInfo timeZoneInfo, DateTime dt, int hours) => DSTAwareTimeAdjust(timeZoneInfo, dt, dt.AddHours(hours)); /// /// Adds the specified number of days to the given DateTime, taking into account the time zone's daylight saving time rules. /// /// The time zone information. /// The DateTime to which days will be added. /// The number of days to add. /// A DateTime that is the result of adding the specified number of days to the given DateTime, adjusted for daylight saving time. public static DateTime DSTAwareAddDays(this TimeZoneInfo timeZoneInfo, DateTime dt, int days) => DSTAwareTimeAdjust(timeZoneInfo, dt, dt.AddDays(days)); /// /// Adds the specified number of months to the given DateTime, taking into account the time zone's daylight saving time rules. /// /// The time zone information. /// The DateTime to which months will be added. /// The number of months to add. /// A DateTime that is the result of adding the specified number of months to the given DateTime, adjusted for daylight saving time. public static DateTime DSTAwareAddMonths(this TimeZoneInfo timeZoneInfo, DateTime dt, int months) => DSTAwareTimeAdjust(timeZoneInfo, dt, dt.AddMonths(months)); /// /// Adds the specified number of years to the given DateTime, taking into account the time zone's daylight saving time rules. /// /// The time zone information. /// The DateTime to which years will be added. /// The number of years to add. /// A DateTime that is the result of adding the specified number of years to the given DateTime, adjusted for daylight saving time. public static DateTime DSTAwareAddYears(this TimeZoneInfo timeZoneInfo, DateTime dt, int years) => DSTAwareTimeAdjust(timeZoneInfo, dt, dt.AddYears(years)); }