* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-using System.Runtime.Serialization;
+using System;
+using System.Runtime.CompilerServices;
+
+#if !INSIDE_CORLIB && (NET_4_0 || BOOTSTRAP_NET_4_0 || MOONLIGHT)
+
+[assembly:TypeForwardedTo (typeof(TimeZoneInfo))]
+
+#elif NET_3_5 || (MOBILE && !INSIDE_CORLIB) || (MOONLIGHT && INSIDE_CORLIB)
+
using System.Collections.Generic;
using System.Collections.ObjectModel;
-#if LIBC
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+
+#if LIBC || MONODROID
using System.IO;
-using System.Collections;
using Mono;
#endif
+using Microsoft.Win32;
+
namespace System
{
+#if NET_4_0 || BOOTSTRAP_NET_4_0
+ [TypeForwardedFrom (Consts.AssemblySystemCore_3_5)]
+#elif MOONLIGHT
+ [TypeForwardedFrom (Consts.AssemblySystem_Core)]
+#endif
[SerializableAttribute]
public sealed partial class TimeZoneInfo : IEquatable<TimeZoneInfo>, ISerializable, IDeserializationCallback
{
public static TimeZoneInfo Local {
get {
if (local == null) {
-#if LIBC
+#if MONODROID
+ local = ZoneInfoDB.Default;
+#elif LIBC
try {
local = FindSystemTimeZoneByFileName ("Local", "/etc/localtime");
} catch {
}
#if LIBC
static string timeZoneDirectory = null;
- public static string TimeZoneDirectory {
+ static string TimeZoneDirectory {
get {
if (timeZoneDirectory == null)
timeZoneDirectory = "/usr/share/zoneinfo";
#endif
private AdjustmentRule [] adjustmentRules;
+#if !NET_2_1
+ static RegistryKey timeZoneKey = null;
+ static bool timeZoneKeySet = false;
+ static RegistryKey TimeZoneKey {
+ get {
+ if (!timeZoneKeySet) {
+ int p = (int) Environment.OSVersion.Platform;
+ /* Only use the registry on non-Unix platforms. */
+ if ((p != 4) && (p != 6) && (p != 128))
+ timeZoneKey = Registry.LocalMachine.OpenSubKey (
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
+ false);
+ timeZoneKeySet = true;
+ }
+ return timeZoneKey;
+ }
+ }
+#endif
+
public static void ClearCachedData ()
{
local = null;
//FIXME: this method should check for cached values in systemTimeZones
if (id == null)
throw new ArgumentNullException ("id");
-#if LIBC
+#if !NET_2_1
+ if (TimeZoneKey != null)
+ {
+ RegistryKey key = TimeZoneKey.OpenSubKey (id, false);
+ if (key == null)
+ throw new TimeZoneNotFoundException ();
+ return FromRegistryKey(id, key);
+ }
+#endif
+#if MONODROID
+ return ZoneInfoDB.GetTimeZone (id);
+#elif LIBC
string filepath = Path.Combine (TimeZoneDirectory, id);
return FindSystemTimeZoneByFileName (id, filepath);
#else
}
#if LIBC
- const int BUFFER_SIZE = 8192; //Big enough for any tz file
+ const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k)
private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
{
if (!File.Exists (filepath))
}
if (!ValidTZFile (buffer, length))
- throw new InvalidTimeZoneException ();
+ throw new InvalidTimeZoneException ("TZ file too big for the buffer");
try {
return ParseTZBuffer (id, buffer, length);
}
#endif
+#if !NET_2_1
+ private static TimeZoneInfo FromRegistryKey (string id, RegistryKey key)
+ {
+ byte [] reg_tzi = (byte []) key.GetValue ("TZI");
+
+ if (reg_tzi == null)
+ throw new InvalidTimeZoneException ();
+
+ int bias = BitConverter.ToInt32 (reg_tzi, 0);
+ TimeSpan baseUtcOffset = new TimeSpan (0, -bias, 0);
+
+ string display_name = (string) key.GetValue ("Display");
+ string standard_name = (string) key.GetValue ("Std");
+ string daylight_name = (string) key.GetValue ("Dlt");
+
+ List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
+
+ RegistryKey dst_key = key.OpenSubKey ("Dynamic DST", false);
+ if (dst_key != null) {
+ int first_year = (int) dst_key.GetValue ("FirstEntry");
+ int last_year = (int) dst_key.GetValue ("LastEntry");
+ int year;
+
+ for (year=first_year; year<=last_year; year++) {
+ byte [] dst_tzi = (byte []) dst_key.GetValue (year.ToString ());
+ if (dst_tzi != null) {
+ int start_year = year == first_year ? 1 : year;
+ int end_year = year == last_year ? 9999 : year;
+ ParseRegTzi(adjustmentRules, start_year, end_year, dst_tzi);
+ }
+ }
+ }
+ else
+ ParseRegTzi(adjustmentRules, 1, 9999, reg_tzi);
+
+ return CreateCustomTimeZone (id, baseUtcOffset, display_name, standard_name, daylight_name, ValidateRules (adjustmentRules).ToArray ());
+ }
+
+ private static void ParseRegTzi (List<AdjustmentRule> adjustmentRules, int start_year, int end_year, byte [] buffer)
+ {
+ //int standard_bias = BitConverter.ToInt32 (buffer, 4); /* not sure how to handle this */
+ int daylight_bias = BitConverter.ToInt32 (buffer, 8);
+
+ int standard_year = BitConverter.ToInt16 (buffer, 12);
+ int standard_month = BitConverter.ToInt16 (buffer, 14);
+ int standard_dayofweek = BitConverter.ToInt16 (buffer, 16);
+ int standard_day = BitConverter.ToInt16 (buffer, 18);
+ int standard_hour = BitConverter.ToInt16 (buffer, 20);
+ int standard_minute = BitConverter.ToInt16 (buffer, 22);
+ int standard_second = BitConverter.ToInt16 (buffer, 24);
+ int standard_millisecond = BitConverter.ToInt16 (buffer, 26);
+
+ int daylight_year = BitConverter.ToInt16 (buffer, 28);
+ int daylight_month = BitConverter.ToInt16 (buffer, 30);
+ int daylight_dayofweek = BitConverter.ToInt16 (buffer, 32);
+ int daylight_day = BitConverter.ToInt16 (buffer, 34);
+ int daylight_hour = BitConverter.ToInt16 (buffer, 36);
+ int daylight_minute = BitConverter.ToInt16 (buffer, 38);
+ int daylight_second = BitConverter.ToInt16 (buffer, 40);
+ int daylight_millisecond = BitConverter.ToInt16 (buffer, 42);
+
+ if (standard_month == 0 || daylight_month == 0)
+ return;
+
+ DateTime start_date;
+ DateTime start_timeofday = new DateTime (1, 1, 1, daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
+ TransitionTime start_transition_time;
+
+ if (daylight_year == 0) {
+ start_date = new DateTime (start_year, 1, 1);
+ start_transition_time = TransitionTime.CreateFloatingDateRule (
+ start_timeofday, daylight_month, daylight_day,
+ (DayOfWeek) daylight_dayofweek);
+ }
+ else {
+ start_date = new DateTime (daylight_year, daylight_month, daylight_day,
+ daylight_hour, daylight_minute, daylight_second, daylight_millisecond);
+ start_transition_time = TransitionTime.CreateFixedDateRule (
+ start_timeofday, daylight_month, daylight_day);
+ }
+
+ DateTime end_date;
+ DateTime end_timeofday = new DateTime (1, 1, 1, standard_hour, standard_minute, standard_second, standard_millisecond);
+ TransitionTime end_transition_time;
+
+ if (standard_year == 0) {
+ end_date = new DateTime (end_year, 12, 31);
+ end_transition_time = TransitionTime.CreateFloatingDateRule (
+ end_timeofday, standard_month, standard_day,
+ (DayOfWeek) standard_dayofweek);
+ }
+ else {
+ end_date = new DateTime (standard_year, standard_month, standard_day,
+ standard_hour, standard_minute, standard_second, standard_millisecond);
+ end_transition_time = TransitionTime.CreateFixedDateRule (
+ end_timeofday, standard_month, standard_day);
+ }
+
+ TimeSpan daylight_delta = new TimeSpan(0, -daylight_bias, 0);
+
+ adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (
+ start_date, end_date, daylight_delta,
+ start_transition_time, end_transition_time));
+ }
+#endif
+
public static TimeZoneInfo FromSerializedString (string source)
{
throw new NotImplementedException ();
return hash_code;
}
+#if NET_4_0
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+#else
public void GetObjectData (SerializationInfo info, StreamingContext context)
+#endif
{
throw new NotImplementedException ();
}
{
if (systemTimeZones == null) {
systemTimeZones = new List<TimeZoneInfo> ();
-#if LIBC
+#if !NET_2_1
+ if (TimeZoneKey != null) {
+ foreach (string id in TimeZoneKey.GetSubKeyNames ()) {
+ try {
+ systemTimeZones.Add (FindSystemTimeZoneById (id));
+ } catch {}
+ }
+
+ return new ReadOnlyCollection<TimeZoneInfo> (systemTimeZones);
+ }
+#endif
+#if MONODROID
+ systemTimeZones.AddRange (ZoneInfoDB.GetAvailableIds ()
+ .Select (id => ZoneInfoDB.GetTimeZone (id)));
+#elif LIBC
string[] continents = new string [] {"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Brazil", "Canada", "Chile", "Europe", "Indian", "Mexico", "Mideast", "Pacific", "US"};
foreach (string continent in continents) {
try {
} catch (ArgumentNullException) {
} catch (TimeZoneNotFoundException) {
} catch (InvalidTimeZoneException) {
- } catch (Exception e) {
- if (e is OutOfMemoryException || e is System.Security.SecurityException)
- throw;
- else {
- Console.WriteLine ("Unexpected Exception");
- throw;
- }
+ } catch (Exception) {
+ throw;
}
}
} catch {}
if (other == null)
throw new ArgumentNullException ("other");
+ if ((this.adjustmentRules == null) != (other.adjustmentRules == null))
+ return false;
+
+ if (this.adjustmentRules == null)
+ return true;
+
if (this.BaseUtcOffset != other.BaseUtcOffset)
return false;
DST_end -= (BaseUtcOffset + rule.DaylightDelta);
}
- return (dateTime >= DST_start && dateTime < DST_end)
+ return (dateTime >= DST_start && dateTime < DST_end);
}
public bool IsDaylightSavingTime (DateTimeOffset dateTimeOffset)
return false;
}
+#if NET_4_0
+ void IDeserializationCallback.OnDeserialization (object sender)
+#else
public void OnDeserialization (object sender)
+#endif
{
throw new NotImplementedException ();
}
if (dateTime.Kind == DateTimeKind.Utc && this != TimeZoneInfo.Utc)
date = date + BaseUtcOffset;
- foreach (AdjustmentRule rule in adjustmentRules) {
- if (rule.DateStart > date.Date)
- return null;
- if (rule.DateEnd < date.Date)
- continue;
- return rule;
+ if (adjustmentRules != null) {
+ foreach (AdjustmentRule rule in adjustmentRules) {
+ if (rule.DateStart > date.Date)
+ return null;
+ if (rule.DateEnd < date.Date)
+ continue;
+ return rule;
+ }
}
return null;
}
return new DateTime (year, transition.Month, day) + transition.TimeOfDay.TimeOfDay;
}
-#if LIBC
+ static List<AdjustmentRule> ValidateRules (List<AdjustmentRule> adjustmentRules)
+ {
+ AdjustmentRule prev = null;
+ foreach (AdjustmentRule current in adjustmentRules.ToArray ()) {
+ if (prev != null && prev.DateEnd > current.DateStart) {
+ adjustmentRules.Remove (current);
+ }
+ prev = current;
+ }
+ return adjustmentRules;
+ }
+
+#if LIBC || MONODROID
private static bool ValidTZFile (byte [] buffer, int length)
{
- System.Text.StringBuilder magic = new System.Text.StringBuilder ();
+ StringBuilder magic = new StringBuilder ();
for (int i = 0; i < 4; i++)
magic.Append ((char)buffer [i]);
return true;
}
- private struct TimeType
+ struct TimeType
{
- int offset;
- bool is_dst;
- string abbrev;
-
+ public readonly int Offset;
+ public readonly bool IsDst;
+ public string Name;
+
public TimeType (int offset, bool is_dst, string abbrev)
{
- this.offset = offset;
- this.is_dst = is_dst;
- this.abbrev = abbrev;
+ this.Offset = offset;
+ this.IsDst = is_dst;
+ this.Name = abbrev;
}
- public int Offset {
- get { return offset; }
+ public override string ToString ()
+ {
+ return "offset: " + Offset + "s, is_dst: " + IsDst + ", zone name: " + Name;
}
+ }
- public bool IsDst {
- get { return is_dst; }
- }
+ static int SwapInt32 (int i)
+ {
+ return (((i >> 24) & 0xff)
+ | ((i >> 8) & 0xff00)
+ | ((i << 8) & 0xff0000)
+ | ((i << 24)));
+ }
- public string Name {
- get { return abbrev; }
- }
+ static int ReadBigEndianInt32 (byte [] buffer, int start)
+ {
+ int i = BitConverter.ToInt32 (buffer, start);
+ if (!BitConverter.IsLittleEndian)
+ return i;
- public override string ToString ()
- {
- return "offset: " + offset + "s, is_dst: " + is_dst + ", zone name: " + abbrev;
- }
+ return SwapInt32 (i);
}
private static TimeZoneInfo ParseTZBuffer (string id, byte [] buffer, int length)
{
- DataConverter enc = DataConverter.BigEndian;
-
//Reading the header. 4 bytes for magic, 16 are reserved
- int ttisgmtcnt = enc.GetInt32 (buffer, 20);
- int ttisstdcnt = enc.GetInt32 (buffer, 24);
- int leapcnt = enc.GetInt32 (buffer, 28);
- int timecnt = enc.GetInt32 (buffer, 32);
- int typecnt = enc.GetInt32 (buffer, 36);
- int charcnt = enc.GetInt32 (buffer, 40);
-
- if (length != 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
+ int ttisgmtcnt = ReadBigEndianInt32 (buffer, 20);
+ int ttisstdcnt = ReadBigEndianInt32 (buffer, 24);
+ int leapcnt = ReadBigEndianInt32 (buffer, 28);
+ int timecnt = ReadBigEndianInt32 (buffer, 32);
+ int typecnt = ReadBigEndianInt32 (buffer, 36);
+ int charcnt = ReadBigEndianInt32 (buffer, 40);
+
+ if (length < 44 + timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisgmtcnt)
throw new InvalidTimeZoneException ();
- Hashtable abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
- Hashtable time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
- SortedList transitions = ParseTransitions (buffer, 44, timecnt, time_types);
+ Dictionary<int, string> abbreviations = ParseAbbreviations (buffer, 44 + 4 * timecnt + timecnt + 6 * typecnt, charcnt);
+ Dictionary<int, TimeType> time_types = ParseTimesTypes (buffer, 44 + 4 * timecnt + timecnt, typecnt, abbreviations);
+ List<KeyValuePair<DateTime, TimeType>> transitions = ParseTransitions (buffer, 44, timecnt, time_types);
if (time_types.Count == 0)
throw new InvalidTimeZoneException ();
List<AdjustmentRule> adjustmentRules = new List<AdjustmentRule> ();
for (int i = 0; i < transitions.Count; i++) {
- DateTime ttime = (DateTime) transitions.GetKey (i);
- TimeType ttype = (TimeType) transitions [ttime];
+ var pair = transitions [i];
+ DateTime ttime = pair.Key;
+ TimeType ttype = pair.Value;
if (!ttype.IsDst) {
if (standardDisplayName != ttype.Name || baseUtcOffset.TotalSeconds != ttype.Offset) {
standardDisplayName = ttype.Name;
dst_start += baseUtcOffset;
DateTime dst_end = ttime + baseUtcOffset + dstDelta;
+ //some weird timezone (America/Phoenix) have end dates on Jan 1st
+ if (dst_end.Date == new DateTime (dst_end.Year, 1, 1) && dst_end.Year > dst_start.Year)
+ dst_end -= new TimeSpan (24, 0, 0);
+
DateTime dateStart, dateEnd;
if (dst_start.Month < 7)
dateStart = new DateTime (dst_start.Year, 1, 1);
dateStart = new DateTime (dst_start.Year, 7, 1);
if (dst_end.Month >= 7)
- dateEnd = new DateTime (dst_start.Year, 12, 31);
+ dateEnd = new DateTime (dst_end.Year, 12, 31);
else
dateEnd = new DateTime (dst_end.Year, 6, 30);
+
TransitionTime transition_start = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_start.TimeOfDay, dst_start.Month, dst_start.Day);
TransitionTime transition_end = TransitionTime.CreateFixedDateRule (new DateTime (1, 1, 1) + dst_end.TimeOfDay, dst_end.Month, dst_end.Day);
- adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
+ if (transition_start != transition_end) //y, that happened in Argentina in 1943-1946
+ adjustmentRules.Add (AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, dstDelta, transition_start, transition_end));
}
dst_observed = false;
} else {
}
return CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName);
} else {
- return CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, adjustmentRules.ToArray ());
+ return CreateCustomTimeZone (id, baseUtcOffset, id, standardDisplayName, daylightDisplayName, ValidateRules (adjustmentRules).ToArray ());
}
}
- private static Hashtable ParseAbbreviations (byte [] buffer, int index, int count)
+ static Dictionary<int, string> ParseAbbreviations (byte [] buffer, int index, int count)
{
- Hashtable abbrevs = new Hashtable ();
+ var abbrevs = new Dictionary<int, string> ();
int abbrev_index = 0;
- System.Text.StringBuilder sb = new System.Text.StringBuilder ();
+ var sb = new StringBuilder ();
for (int i = 0; i < count; i++) {
char c = (char) buffer [index + i];
if (c != '\0')
sb.Append (c);
else {
abbrevs.Add (abbrev_index, sb.ToString ());
+ //Adding all the substrings too, as it seems to be used, at least for Africa/Windhoek
+ for (int j = 1; j < sb.Length; j++)
+ abbrevs.Add (abbrev_index + j, sb.ToString (j, sb.Length - j));
abbrev_index = i + 1;
- sb = new System.Text.StringBuilder ();
+ sb = new StringBuilder ();
}
}
return abbrevs;
}
- private static Hashtable ParseTimesTypes (byte [] buffer, int index, int count, Hashtable abbreviations)
+ static Dictionary<int, TimeType> ParseTimesTypes (byte [] buffer, int index, int count, Dictionary<int, string> abbreviations)
{
- DataConverter enc = DataConverter.BigEndian;
- Hashtable types = new Hashtable (count);
+ var types = new Dictionary<int, TimeType> (count);
for (int i = 0; i < count; i++) {
- int offset = enc.GetInt32 (buffer, index + 6 * i);
+ int offset = ReadBigEndianInt32 (buffer, index + 6 * i);
byte is_dst = buffer [index + 6 * i + 4];
byte abbrev = buffer [index + 6 * i + 5];
- types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev] as string));
+ types.Add (i, new TimeType (offset, (is_dst != 0), abbreviations [(int)abbrev]));
}
return types;
}
- private static SortedList ParseTransitions (byte [] buffer, int index, int count, Hashtable time_types)
+ static List<KeyValuePair<DateTime, TimeType>> ParseTransitions (byte [] buffer, int index, int count, Dictionary<int, TimeType> time_types)
{
- DataConverter enc = DataConverter.BigEndian;
- SortedList trans = new SortedList (count);
+ var list = new List<KeyValuePair<DateTime, TimeType>> (count);
for (int i = 0; i < count; i++) {
- int unixtime = enc.GetInt32 (buffer, index + 4 * i);
+ int unixtime = ReadBigEndianInt32 (buffer, index + 4 * i);
DateTime ttime = DateTimeFromUnixTime (unixtime);
byte ttype = buffer [index + 4 * count + i];
- trans.Add (ttime, time_types [(int)ttype]);
+ list.Add (new KeyValuePair<DateTime, TimeType> (ttime, time_types [(int)ttype]));
}
- return trans;
+ return list;
}
- private static DateTime DateTimeFromUnixTime (long unix_time)
+ static DateTime DateTimeFromUnixTime (long unix_time)
{
DateTime date_time = new DateTime (1970, 1, 1);
return date_time.AddSeconds (unix_time);
#endif
}
}
+
+#endif