TimeZoneInfo.Android.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * System.TimeZoneInfo Android Support
  3. *
  4. * Author(s)
  5. * Jonathan Pryor <[email protected]>
  6. * The Android Open Source Project
  7. *
  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
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  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.
  19. */
  20. #if (INSIDE_CORLIB && MONODROID)
  21. using System;
  22. using System.Collections.Generic;
  23. using System.IO;
  24. using System.Runtime.CompilerServices;
  25. using System.Runtime.InteropServices;
  26. using System.Text;
  27. namespace System {
  28. partial class TimeZoneInfo {
  29. /*
  30. * Android Timezone support infrastructure.
  31. *
  32. * This is a C# port of org.apache.harmony.luni.internal.util.ZoneInfoDB:
  33. *
  34. * 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
  35. *
  36. * From the ZoneInfoDB source:
  37. *
  38. * However, to conserve disk space the data for all time zones are
  39. * concatenated into a single file, and a second file is used to indicate
  40. * the starting position of each time zone record. A third file indicates
  41. * the version of the zoneinfo databse used to generate the data.
  42. *
  43. * which succinctly describes why we can't just use the LIBC implementation in
  44. * TimeZoneInfo.cs -- the "standard Unixy" directory structure is NOT used.
  45. */
  46. static class ZoneInfoDB {
  47. const int TimeZoneNameLength = 40;
  48. const int TimeZoneIntSize = 4;
  49. static readonly string ZoneDirectoryName = Environment.GetEnvironmentVariable ("ANDROID_ROOT") + "/usr/share/zoneinfo/";
  50. static readonly string ZoneFileName = ZoneDirectoryName + "zoneinfo.dat";
  51. static readonly string IndexFileName = ZoneDirectoryName + "zoneinfo.idx";
  52. const string DefaultVersion = "2007h";
  53. static readonly string VersionFileName = ZoneDirectoryName + "zoneinfo.version";
  54. static readonly object _lock = new object ();
  55. static readonly string version;
  56. static readonly string[] names;
  57. static readonly int[] starts;
  58. static readonly int[] lengths;
  59. static readonly int[] offsets;
  60. static ZoneInfoDB ()
  61. {
  62. try {
  63. version = ReadVersion ();
  64. } catch {
  65. version = DefaultVersion;
  66. }
  67. try {
  68. ReadDatabase (out names, out starts, out lengths, out offsets);
  69. } catch {
  70. names = new string [0];
  71. starts = new int [0];
  72. lengths = new int [0];
  73. offsets = new int [0];
  74. }
  75. }
  76. static string ReadVersion ()
  77. {
  78. using (var file = new StreamReader (VersionFileName, Encoding.GetEncoding ("iso-8859-1"))) {
  79. return file.ReadToEnd ().Trim ();
  80. }
  81. }
  82. static void ReadDatabase (out string[] names, out int[] starts, out int[] lengths, out int[] offsets)
  83. {
  84. using (var file = File.OpenRead (IndexFileName)) {
  85. var nbuf = new byte [TimeZoneNameLength];
  86. int numEntries = (int) (file.Length / (TimeZoneNameLength + 3*TimeZoneIntSize));
  87. char[] namebuf = new char [TimeZoneNameLength];
  88. names = new string [numEntries];
  89. starts = new int [numEntries];
  90. lengths = new int [numEntries];
  91. offsets = new int [numEntries];
  92. for (int i = 0; i < numEntries; ++i) {
  93. Fill (file, nbuf, nbuf.Length);
  94. int namelen;
  95. for (namelen = 0; namelen < nbuf.Length; ++namelen) {
  96. if (nbuf [namelen] == '\0')
  97. break;
  98. namebuf [namelen] = (char) (nbuf [namelen] & 0xFF);
  99. }
  100. names [i] = new string (namebuf, 0, namelen);
  101. starts [i] = ReadInt32 (file, nbuf);
  102. lengths [i] = ReadInt32 (file, nbuf);
  103. offsets [i] = ReadInt32 (file, nbuf);
  104. }
  105. }
  106. }
  107. static void Fill (Stream stream, byte[] nbuf, int required)
  108. {
  109. int read, offset = 0;
  110. while (offset < required && (read = stream.Read (nbuf, offset, required - offset)) > 0)
  111. offset += read;
  112. if (read != required)
  113. throw new EndOfStreamException ("Needed to read " + required + " bytes; read " + read + " bytes");
  114. }
  115. // From java.io.RandomAccessFioe.readInt(), as we need to use the same
  116. // byte ordering as Java uses.
  117. static int ReadInt32 (Stream stream, byte[] nbuf)
  118. {
  119. Fill (stream, nbuf, 4);
  120. return ((nbuf [0] & 0xff) << 24) + ((nbuf [1] & 0xff) << 16) +
  121. ((nbuf [2] & 0xff) << 8) + (nbuf [3] & 0xff);
  122. }
  123. internal static string Version {
  124. get {return version;}
  125. }
  126. internal static IEnumerable<string> GetAvailableIds ()
  127. {
  128. return GetAvailableIds (0, false);
  129. }
  130. internal static IEnumerable<string> GetAvailableIds (int rawOffset)
  131. {
  132. return GetAvailableIds (rawOffset, true);
  133. }
  134. static IEnumerable<string> GetAvailableIds (int rawOffset, bool checkOffset)
  135. {
  136. for (int i = 0; i < offsets.Length; ++i) {
  137. if (!checkOffset || offsets [i] == rawOffset)
  138. yield return names [i];
  139. }
  140. }
  141. static TimeZoneInfo _GetTimeZone (string name)
  142. {
  143. int start, length;
  144. using (var stream = GetTimeZoneData (name, out start, out length)) {
  145. if (stream == null)
  146. return null;
  147. byte[] buf = new byte [length];
  148. Fill (stream, buf, buf.Length);
  149. return TimeZoneInfo.ParseTZBuffer (name, buf, length);
  150. }
  151. }
  152. static FileStream GetTimeZoneData (string name, out int start, out int length)
  153. {
  154. var f = new FileInfo (Path.Combine (ZoneDirectoryName, name));
  155. if (f.Exists) {
  156. start = 0;
  157. length = (int) f.Length;
  158. return f.OpenRead ();
  159. }
  160. start = length = 0;
  161. int i = Array.BinarySearch (names, name);
  162. if (i < 0)
  163. return null;
  164. start = starts [i];
  165. length = lengths [i];
  166. var stream = File.OpenRead (ZoneFileName);
  167. stream.Seek (start, SeekOrigin.Begin);
  168. return stream;
  169. }
  170. internal static TimeZoneInfo GetTimeZone (string id)
  171. {
  172. if (id != null) {
  173. if (id == "GMT" || id == "UTC")
  174. return new TimeZoneInfo (id, TimeSpan.FromSeconds (0), id, id, id, null, true);
  175. if (id.StartsWith ("GMT"))
  176. return new TimeZoneInfo (id,
  177. TimeSpan.FromSeconds (ParseNumericZone (id)),
  178. id, id, id, null, true);
  179. }
  180. try {
  181. return _GetTimeZone (id);
  182. } catch (Exception e) {
  183. return null;
  184. }
  185. }
  186. static int ParseNumericZone (string name)
  187. {
  188. if (name == null || !name.StartsWith ("GMT") || name.Length <= 3)
  189. return 0;
  190. int sign;
  191. if (name [3] == '+')
  192. sign = 1;
  193. else if (name [3] == '-')
  194. sign = -1;
  195. else
  196. return 0;
  197. int where;
  198. int hour = 0;
  199. bool colon = false;
  200. for (where = 4; where < name.Length; where++) {
  201. char c = name [where];
  202. if (c == ':') {
  203. where++;
  204. colon = true;
  205. break;
  206. }
  207. if (c >= '0' && c <= '9')
  208. hour = hour * 10 + c - '0';
  209. else
  210. return 0;
  211. }
  212. int min = 0;
  213. for (; where < name.Length; where++) {
  214. char c = name [where];
  215. if (c >= '0' && c <= '9')
  216. min = min * 10 + c - '0';
  217. else
  218. return 0;
  219. }
  220. if (colon)
  221. return sign * (hour * 60 + min) * 60;
  222. else if (hour >= 100)
  223. return sign * ((hour / 100) * 60 + (hour % 100)) * 60;
  224. else
  225. return sign * (hour * 60) * 60;
  226. }
  227. static TimeZoneInfo defaultZone;
  228. internal static TimeZoneInfo Default {
  229. get {
  230. lock (_lock) {
  231. if (defaultZone != null)
  232. return defaultZone;
  233. return defaultZone = GetTimeZone (GetDefaultTimeZoneName ());
  234. }
  235. }
  236. }
  237. // <sys/system_properties.h>
  238. [DllImport ("/system/lib/libc.so")]
  239. static extern int __system_property_get (string name, StringBuilder value);
  240. const int MaxPropertyNameLength = 32; // <sys/system_properties.h>
  241. const int MaxPropertyValueLength = 92; // <sys/system_properties.h>
  242. static string GetDefaultTimeZoneName ()
  243. {
  244. var buf = new StringBuilder (MaxPropertyValueLength + 1);
  245. int n = __system_property_get ("persist.sys.timezone", buf);
  246. if (n > 0)
  247. return buf.ToString ();
  248. return null;
  249. }
  250. }
  251. }
  252. }
  253. #endif // MONODROID