+
/*
* System.TimeZoneInfo
*
using System;
using System.Runtime.CompilerServices;
+using System.Threading;
#if !INSIDE_CORLIB && NET_4_0
static TimeZoneInfo local;
public static TimeZoneInfo Local {
get {
- if (local == null) {
+ var l = local;
+ if (l == null) {
+ l = CreateLocal ();
+ if (l == null)
+ throw new TimeZoneNotFoundException ();
+
+ if (Interlocked.CompareExchange (ref local, l, null) != null)
+ l = local;
+ }
+
+ return l;
+ }
+ }
+
+ static TimeZoneInfo CreateLocal ()
+ {
#if MONODROID
- local = ZoneInfoDB.Default;
+ return AndroidTimeZones.Default;
#elif MONOTOUCH
- using (Stream stream = GetMonoTouchData (null)) {
- local = BuildFromStream ("Local", stream);
- }
+ using (Stream stream = GetMonoTouchData (null)) {
+ return BuildFromStream ("Local", stream);
+ }
#elif LIBC
- try {
- local = FindSystemTimeZoneByFileName ("Local", "/etc/localtime");
- } catch {
- try {
- local = FindSystemTimeZoneByFileName ("Local", Path.Combine (TimeZoneDirectory, "localtime"));
- } catch {
- throw new TimeZoneNotFoundException ();
- }
- }
-#else
- if (IsWindows && LocalZoneKey != null) {
- string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName");
- name = TrimSpecial (name);
- if (name != null)
- local = TimeZoneInfo.FindSystemTimeZoneById (name);
- }
-
- if (local == null)
- throw new TimeZoneNotFoundException ();
-#endif
+ try {
+ return FindSystemTimeZoneByFileName ("Local", "/etc/localtime");
+ } catch {
+ try {
+ return FindSystemTimeZoneByFileName ("Local", Path.Combine (TimeZoneDirectory, "localtime"));
+ } catch {
+ return null;
}
- return local;
}
+#else
+ if (IsWindows && LocalZoneKey != null) {
+ string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName");
+ name = TrimSpecial (name);
+ if (name != null)
+ return TimeZoneInfo.FindSystemTimeZoneById (name);
+ }
+
+ return null;
+#endif
}
string standardDisplayName;
public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo destinationTimeZone)
{
- return ConvertTime (dateTime, TimeZoneInfo.Local, destinationTimeZone);
+ return ConvertTime (dateTime, dateTime.Kind == DateTimeKind.Utc ? TimeZoneInfo.Utc : TimeZoneInfo.Local, destinationTimeZone);
}
public static DateTime ConvertTime (DateTime dateTime, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone)
{
+ if (sourceTimeZone == null)
+ throw new ArgumentNullException ("sourceTimeZone");
+
+ if (destinationTimeZone == null)
+ throw new ArgumentNullException ("destinationTimeZone");
+
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 (sourceTimeZone == null)
- throw new ArgumentNullException ("sourceTimeZone");
-
- if (destinationTimeZone == null)
- throw new ArgumentNullException ("destinationTimeZone");
-
if (dateTime.Kind == DateTimeKind.Local && sourceTimeZone == TimeZoneInfo.Local && destinationTimeZone == TimeZoneInfo.Local)
return dateTime;
DateTime utc = ConvertTimeToUtc (dateTime);
- if (destinationTimeZone == TimeZoneInfo.Utc)
- return utc;
-
- return ConvertTimeFromUtc (utc, destinationTimeZone);
-
+ if (destinationTimeZone != TimeZoneInfo.Utc) {
+ utc = ConvertTimeFromUtc (utc, destinationTimeZone);
+ if (dateTime.Kind == DateTimeKind.Unspecified)
+ return DateTime.SpecifyKind (utc, DateTimeKind.Unspecified);
+ }
+
+ return utc;
}
public static DateTimeOffset ConvertTime(DateTimeOffset dateTimeOffset, TimeZoneInfo destinationTimeZone)
if (this == TimeZoneInfo.Utc)
return DateTime.SpecifyKind (dateTime, DateTimeKind.Utc);
-
+
//FIXME: do not rely on DateTime implementation !
- if (this == TimeZoneInfo.Local)
+ if (this == TimeZoneInfo.Local)
+ {
+#if NET_4_0
+ return dateTime.ToLocalTime ();
+#else
return DateTime.SpecifyKind (dateTime.ToLocalTime (), DateTimeKind.Unspecified);
+#endif
+ }
+
AdjustmentRule rule = GetApplicableRule (dateTime);
-
if (rule != null && IsDaylightSavingTime (DateTime.SpecifyKind (dateTime, DateTimeKind.Utc)))
return DateTime.SpecifyKind (dateTime + BaseUtcOffset + rule.DaylightDelta , DateTimeKind.Unspecified);
else
}
#endif
#if MONODROID
- var timeZoneInfo = ZoneInfoDB.GetTimeZone (id);
+ var timeZoneInfo = AndroidTimeZones.GetTimeZone (id);
if (timeZoneInfo == null)
throw new TimeZoneNotFoundException ();
return timeZoneInfo;
}
#endif
- public static TimeZoneInfo FromSerializedString (string source)
- {
- throw new NotImplementedException ();
- }
-
public AdjustmentRule [] GetAdjustmentRules ()
{
if (!supportsDaylightSavingTime)
public void GetObjectData (SerializationInfo info, StreamingContext context)
#endif
{
- throw new NotImplementedException ();
+ if (info == null)
+ throw new ArgumentNullException ("info");
+ info.AddValue ("Id", id);
+ info.AddValue ("DisplayName", displayName);
+ info.AddValue ("StandardName", standardDisplayName);
+ info.AddValue ("DaylightName", daylightDisplayName);
+ info.AddValue ("BaseUtcOffset", baseUtcOffset);
+ info.AddValue ("AdjustmentRules", adjustmentRules);
+ info.AddValue ("SupportsDaylightSavingTime", SupportsDaylightSavingTime);
}
//FIXME: change this to a generic Dictionary and allow caching for FindSystemTimeZoneById
}
#endif
#if MONODROID
- foreach (string id in ZoneInfoDB.GetAvailableIds ()) {
- var tz = ZoneInfoDB.GetTimeZone (id);
+ foreach (string id in AndroidTimeZones.GetAvailableIds ()) {
+ var tz = AndroidTimeZones.GetTimeZone (id);
if (tz != null)
systemTimeZones.Add (tz);
}
#elif MONOTOUCH
if (systemTimeZones.Count == 0) {
foreach (string name in GetMonoTouchNames ()) {
- using (Stream stream = GetMonoTouchData (name)) {
+ using (Stream stream = GetMonoTouchData (name, false)) {
+ if (stream == null)
+ continue;
systemTimeZones.Add (BuildFromStream (name, stream));
}
}
throw new NotImplementedException ();
}
+ bool IsInDSTForYear (AdjustmentRule rule, DateTime dateTime, int year)
+ {
+ DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, year);
+ DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
+ if (dateTime.Kind == DateTimeKind.Utc) {
+ DST_start -= BaseUtcOffset;
+ DST_end -= (BaseUtcOffset + rule.DaylightDelta);
+ }
+
+ return (dateTime >= DST_start && dateTime < DST_end);
+ }
+
public bool IsDaylightSavingTime (DateTime dateTime)
{
if (dateTime.Kind == DateTimeKind.Local && IsInvalidTime (dateTime))
if (this == TimeZoneInfo.Utc)
return false;
-
+
if (!SupportsDaylightSavingTime)
return false;
+
//FIXME: do not rely on DateTime implementation !
if ((dateTime.Kind == DateTimeKind.Local || dateTime.Kind == DateTimeKind.Unspecified) && this == TimeZoneInfo.Local)
return dateTime.IsDaylightSavingTime ();
-
+
//FIXME: do not rely on DateTime implementation !
if (dateTime.Kind == DateTimeKind.Local && this != TimeZoneInfo.Utc)
return IsDaylightSavingTime (DateTime.SpecifyKind (dateTime.ToUniversalTime (), DateTimeKind.Utc));
-
+
AdjustmentRule rule = GetApplicableRule (dateTime.Date);
if (rule == null)
return false;
- DateTime DST_start = TransitionPoint (rule.DaylightTransitionStart, dateTime.Year);
- DateTime DST_end = TransitionPoint (rule.DaylightTransitionEnd, dateTime.Year + ((rule.DaylightTransitionStart.Month < rule.DaylightTransitionEnd.Month) ? 0 : 1));
- if (dateTime.Kind == DateTimeKind.Utc) {
- DST_start -= BaseUtcOffset;
- DST_end -= (BaseUtcOffset + rule.DaylightDelta);
- }
+ // Check whether we're in the dateTime year's DST period
+ if (IsInDSTForYear (rule, dateTime, dateTime.Year))
+ return true;
- return (dateTime >= DST_start && dateTime < DST_end);
+ // We might be in the dateTime previous year's DST period
+ return IsInDSTForYear (rule, dateTime, dateTime.Year - 1);
}
public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
public void OnDeserialization (object sender)
#endif
{
- throw new NotImplementedException ();
+ try {
+ TimeZoneInfo.Validate (id, baseUtcOffset, adjustmentRules);
+ } catch (ArgumentException ex) {
+ throw new SerializationException ("invalid serialization data", ex);
+ }
+ }
+
+ private static void Validate (string id, TimeSpan baseUtcOffset, AdjustmentRule [] adjustmentRules)
+ {
+ if (id == null)
+ throw new ArgumentNullException ("id");
+
+ if (id == String.Empty)
+ throw new ArgumentException ("id parameter is an empty string");
+
+ if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
+ throw new ArgumentException ("baseUtcOffset parameter does not represent a whole number of minutes");
+
+ if (baseUtcOffset > new TimeSpan (14, 0, 0) || baseUtcOffset < new TimeSpan (-14, 0, 0))
+ throw new ArgumentOutOfRangeException ("baseUtcOffset parameter is greater than 14 hours or less than -14 hours");
+
+#if STRICT
+ if (id.Length > 32)
+ throw new ArgumentException ("id parameter shouldn't be longer than 32 characters");
+#endif
+
+ if (adjustmentRules != null && adjustmentRules.Length != 0) {
+ AdjustmentRule prev = null;
+ foreach (AdjustmentRule current in adjustmentRules) {
+ if (current == null)
+ throw new InvalidTimeZoneException ("one or more elements in adjustmentRules are null");
+
+ if ((baseUtcOffset + current.DaylightDelta < new TimeSpan (-14, 0, 0)) ||
+ (baseUtcOffset + current.DaylightDelta > new TimeSpan (14, 0, 0)))
+ throw new InvalidTimeZoneException ("Sum of baseUtcOffset and DaylightDelta of one or more object in adjustmentRules array is greater than 14 or less than -14 hours;");
+
+ if (prev != null && prev.DateStart > current.DateStart)
+ throw new InvalidTimeZoneException ("adjustment rules specified in adjustmentRules parameter are not in chronological order");
+
+ if (prev != null && prev.DateEnd > current.DateStart)
+ throw new InvalidTimeZoneException ("some adjustment rules in the adjustmentRules parameter overlap");
+
+ if (prev != null && prev.DateEnd == current.DateStart)
+ throw new InvalidTimeZoneException ("a date can have multiple adjustment rules applied to it");
+
+ prev = current;
+ }
+ }
}
- public string ToSerializedString ()
+ public override string ToString ()
{
- throw new NotImplementedException ();
+ return DisplayName;
}
- public override string ToString ()
+ private TimeZoneInfo (SerializationInfo info, StreamingContext context)
{
- return DisplayName;
+ if (info == null)
+ throw new ArgumentNullException ("info");
+ id = (string) info.GetValue ("Id", typeof (string));
+ displayName = (string) info.GetValue ("DisplayName", typeof (string));
+ standardDisplayName = (string) info.GetValue ("StandardName", typeof (string));
+ daylightDisplayName = (string) info.GetValue ("DaylightName", typeof (string));
+ baseUtcOffset = (TimeSpan) info.GetValue ("BaseUtcOffset", typeof (TimeSpan));
+ adjustmentRules = (TimeZoneInfo.AdjustmentRule []) info.GetValue ("AdjustmentRules", typeof (TimeZoneInfo.AdjustmentRule []));
+ supportsDaylightSavingTime = (bool) info.GetValue ("SupportsDaylightSavingTime", typeof (bool));
}
private TimeZoneInfo (string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule [] adjustmentRules, bool disableDaylightSavingTime)
int day = 1 + (transition.Week - 1) * 7 + (transition.DayOfWeek - first) % 7;
if (day > DateTime.DaysInMonth (year, transition.Month))
day -= 7;
+ if (day < 1)
+ day += 7;
return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
}