1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * you may not use this file except in compliance with the License.
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * You may obtain a copy of the License at
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
176d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughespackage libcore.util;
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.RandomAccessFile;
213ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughesimport java.nio.ByteBuffer;
223ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughesimport java.nio.ByteOrder;
23d6b9f2e0e9640ff9c896b394716cf5e7eee6e257Elliott Hughesimport java.nio.channels.FileChannel.MapMode;
242a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughesimport java.nio.charset.StandardCharsets;
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.ArrayList;
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Arrays;
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.List;
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.TimeZone;
29d6b9f2e0e9640ff9c896b394716cf5e7eee6e257Elliott Hughesimport libcore.io.BufferIterator;
309b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughesimport libcore.io.ErrnoException;
31f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughesimport libcore.io.IoUtils;
32d6b9f2e0e9640ff9c896b394716cf5e7eee6e257Elliott Hughesimport libcore.io.MemoryMappedFile;
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
356cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes * A class used to initialize the time zone database. This implementation uses the
366cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes * Olson tzdata as the source of time zone information. However, to conserve
376cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes * disk space (inodes) and reduce I/O, all the data is concatenated into a single file,
386cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes * with an index to indicate the starting position of each time zone record.
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
406d82ce5d35a5e84aedf08528fd98b849f3f565a6Elliott Hughes * @hide - used to implement TimeZone
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic final class ZoneInfoDB {
43995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  private static final TzData DATA =
44995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      new TzData(System.getenv("ANDROID_DATA") + "/misc/zoneinfo/tzdata",
45995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes                 System.getenv("ANDROID_ROOT") + "/usr/share/zoneinfo/tzdata");
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
47995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  public static class TzData {
483ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughes    /**
499b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes     * Rather than open, read, and close the big data file each time we look up a time zone,
509b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes     * we map the big data file during startup, and then just use the MemoryMappedFile.
519b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes     *
529b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes     * At the moment, this "big" data file is about 500 KiB. At some point, that will be small
53995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes     * enough that we could just keep the byte[] in memory, but using mmap(2) like this has the
54995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes     * nice property that even if someone replaces the file under us (because multiple gservices
55995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes     * updates have gone out, say), we still get a consistent (if outdated) view of the world.
569b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes     */
57995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private MemoryMappedFile mappedFile;
586cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes
59995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private String version;
60995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private String zoneTab;
619b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes
629b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes    /**
633ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughes     * The 'ids' array contains time zone ids sorted alphabetically, for binary searching.
643ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughes     * The other two arrays are in the same order. 'byteOffsets' gives the byte offset
656cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes     * of each time zone, and 'rawUtcOffsets' gives the time zone's raw UTC offset.
663ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughes     */
67995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private String[] ids;
68995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private int[] byteOffsets;
69995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private int[] rawUtcOffsets;
70995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
71995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    public TzData(String... paths) {
72995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      for (String path : paths) {
73995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        if (loadData(path)) {
74995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes          return;
75adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
76995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
77995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
78995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // We didn't find any usable tzdata on disk, so let's just hard-code knowledge of "GMT".
79995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // This is actually implemented in TimeZone itself, so if this is the only time zone
80995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // we report, we won't be asked any more questions.
81995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      System.logE("Couldn't find any tzdata!");
82995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      version = "missing";
83995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      zoneTab = "# Emergency fallback data.\n";
84995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      ids = new String[] { "GMT" };
85995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      byteOffsets = rawUtcOffsets = new int[1];
86f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes    }
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
88995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private boolean loadData(String path) {
89995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      try {
90995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        mappedFile = MemoryMappedFile.mmapRO(path);
91995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      } catch (ErrnoException errnoException) {
92995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        return false;
93995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
94995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      try {
95995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        readHeader();
96995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        return true;
97995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      } catch (Exception ex) {
98995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        // Something's wrong with the file.
99995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        // Log the problem and return false so we try the next choice.
100995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        System.logE("tzdata file \"" + path + "\" was present but invalid!", ex);
101995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        return false;
102995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
1034c0cbc2cf60df843387233801784ae43c2cd747cElliott Hughes    }
1044c0cbc2cf60df843387233801784ae43c2cd747cElliott Hughes
105995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private void readHeader() {
106995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // byte[12] tzdata_version  -- "tzdata2012f\0"
107995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // int index_offset
108995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // int data_offset
109995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // int zonetab_offset
110995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      BufferIterator it = mappedFile.bigEndianIterator();
111995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
112995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      byte[] tzdata_version = new byte[12];
113995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      it.readByteArray(tzdata_version, 0, tzdata_version.length);
1142a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes      String magic = new String(tzdata_version, 0, 6, StandardCharsets.US_ASCII);
115995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      if (!magic.equals("tzdata") || tzdata_version[11] != 0) {
116995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        throw new RuntimeException("bad tzdata magic: " + Arrays.toString(tzdata_version));
117995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
1182a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes      version = new String(tzdata_version, 6, 5, StandardCharsets.US_ASCII);
119995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
120995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int index_offset = it.readInt();
121995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int data_offset = it.readInt();
122995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int zonetab_offset = it.readInt();
123995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
124995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      readIndex(it, index_offset, data_offset);
125995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      readZoneTab(it, zonetab_offset, (int) mappedFile.size() - zonetab_offset);
1269b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes    }
1279b510df35b57946d843ffc34cf23fdcfc84c5220Elliott Hughes
128995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private void readZoneTab(BufferIterator it, int zoneTabOffset, int zoneTabSize) {
129995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      byte[] bytes = new byte[zoneTabSize];
130995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      it.seek(zoneTabOffset);
131995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      it.readByteArray(bytes, 0, bytes.length);
1322a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes      zoneTab = new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
133dad6760aedf4c6b29b47dea6352d88fc3df9b2e5Elliott Hughes    }
134dad6760aedf4c6b29b47dea6352d88fc3df9b2e5Elliott Hughes
135995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    private void readIndex(BufferIterator it, int indexOffset, int dataOffset) {
136995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      it.seek(indexOffset);
137f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes
138995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // The database reserves 40 bytes for each id.
139995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      final int SIZEOF_TZNAME = 40;
140995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // The database uses 32-bit (4 byte) integers.
141995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      final int SIZEOF_TZINT = 4;
142f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes
143995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      byte[] idBytes = new byte[SIZEOF_TZNAME];
144995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int indexSize = (dataOffset - indexOffset);
145995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int entryCount = indexSize / (SIZEOF_TZNAME + 3*SIZEOF_TZINT);
146f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes
147995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      char[] idChars = new char[entryCount * SIZEOF_TZNAME];
148995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int[] idEnd = new int[entryCount];
149995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int idOffset = 0;
150f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes
151995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      byteOffsets = new int[entryCount];
152995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      rawUtcOffsets = new int[entryCount];
153f14cadb15b06371fb9a6daf885dc1c4bccf975b9Elliott Hughes
154995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      for (int i = 0; i < entryCount; i++) {
155995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        it.readByteArray(idBytes, 0, idBytes.length);
1566cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes
157995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        byteOffsets[i] = it.readInt();
158995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        byteOffsets[i] += dataOffset; // TODO: change the file format so this is included.
1596cbefca623f55004ba65f11577fc25f92f6297dcElliott Hughes
160995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        int length = it.readInt();
161995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        if (length < 44) {
162995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes          throw new AssertionError("length in index file < sizeof(tzhead)");
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
164995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        rawUtcOffsets[i] = it.readInt();
165995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
166995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        // Don't include null chars in the String
167995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        int len = idBytes.length;
168995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        for (int j = 0; j < len; j++) {
169995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes          if (idBytes[j] == 0) {
170995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes            break;
171995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes          }
172995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes          idChars[idOffset++] = (char) (idBytes[j] & 0xFF);
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
175995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        idEnd[i] = idOffset;
176995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
178995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // We create one string containing all the ids, and then break that into substrings.
179995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // This way, all ids share a single char[] on the heap.
180995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      String allIds = new String(idChars, 0, idOffset);
181995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      ids = new String[entryCount];
182995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      for (int i = 0; i < entryCount; i++) {
183995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        ids[i] = allIds.substring(i == 0 ? 0 : idEnd[i - 1], idEnd[i]);
184995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
1853ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughes    }
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
187995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    public String[] getAvailableIDs() {
188995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      return ids.clone();
1893ab13ebe9d67b0b210865853902b854711ef45b0Elliott Hughes    }
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
191995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    public String[] getAvailableIDs(int rawOffset) {
192995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      List<String> matches = new ArrayList<String>();
193995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      for (int i = 0, end = rawUtcOffsets.length; i < end; ++i) {
194995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        if (rawUtcOffsets[i] == rawOffset) {
195995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes          matches.add(ids[i]);
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
197995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
198995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      return matches.toArray(new String[matches.size()]);
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
201995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    public String getVersion() {
202995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      return version;
203adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
205995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    public String getZoneTab() {
206995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      return zoneTab;
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
208dad6760aedf4c6b29b47dea6352d88fc3df9b2e5Elliott Hughes
209995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    public TimeZone makeTimeZone(String id) throws IOException {
210995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      // Work out where in the big data file this time zone is.
211995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      int index = Arrays.binarySearch(ids, id);
212995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      if (index < 0) {
213995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes        return null;
214995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      }
215995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
216995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      BufferIterator it = mappedFile.bigEndianIterator();
217995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      it.skip(byteOffsets[index]);
218995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
219995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes      return ZoneInfo.makeTimeZone(id, it);
220dad6760aedf4c6b29b47dea6352d88fc3df9b2e5Elliott Hughes    }
221995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  }
222995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
223995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  private ZoneInfoDB() {
224995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  }
225995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes
226995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  public static TzData getInstance() {
227995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes    return DATA;
228995caee51334a4f1a1429e29680ea079c900c37aElliott Hughes  }
229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
230