X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem%2FTimeZoneInfo.cs;h=d3fc6ff2c6615a6614d78497aab908a3d13d5bd6;hb=0db4b09607843d097a6f60f5c8101bb7ef8f3866;hp=4b82ab77416132a00d4a844f23d3a0f21ae453a8;hpb=cc40e2e17dc6fd2dae6d3a4c7d7ab43570d63546;p=mono.git diff --git a/mcs/class/corlib/System/TimeZoneInfo.cs b/mcs/class/corlib/System/TimeZoneInfo.cs index 4b82ab77416..d3fc6ff2c66 100644 --- a/mcs/class/corlib/System/TimeZoneInfo.cs +++ b/mcs/class/corlib/System/TimeZoneInfo.cs @@ -33,13 +33,10 @@ using System.Threading; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.Serialization; +using System.Runtime.InteropServices; using System.Text; using System.Globalization; - -#if LIBC || MONODROID using System.IO; -using Mono; -#endif using Microsoft.Win32; @@ -93,16 +90,62 @@ namespace System */ private List> transitions; - static TimeZoneInfo CreateLocal () + private static bool libcNotFound; + + [DllImport ("libc")] + private static extern int readlink (string path, byte[] buffer, int buflen); + + private static string readlink (string path) { -#if MONODROID - return AndroidTimeZones.Local; -#elif MONOTOUCH - using (Stream stream = GetMonoTouchData (null)) { - return BuildFromStream ("Local", stream); + if (libcNotFound) + return null; + + byte[] buf = new byte [512]; + int ret; + + try { + ret = readlink (path, buf, buf.Length); + } catch (DllNotFoundException e) { + libcNotFound = true; + return null; } -#else -#if !NET_2_1 + + if (ret == -1) return null; + char[] cbuf = new char [512]; + int chars = System.Text.Encoding.Default.GetChars (buf, 0, ret, cbuf, 0); + return new String (cbuf, 0, chars); + } + + private static bool TryGetNameFromPath (string path, out string name) + { + name = null; + var linkPath = readlink (path); + if (linkPath != null) + path = linkPath; + + path = Path.GetFullPath (path); + + if (string.IsNullOrEmpty (TimeZoneDirectory)) + return false; + + var baseDir = TimeZoneDirectory; + if (baseDir [baseDir.Length-1] != Path.DirectorySeparatorChar) + baseDir += Path.DirectorySeparatorChar; + + if (!path.StartsWith (baseDir, StringComparison.InvariantCulture)) + return false; + + name = path.Substring (baseDir.Length); + if (name == "localtime") + name = "Local"; + + return true; + } + +#if !MOBILE || MOBILE_STATIC + static TimeZoneInfo CreateLocal () + { +#if !MOBILE_STATIC if (IsWindows && LocalZoneKey != null) { string name = (string)LocalZoneKey.GetValue ("TimeZoneKeyName"); if (name == null) @@ -124,17 +167,70 @@ namespace System } } - try { - return FindSystemTimeZoneByFileName ("Local", "/etc/localtime"); - } catch { + var tzFilePaths = new string [] { + "/etc/localtime", + Path.Combine (TimeZoneDirectory, "localtime")}; + + foreach (var tzFilePath in tzFilePaths) { try { - return FindSystemTimeZoneByFileName ("Local", Path.Combine (TimeZoneDirectory, "localtime")); - } catch { - return null; + string tzName = null; + if (!TryGetNameFromPath (tzFilePath, out tzName)) + tzName = "Local"; + return FindSystemTimeZoneByFileName (tzName, tzFilePath); + } catch (TimeZoneNotFoundException) { + continue; } } + + return Utc; + } + + static TimeZoneInfo FindSystemTimeZoneByIdCore (string id) + { +#if LIBC + string filepath = Path.Combine (TimeZoneDirectory, id); + return FindSystemTimeZoneByFileName (id, filepath); +#else + throw new NotImplementedException (); +#endif + } + + static void GetSystemTimeZonesCore (List systemTimeZones) + { +#if !MOBILE_STATIC + if (TimeZoneKey != null) { + foreach (string id in TimeZoneKey.GetSubKeyNames ()) { + try { + systemTimeZones.Add (FindSystemTimeZoneById (id)); + } catch {} + } + + return; + } +#endif + +#if LIBC + string[] continents = new string [] {"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Australia", "Brazil", "Canada", "Chile", "Europe", "Indian", "Mexico", "Mideast", "Pacific", "US"}; + foreach (string continent in continents) { + try { + foreach (string zonepath in Directory.GetFiles (Path.Combine (TimeZoneDirectory, continent))) { + try { + string id = String.Format ("{0}/{1}", continent, Path.GetFileName (zonepath)); + systemTimeZones.Add (FindSystemTimeZoneById (id)); + } catch (ArgumentNullException) { + } catch (TimeZoneNotFoundException) { + } catch (InvalidTimeZoneException) { + } catch (Exception) { + throw; + } + } + } catch {} + } +#else + throw new NotImplementedException ("This method is not implemented for this platform"); #endif } +#endif string standardDisplayName; public string StandardName { @@ -170,7 +266,7 @@ namespace System #endif private AdjustmentRule [] adjustmentRules; -#if !NET_2_1 +#if !NET_2_1 || MOBILE_STATIC /// /// Determine whether windows of not (taken Stephane Delcroix's code) /// @@ -197,6 +293,7 @@ namespace System return str.Substring (Istart, Iend-Istart+1); } +#if !MOBILE_STATIC static RegistryKey timeZoneKey; static RegistryKey TimeZoneKey { get { @@ -224,6 +321,7 @@ namespace System "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", false); } } +#endif #endif private static bool TryAddTicks (DateTime date, long ticks, out DateTime result, DateTimeKind kind = DateTimeKind.Unspecified) @@ -436,21 +534,8 @@ namespace System // Local requires special logic that already exists in the Local property (bug #326) if (id == "Local") return Local; -#if MONOTOUCH - using (Stream stream = GetMonoTouchData (id)) { - return BuildFromStream (id, stream); - } -#elif MONODROID - var timeZoneInfo = AndroidTimeZones.GetTimeZone (id, id); - if (timeZoneInfo == null) - throw new TimeZoneNotFoundException (); - return timeZoneInfo; -#elif LIBC - string filepath = Path.Combine (TimeZoneDirectory, id); - return FindSystemTimeZoneByFileName (id, filepath); -#else - throw new NotImplementedException (); -#endif + + return FindSystemTimeZoneByIdCore (id); } #if LIBC @@ -464,24 +549,6 @@ namespace System } } #endif -#if LIBC || MONOTOUCH - const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k) - - private static TimeZoneInfo BuildFromStream (string id, Stream stream) - { - byte [] buffer = new byte [BUFFER_SIZE]; - int length = stream.Read (buffer, 0, BUFFER_SIZE); - - if (!ValidTZFile (buffer, length)) - throw new InvalidTimeZoneException ("TZ file too big for the buffer"); - - try { - return ParseTZBuffer (id, buffer, length); - } catch (Exception e) { - throw new InvalidTimeZoneException (e.Message); - } - } -#endif #if !NET_2_1 private static TimeZoneInfo FromRegistryKey (string id, RegistryKey key) @@ -638,61 +705,17 @@ namespace System info.AddValue ("SupportsDaylightSavingTime", SupportsDaylightSavingTime); } - //FIXME: change this to a generic Dictionary and allow caching for FindSystemTimeZoneById - private static List systemTimeZones; + static ReadOnlyCollection systemTimeZones; + public static ReadOnlyCollection GetSystemTimeZones () { if (systemTimeZones == null) { - systemTimeZones = new List (); -#if !NET_2_1 - if (TimeZoneKey != null) { - foreach (string id in TimeZoneKey.GetSubKeyNames ()) { - try { - systemTimeZones.Add (FindSystemTimeZoneById (id)); - } catch {} - } - - return new ReadOnlyCollection (systemTimeZones); - } -#endif -#if MONODROID - foreach (string id in AndroidTimeZones.GetAvailableIds ()) { - var tz = AndroidTimeZones.GetTimeZone (id, id); - if (tz != null) - systemTimeZones.Add (tz); - } -#elif MONOTOUCH - if (systemTimeZones.Count == 0) { - foreach (string name in GetMonoTouchNames ()) { - using (Stream stream = GetMonoTouchData (name, false)) { - if (stream == null) - continue; - systemTimeZones.Add (BuildFromStream (name, stream)); - } - } - } -#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 { - foreach (string zonepath in Directory.GetFiles (Path.Combine (TimeZoneDirectory, continent))) { - try { - string id = String.Format ("{0}/{1}", continent, Path.GetFileName (zonepath)); - systemTimeZones.Add (FindSystemTimeZoneById (id)); - } catch (ArgumentNullException) { - } catch (TimeZoneNotFoundException) { - } catch (InvalidTimeZoneException) { - } catch (Exception) { - throw; - } - } - } catch {} - } -#else - throw new NotImplementedException ("This method is not implemented for this platform"); -#endif + var tz = new List (); + GetSystemTimeZonesCore (tz); + Interlocked.CompareExchange (ref systemTimeZones, new ReadOnlyCollection (tz), null); } - return new ReadOnlyCollection (systemTimeZones); + + return systemTimeZones; } public TimeSpan GetUtcOffset (DateTime dateTime) @@ -892,24 +915,17 @@ namespace System DateTime ttime = pair.Key; TimeType ttype = pair.Value; + if (ttime.Year > year) + continue; + if (ttime.Year < year) + break; + 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; - + delta = new TimeSpan (0, 0, ttype.Offset) - BaseUtcOffset; start = ttime; - delta = d; - - if (ttime.Year <= year) - break; } else { - if (ttime.Year < year) - break; - end = ttime; - start = DateTime.MinValue; } } @@ -918,22 +934,26 @@ namespace System start += BaseUtcOffset; // DaylightTime.End is relative to the DST time. - if (end != DateTime.MaxValue) + if (end != DateTime.MinValue) end += BaseUtcOffset + delta; } else { - AdjustmentRule rule = null; - foreach (var r in GetAdjustmentRules ()) { - if (r.DateEnd.Year < year) + AdjustmentRule first = null, last = null; + + foreach (var rule in GetAdjustmentRules ()) { + if (rule.DateStart.Year != year && rule.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 (rule.DateStart.Year == year) + first = rule; + if (rule.DateEnd.Year == year) + last = rule; } + + if (first == null || last == null) + return new DaylightTime (new DateTime (), new DateTime (), new TimeSpan ()); + + start = TransitionPoint (first.DaylightTransitionStart, year); + end = TransitionPoint (last.DaylightTransitionEnd, year); + delta = first.DaylightDelta; } if (start == DateTime.MinValue || end == DateTime.MinValue) @@ -1176,7 +1196,24 @@ namespace System return adjustmentRules; } -#if LIBC || MONODROID +#if LIBC || MONOTOUCH + const int BUFFER_SIZE = 16384; //Big enough for any tz file (on Oct 2008, all tz files are under 10k) + + private static TimeZoneInfo BuildFromStream (string id, Stream stream) + { + byte [] buffer = new byte [BUFFER_SIZE]; + int length = stream.Read (buffer, 0, BUFFER_SIZE); + + if (!ValidTZFile (buffer, length)) + throw new InvalidTimeZoneException ("TZ file too big for the buffer"); + + try { + return ParseTZBuffer (id, buffer, length); + } catch (Exception e) { + throw new InvalidTimeZoneException (e.Message); + } + } + private static bool ValidTZFile (byte [] buffer, int length) { StringBuilder magic = new StringBuilder (); @@ -1298,8 +1335,10 @@ namespace System 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; + // This happens for instance with Europe/Dublin, which had an offset of 34 minutes and 39 seconds in 1916. + dstDelta = new TimeSpan (0, 0, ttype.Offset) - baseUtcOffset; + if (dstDelta.Ticks % TimeSpan.TicksPerMinute != 0) + dstDelta = TimeSpan.FromMinutes ((long) (dstDelta.TotalMinutes + 0.5f)); } dst_start = ttime;