2 * System.TimeZoneInfo Android Support
5 * Jonathan Pryor <jpryor@novell.com>
6 * The Android Open Source Project
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
24 using System.Collections.Generic;
26 using System.Runtime.CompilerServices;
27 using System.Runtime.InteropServices;
32 partial class TimeZoneInfo {
35 * Android Timezone support infrastructure.
37 * This is a C# port of org.apache.harmony.luni.internal.util.ZoneInfoDB:
39 * http://android.git.kernel.org/?p=platform/libcore.git;a=blob;f=luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfoDB.java;h=3e7bdc3a952b24da535806d434a3a27690feae26;hb=HEAD
41 * From the ZoneInfoDB source:
43 * However, to conserve disk space the data for all time zones are
44 * concatenated into a single file, and a second file is used to indicate
45 * the starting position of each time zone record. A third file indicates
46 * the version of the zoneinfo databse used to generate the data.
48 * which succinctly describes why we can't just use the LIBC implementation in
49 * TimeZoneInfo.cs -- the "standard Unixy" directory structure is NOT used.
51 static class ZoneInfoDB {
52 const int TimeZoneNameLength = 40;
53 const int TimeZoneIntSize = 4;
55 static readonly string ZoneDirectoryName = Environment.GetEnvironmentVariable ("ANDROID_ROOT") + "/usr/share/zoneinfo/";
56 static readonly string ZoneFileName = ZoneDirectoryName + "zoneinfo.dat";
57 static readonly string IndexFileName = ZoneDirectoryName + "zoneinfo.idx";
58 const string DefaultVersion = "2007h";
59 static readonly string VersionFileName = ZoneDirectoryName + "zoneinfo.version";
61 static readonly object _lock = new object ();
63 static readonly string version;
64 static readonly string[] names;
65 static readonly int[] starts;
66 static readonly int[] lengths;
67 static readonly int[] offsets;
72 version = ReadVersion ();
74 version = DefaultVersion;
78 ReadDatabase (out names, out starts, out lengths, out offsets);
80 names = new string [0];
82 lengths = new int [0];
83 offsets = new int [0];
87 static string ReadVersion ()
89 using (var file = new StreamReader (VersionFileName, Encoding.GetEncoding ("iso-8859-1"))) {
90 return file.ReadToEnd ().Trim ();
94 static void ReadDatabase (out string[] names, out int[] starts, out int[] lengths, out int[] offsets)
96 using (var file = File.OpenRead (IndexFileName)) {
97 var nbuf = new byte [TimeZoneNameLength];
99 int numEntries = (int) (file.Length / (TimeZoneNameLength + 3*TimeZoneIntSize));
101 char[] namebuf = new char [TimeZoneNameLength];
103 names = new string [numEntries];
104 starts = new int [numEntries];
105 lengths = new int [numEntries];
106 offsets = new int [numEntries];
108 for (int i = 0; i < numEntries; ++i) {
109 Fill (file, nbuf, nbuf.Length);
111 for (namelen = 0; namelen < nbuf.Length; ++namelen) {
112 if (nbuf [namelen] == '\0')
114 namebuf [namelen] = (char) (nbuf [namelen] & 0xFF);
117 names [i] = new string (namebuf, 0, namelen);
118 starts [i] = ReadInt32 (file, nbuf);
119 lengths [i] = ReadInt32 (file, nbuf);
120 offsets [i] = ReadInt32 (file, nbuf);
125 static void Fill (Stream stream, byte[] nbuf, int required)
127 int read, offset = 0;
128 while (offset < required && (read = stream.Read (nbuf, offset, required - offset)) > 0)
130 if (read != required)
131 throw new EndOfStreamException ("Needed to read " + required + " bytes; read " + read + " bytes");
134 // From java.io.RandomAccessFioe.readInt(), as we need to use the same
135 // byte ordering as Java uses.
136 static int ReadInt32 (Stream stream, byte[] nbuf)
138 Fill (stream, nbuf, 4);
139 return ((nbuf [0] & 0xff) << 24) + ((nbuf [1] & 0xff) << 16) +
140 ((nbuf [2] & 0xff) << 8) + (nbuf [3] & 0xff);
143 internal static string Version {
144 get {return version;}
147 internal static IEnumerable<string> GetAvailableIds ()
149 return GetAvailableIds (0, false);
152 internal static IEnumerable<string> GetAvailableIds (int rawOffset)
154 return GetAvailableIds (rawOffset, true);
157 static IEnumerable<string> GetAvailableIds (int rawOffset, bool checkOffset)
159 for (int i = 0; i < offsets.Length; ++i) {
160 if (!checkOffset || offsets [i] == rawOffset)
161 yield return names [i];
165 static TimeZoneInfo _GetTimeZone (string name)
168 using (var stream = GetTimeZoneData (name, out start, out length)) {
171 byte[] buf = new byte [length];
172 Fill (stream, buf, buf.Length);
173 return TimeZoneInfo.ParseTZBuffer (name, buf, length);
177 static FileStream GetTimeZoneData (string name, out int start, out int length)
179 var f = new FileInfo (Path.Combine (ZoneDirectoryName, name));
182 length = (int) f.Length;
183 return f.OpenRead ();
188 int i = Array.BinarySearch (names, name);
193 length = lengths [i];
195 var stream = File.OpenRead (ZoneFileName);
196 stream.Seek (start, SeekOrigin.Begin);
201 internal static TimeZoneInfo GetTimeZone (string id)
204 if (id == "GMT" || id == "UTC")
205 return new TimeZoneInfo (id, TimeSpan.FromSeconds (0), id, id, id, null, true);
206 if (id.StartsWith ("GMT"))
207 return new TimeZoneInfo (id,
208 TimeSpan.FromSeconds (ParseNumericZone (id)),
209 id, id, id, null, true);
213 return _GetTimeZone (id);
214 } catch (Exception e) {
219 static int ParseNumericZone (string name)
221 if (name == null || !name.StartsWith ("GMT") || name.Length <= 3)
227 else if (name [3] == '-')
235 for (where = 4; where < name.Length; where++) {
236 char c = name [where];
244 if (c >= '0' && c <= '9')
245 hour = hour * 10 + c - '0';
251 for (; where < name.Length; where++) {
252 char c = name [where];
254 if (c >= '0' && c <= '9')
255 min = min * 10 + c - '0';
261 return sign * (hour * 60 + min) * 60;
262 else if (hour >= 100)
263 return sign * ((hour / 100) * 60 + (hour % 100)) * 60;
265 return sign * (hour * 60) * 60;
268 static TimeZoneInfo defaultZone;
269 internal static TimeZoneInfo Default {
272 if (defaultZone != null)
274 return defaultZone = GetTimeZone (GetDefaultTimeZoneName ());
279 // <sys/system_properties.h>
280 [DllImport ("/system/lib/libc.so")]
281 static extern int __system_property_get (string name, StringBuilder value);
283 const int MaxPropertyNameLength = 32; // <sys/system_properties.h>
284 const int MaxPropertyValueLength = 92; // <sys/system_properties.h>
286 static string GetDefaultTimeZoneName ()
288 var buf = new StringBuilder (MaxPropertyValueLength + 1);
289 int n = __system_property_get ("persist.sys.timezone", buf);
291 return buf.ToString ();