using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Text;
+using System.Globalization;
#if LIBC || MONODROID
using System.IO;
namespace System
{
-#if MOBILE
- [TypeForwardedFrom (Consts.AssemblySystem_Core)]
-#else
- [TypeForwardedFrom (Consts.AssemblySystemCore_3_5)]
-#endif
- [SerializableAttribute]
- public
- sealed partial class TimeZoneInfo : IEquatable<TimeZoneInfo>, ISerializable, IDeserializationCallback
+ partial class TimeZoneInfo
{
TimeSpan baseUtcOffset;
public TimeSpan BaseUtcOffset {
return BuildFromStream ("Local", stream);
}
#else
+#if !NET_2_1
if (IsWindows && LocalZoneKey != null) {
string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName");
+ if (name == null)
+ name = (string)LocalZoneKey.GetValue ("StandardName"); // windows xp
name = TrimSpecial (name);
if (name != null)
return TimeZoneInfo.FindSystemTimeZoneById (name);
}
+#endif
var tz = Environment.GetEnvironmentVariable ("TZ");
if (tz != null) {
/// </summary>
private static string TrimSpecial (string str)
{
+ if (str == null)
+ return str;
var Istart = 0;
while (Istart < str.Length && !char.IsLetterOrDigit(str[Istart])) Istart++;
var Iend = str.Length - 1;
}
#endif
+ private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified)
+ {
+ var resultTicks = date.Ticks + ticks;
+ if (resultTicks < DateTime.MinValue.Ticks || resultTicks > DateTime.MaxValue.Ticks) {
+ result = default (DateTime);
+ return false;
+ }
+
+ result = new DateTime (resultTicks, kind);
+ return true;
+ }
+
public static void ClearCachedData ()
{
local = null;
{
if (destinationTimeZone == null)
throw new ArgumentNullException("destinationTimeZone");
-
+
var utcDateTime = dateTimeOffset.UtcDateTime;
- AdjustmentRule rule = destinationTimeZone.GetApplicableRule (utcDateTime);
-
- if (rule != null && destinationTimeZone.IsDaylightSavingTime(utcDateTime)) {
- var offset = destinationTimeZone.BaseUtcOffset + rule.DaylightDelta;
- return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + offset, offset);
- }
- else {
- return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + destinationTimeZone.BaseUtcOffset, destinationTimeZone.BaseUtcOffset);
- }
+
+ bool isDst;
+ var utcOffset = destinationTimeZone.GetUtcOffset(utcDateTime, out isDst);
+
+ return new DateTimeOffset(DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified) + utcOffset, utcOffset);
}
public static DateTime ConvertTimeBySystemTimeZoneId (DateTime dateTime, string destinationTimeZoneId)
if (this == TimeZoneInfo.Utc)
return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
-
- //FIXME: do not rely on DateTime implementation !
- if (this == TimeZoneInfo.Local)
- {
- return dateTime.ToLocalTime ();
- }
+ var utcOffset = GetUtcOffset (dateTime);
- AdjustmentRule rule = GetApplicableRule (dateTime);
- if (rule != null && IsDaylightSavingTime (DateTime.SpecifyKind (dateTime, DateTimeKind.Utc)))
- return DateTime.SpecifyKind (dateTime + BaseUtcOffset + rule.DaylightDelta , DateTimeKind.Unspecified);
- else
- return DateTime.SpecifyKind (dateTime + BaseUtcOffset, DateTimeKind.Unspecified);
+ var kind = (this == TimeZoneInfo.Local)? DateTimeKind.Local : DateTimeKind.Unspecified;
+
+ DateTime result;
+ if (!TryAddTicks (dateTime, utcOffset.Ticks, out result, kind))
+ return DateTime.SpecifyKind (DateTime.MaxValue, kind);
+
+ return result;
}
public static DateTime ConvertTimeFromUtc (DateTime dateTime, TimeZoneInfo destinationTimeZone)
return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local);
}
+ static internal DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ return ConvertTimeToUtc (dateTime, TimeZoneInfo.Local, flags);
+ }
+
public static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone)
{
- if (sourceTimeZone == null)
- throw new ArgumentNullException ("sourceTimeZone");
+ return ConvertTimeToUtc (dateTime, sourceTimeZone, TimeZoneInfoOptions.None);
+ }
- if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
- throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
+ static DateTime ConvertTimeToUtc (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfoOptions flags)
+ {
+ if ((flags & TimeZoneInfoOptions.NoThrowOnInvalidTime) == 0) {
+ if (sourceTimeZone == null)
+ throw new ArgumentNullException ("sourceTimeZone");
- if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
- throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
+ if (dateTime.Kind == DateTimeKind.Utc && sourceTimeZone != TimeZoneInfo.Utc)
+ throw new ArgumentException ("Kind property of dateTime is Utc but the sourceTimeZone does not equal TimeZoneInfo.Utc");
- if (sourceTimeZone.IsInvalidTime (dateTime))
- throw new ArgumentException ("dateTime parameter is an invalid time");
+ if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone != TimeZoneInfo.Local)
+ throw new ArgumentException ("Kind property of dateTime is Local but the sourceTimeZone does not equal TimeZoneInfo.Local");
+
+ if (sourceTimeZone.IsInvalidTime (dateTime))
+ throw new ArgumentException ("dateTime parameter is an invalid time");
+ }
if (dateTime.Kind == DateTimeKind.Utc)
return dateTime;
- if (sourceTimeZone.IsAmbiguousTime (dateTime) || !sourceTimeZone.IsDaylightSavingTime (dateTime))
- return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset, DateTimeKind.Utc);
- else {
- AdjustmentRule rule = sourceTimeZone.GetApplicableRule (dateTime);
- if (rule != null)
- return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset - rule.DaylightDelta, DateTimeKind.Utc);
- else
- return DateTime.SpecifyKind (dateTime - sourceTimeZone.BaseUtcOffset, DateTimeKind.Utc);
- }
+ bool isDst;
+ var utcOffset = sourceTimeZone.GetUtcOffset (dateTime, out isDst);
+
+ DateTime utcDateTime;
+ if (!TryAddTicks (dateTime, -utcOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
+ return DateTime.SpecifyKind (DateTime.MinValue, DateTimeKind.Utc);
+
+ return utcDateTime;
+ }
+
+ static internal TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out Boolean isAmbiguousLocalDst)
+ {
+ bool isDaylightSavings;
+ return GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst);
}
public static TimeZoneInfo CreateCustomTimeZone (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)
#if !NET_2_1
if (TimeZoneKey != null)
{
+ if (id == "Coordinated Universal Time")
+ id = "UTC"; //windows xp exception for "StandardName" property
RegistryKey key = TimeZoneKey.OpenSubKey (id, false);
if (key == null)
throw new TimeZoneNotFoundException ();
tz = TimeZoneInfo.Local;
bool isTzDst;
- var tzOffset = GetUtcOffset (dateTime, tz, out isTzDst);
+ var tzOffset = GetUtcOffsetHelper (dateTime, tz, out isTzDst);
if (tz == this) {
isDST = isTzDst;
return tzOffset;
}
- var utcTicks = dateTime.Ticks - tzOffset.Ticks;
- if (utcTicks < 0 || utcTicks > DateTime.MaxValue.Ticks)
+ DateTime utcDateTime;
+ if (!TryAddTicks (dateTime, -tzOffset.Ticks, out utcDateTime, DateTimeKind.Utc))
return BaseUtcOffset;
- var utcDateTime = new DateTime (utcTicks, DateTimeKind.Utc);
-
- return GetUtcOffset (utcDateTime, this, out isDST);
+ return GetUtcOffsetHelper (utcDateTime, this, out isDST);
}
- private static TimeSpan GetUtcOffset (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
+ // This is an helper method used by the method above, do not use this on its own.
+ private static TimeSpan GetUtcOffsetHelper (DateTime dateTime, TimeZoneInfo tz, out bool isDST)
{
if (dateTime.Kind == DateTimeKind.Local && tz != TimeZoneInfo.Local)
throw new Exception ();
return tz.BaseUtcOffset;
}
- var stdTicks = dateTime.Ticks - tz.BaseUtcOffset.Ticks;
- if (stdTicks < 0 || stdTicks > DateTime.MaxValue.Ticks)
+ DateTime stdUtcDateTime;
+ if (!TryAddTicks (dateTime, -tz.BaseUtcOffset.Ticks, out stdUtcDateTime, DateTimeKind.Utc))
return tz.BaseUtcOffset;
- var stdUtcDateTime = new DateTime (stdTicks, DateTimeKind.Utc);
var tzRule = tz.GetApplicableRule (stdUtcDateTime);
DateTime dstUtcDateTime = DateTime.MinValue;
if (tzRule != null) {
- var dstTicks = stdUtcDateTime.Ticks - tzRule.DaylightDelta.Ticks;
- if (dstTicks < 0 || dstTicks > DateTime.MaxValue.Ticks)
+ if (!TryAddTicks (stdUtcDateTime, -tzRule.DaylightDelta.Ticks, out dstUtcDateTime, DateTimeKind.Utc))
return tz.BaseUtcOffset;
-
- dstUtcDateTime = new DateTime (dstTicks, DateTimeKind.Utc);
}
if (tzRule != null && tz.IsInDST (tzRule, stdUtcDateTime) && tz.IsInDST (tzRule, dstUtcDateTime)) {
return isDst;
}
+ internal bool IsDaylightSavingTime (DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ return IsDaylightSavingTime (dateTime);
+ }
+
public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
{
throw new NotImplementedException ();
}
+ internal DaylightTime GetDaylightChanges (int year)
+ {
+ DateTime start = DateTime.MinValue, end = DateTime.MinValue;
+ TimeSpan delta = new TimeSpan ();
+
+ if (transitions != null) {
+ end = DateTime.MaxValue;
+ for (var i = transitions.Count - 1; i >= 0; i--) {
+ var pair = transitions [i];
+ DateTime ttime = pair.Key;
+ TimeType ttype = pair.Value;
+
+ if (ttype.IsDst) {
+ // DaylightTime.Delta is relative to the current BaseUtcOffset.
+ var d = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset;
+ // Handle DST gradients
+ if (start != DateTime.MinValue && delta != d)
+ end = start;
+
+ start = ttime;
+ delta = d;
+
+ if (ttime.Year <= year)
+ break;
+ } else {
+ if (ttime.Year < year)
+ break;
+
+ end = ttime;
+ start = DateTime.MinValue;
+ }
+ }
+
+ // DaylightTime.Start is relative to the Standard time.
+ if (start != DateTime.MinValue)
+ start += BaseUtcOffset;
+
+ // DaylightTime.End is relative to the DST time.
+ if (end != DateTime.MaxValue)
+ end += BaseUtcOffset + delta;
+ } else {
+ AdjustmentRule rule = null;
+ foreach (var r in GetAdjustmentRules ()) {
+ if (r.DateEnd.Year < year)
+ continue;
+ if (r.DateStart.Year > year)
+ break;
+ rule = r;
+ }
+ if (rule != null) {
+ start = TransitionPoint (rule.DaylightTransitionStart, year);
+ end = TransitionPoint (rule.DaylightTransitionEnd, year);
+ delta = rule.DaylightDelta;
+ }
+ }
+
+ if (start == DateTime.MinValue || end == DateTime.MinValue)
+ return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ());
+
+ return new DaylightTime (start, end, delta);
+ }
+
public bool IsInvalidTime (DateTime dateTime)
{
if (dateTime.Kind == DateTimeKind.Utc)
if (dateTime >= tpoint && dateTime < tpoint + rule.DaylightDelta)
return true;
}
-
+
return false;
}
private AdjustmentRule GetApplicableRule (DateTime dateTime)
{
- //Transitions are always in standard time
+ //Applicable rules are in standard time
DateTime date = dateTime;
- if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
- date = date.ToUniversalTime () + BaseUtcOffset;
- else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc)
- date = date + BaseUtcOffset;
+ if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
+ if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date))
+ return null;
+ } else if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc) {
+ if (!TryAddTicks (date, BaseUtcOffset.Ticks, out date))
+ return null;
+ }
// get the date component of the datetime
date = date.Date;
if (transitions == null)
return false;
- //Transitions are always in standard time
+ //Transitions are in UTC
DateTime date = dateTime;
- if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local)
- date = date.ToUniversalTime () + BaseUtcOffset;
+ if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Local) {
+ if (!TryAddTicks (date.ToUniversalTime (), BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
+ return false;
+ }
- if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc)
- date = date + BaseUtcOffset;
+ if (dateTime.Kind != DateTimeKind.Utc) {
+ if (!TryAddTicks (date, -BaseUtcOffset.Ticks, out date, DateTimeKind.Utc))
+ return false;
+ }
for (var i = transitions.Count - 1; i >= 0; i--) {
var pair = transitions [i];
if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
dst_end -= new TimeSpan (24, 0, 0);
+ /*
+ * AdjustmentRule specifies a DST period that starts and ends within a year.
+ * When we have a DST period longer than a year, the generated AdjustmentRule may not be usable.
+ * Thus we fallback to the transitions.
+ */
+ if (dst_start.AddYears (1) < dst_end)
+ storeTransition = true;
+
DateTime dateStart, dateEnd;
if (dst_start.Month < 7)
dateStart = new DateTime (dst_start.Year, 1, 1);
} else {
if (daylightDisplayName != ttype.Name)
daylightDisplayName = ttype.Name;
- if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds)
- dstDelta = new TimeSpan(0, 0, ttype.Offset) - baseUtcOffset;
+ if (dstDelta.TotalSeconds != ttype.Offset - baseUtcOffset.TotalSeconds) {
+ // Round to nearest minute, since it's not possible to create an adjustment rule
+ // with sub-minute precision ("The TimeSpan parameter cannot be specified more precisely than whole minutes.")
+ // This happens with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916.
+ dstDelta = new TimeSpan (0, 0, ttype.Offset - ttype.Offset % 60) - baseUtcOffset;
+ }
dst_start = ttime;
dst_observed = true;
DateTime date_time = new DateTime (1970, 1, 1);
return date_time.AddSeconds (unix_time);
}
+
+#region reference sources
+ // Shortcut for TimeZoneInfo.Local.GetUtcOffset
+ internal static TimeSpan GetLocalUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ bool dst;
+ return Local.GetUtcOffset (dateTime, out dst);
+ }
+
+ internal TimeSpan GetUtcOffset(DateTime dateTime, TimeZoneInfoOptions flags)
+ {
+ bool dst;
+ return GetUtcOffset (dateTime, out dst);
+ }
+
+ static internal TimeSpan GetUtcOffsetFromUtc (DateTime time, TimeZoneInfo zone, out Boolean isDaylightSavings, out Boolean isAmbiguousLocalDst)
+ {
+ isDaylightSavings = false;
+ isAmbiguousLocalDst = false;
+ TimeSpan baseOffset = zone.BaseUtcOffset;
+
+ if (zone.IsAmbiguousTime (time)) {
+ isAmbiguousLocalDst = true;
+ return baseOffset;
+ }
+
+ return zone.GetUtcOffset (time, out isDaylightSavings);
+ }
+#endregion
}
struct TimeType {