summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Turner <digit@google.com>2011-02-18 14:53:03 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-02-18 14:53:03 -0800
commit0999f8dcf22cd2ca541314a348720aedcf02ae48 (patch)
treec24bf408b2c069cabbba1fa0df45e483a34be39f
parent0233509c16046766bea9af6c7053cc6ceecef7a2 (diff)
parentd40e63ee47e4a7f072a9d9a20e09c26f0090b02c (diff)
downloadbionic-0999f8dcf22cd2ca541314a348720aedcf02ae48.zip
bionic-0999f8dcf22cd2ca541314a348720aedcf02ae48.tar.gz
bionic-0999f8dcf22cd2ca541314a348720aedcf02ae48.tar.bz2
Merge "Move the zoneinfo generation tool into bionic."
-rw-r--r--libc/tools/zoneinfo/ZoneCompactor.java166
-rw-r--r--libc/tools/zoneinfo/ZoneInfo.java272
-rwxr-xr-xlibc/tools/zoneinfo/generate79
3 files changed, 517 insertions, 0 deletions
diff --git a/libc/tools/zoneinfo/ZoneCompactor.java b/libc/tools/zoneinfo/ZoneCompactor.java
new file mode 100644
index 0000000..b657748
--- /dev/null
+++ b/libc/tools/zoneinfo/ZoneCompactor.java
@@ -0,0 +1,166 @@
+
+import java.io.*;
+import java.util.*;
+
+// usage: java ZoneCompiler <setup file> <top-level directory>
+//
+// Compile a set of tzfile-formatted files into a single file plus
+// an index file.
+//
+// The compilation is controlled by a setup file, which is provided as a
+// command-line argument. The setup file has the form:
+//
+// Link <toName> <fromName>
+// ...
+// <zone filename>
+// ...
+//
+// Note that the links must be declared prior to the zone names. A
+// zone name is a filename relative to the source directory such as
+// 'GMT', 'Africa/Dakar', or 'America/Argentina/Jujuy'.
+//
+// Use the 'zic' command-line tool to convert from flat files
+// (e.g., 'africa', 'northamerica') into a suitable source directory
+// hierarchy for this tool (e.g., 'data/Africa/Abidjan').
+//
+// Example:
+// zic -d data tz2007h
+// javac ZoneCompactor.java
+// java ZoneCompactor setup data
+// <produces zoneinfo.dat and zoneinfo.idx>
+
+public class ZoneCompactor {
+
+ // Zone name synonyms
+ Map<String,String> links = new HashMap<String,String>();
+
+ // File starting bytes by zone name
+ Map<String,Integer> starts = new HashMap<String,Integer>();
+
+ // File lengths by zone name
+ Map<String,Integer> lengths = new HashMap<String,Integer>();
+
+ // Raw GMT offsets by zone name
+ Map<String,Integer> offsets = new HashMap<String,Integer>();
+ int start = 0;
+
+ // Maximum number of characters in a zone name, including '\0' terminator
+ private static final int MAXNAME = 40;
+
+ // Concatenate the contents of 'inFile' onto 'out'
+ // and return the contents as a byte array.
+ private static byte[] copyFile(File inFile, OutputStream out)
+ throws Exception {
+ byte[] ret = new byte[0];
+
+ InputStream in = new FileInputStream(inFile);
+ byte[] buf = new byte[8192];
+ while (true) {
+ int nbytes = in.read(buf);
+ if (nbytes == -1) {
+ break;
+ }
+ out.write(buf, 0, nbytes);
+
+ byte[] nret = new byte[ret.length + nbytes];
+ System.arraycopy(ret, 0, nret, 0, ret.length);
+ System.arraycopy(buf, 0, nret, ret.length, nbytes);
+ ret = nret;
+ }
+ out.flush();
+ return ret;
+ }
+
+ // Write a 32-bit integer in network byte order
+ private void writeInt(OutputStream os, int x) throws IOException {
+ os.write((x >> 24) & 0xff);
+ os.write((x >> 16) & 0xff);
+ os.write((x >> 8) & 0xff);
+ os.write( x & 0xff);
+ }
+
+ public ZoneCompactor(String setupFilename, String dirName)
+ throws Exception {
+ File zoneInfoFile = new File("zoneinfo.dat");
+ zoneInfoFile.delete();
+ OutputStream zoneInfo = new FileOutputStream(zoneInfoFile);
+
+ BufferedReader rdr = new BufferedReader(new FileReader(setupFilename));
+
+ String s;
+ while ((s = rdr.readLine()) != null) {
+ s = s.trim();
+ if (s.startsWith("Link")) {
+ StringTokenizer st = new StringTokenizer(s);
+ st.nextToken();
+ String to = st.nextToken();
+ String from = st.nextToken();
+ links.put(from, to);
+ } else {
+ String link = links.get(s);
+ if (link == null) {
+ File f = new File(dirName, s);
+ long length = f.length();
+ starts.put(s, new Integer(start));
+ lengths.put(s, new Integer((int)length));
+
+ start += length;
+ byte[] data = copyFile(f, zoneInfo);
+
+ TimeZone tz = ZoneInfo.make(s, data);
+ int gmtOffset = tz.getRawOffset();
+ offsets.put(s, new Integer(gmtOffset));
+ }
+ }
+ }
+ zoneInfo.close();
+
+ // Fill in fields for links
+ Iterator<String> iter = links.keySet().iterator();
+ while (iter.hasNext()) {
+ String from = iter.next();
+ String to = links.get(from);
+
+ starts.put(from, starts.get(to));
+ lengths.put(from, lengths.get(to));
+ offsets.put(from, offsets.get(to));
+ }
+
+ File idxFile = new File("zoneinfo.idx");
+ idxFile.delete();
+ FileOutputStream idx = new FileOutputStream(idxFile);
+
+ ArrayList<String> l = new ArrayList<String>();
+ l.addAll(starts.keySet());
+ Collections.sort(l);
+ Iterator<String> ziter = l.iterator();
+ while (ziter.hasNext()) {
+ String zname = ziter.next();
+ if (zname.length() >= MAXNAME) {
+ System.err.println("Error - zone filename exceeds " +
+ (MAXNAME - 1) + " characters!");
+ }
+
+ byte[] znameBuf = new byte[MAXNAME];
+ for (int i = 0; i < zname.length(); i++) {
+ znameBuf[i] = (byte)zname.charAt(i);
+ }
+ idx.write(znameBuf);
+ writeInt(idx, starts.get(zname).intValue());
+ writeInt(idx, lengths.get(zname).intValue());
+ writeInt(idx, offsets.get(zname).intValue());
+ }
+ idx.close();
+
+ // System.out.println("maxLength = " + maxLength);
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ System.err.println("usage: java ZoneCompactor <setup> <data dir>");
+ System.exit(0);
+ }
+ new ZoneCompactor(args[0], args[1]);
+ }
+
+}
diff --git a/libc/tools/zoneinfo/ZoneInfo.java b/libc/tools/zoneinfo/ZoneInfo.java
new file mode 100644
index 0000000..99507ae
--- /dev/null
+++ b/libc/tools/zoneinfo/ZoneInfo.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from ZoneInfo and ZoneInfoDB in dalvik.
+ * {@hide}
+ */
+public class ZoneInfo extends TimeZone {
+
+ private static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
+ private static final long MILLISECONDS_PER_400_YEARS =
+ MILLISECONDS_PER_DAY * (400 * 365 + 100 - 3);
+
+ private static final long UNIX_OFFSET = 62167219200000L;
+
+ private static final int[] NORMAL = new int[] {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+ };
+
+ private static final int[] LEAP = new int[] {
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
+ };
+
+ private static String nullName(byte[] data, int where, int off) {
+ if (off < 0)
+ return null;
+
+ int end = where + off;
+ while (end < data.length && data[end] != '\0')
+ end++;
+
+ return new String(data, where + off, end - (where + off));
+ }
+
+ public static ZoneInfo make(String name, byte[] data) {
+ int ntransition = read4(data, 32);
+ int ngmtoff = read4(data, 36);
+ int base = 44;
+
+ int[] transitions = new int[ntransition];
+ for (int i = 0; i < ntransition; i++)
+ transitions[i] = read4(data, base + 4 * i);
+ base += 4 * ntransition;
+
+ byte[] type = new byte[ntransition];
+ for (int i = 0; i < ntransition; i++)
+ type[i] = data[base + i];
+ base += ntransition;
+
+ int[] gmtoff = new int[ngmtoff];
+ byte[] isdst = new byte[ngmtoff];
+ byte[] abbrev = new byte[ngmtoff];
+ for (int i = 0; i < ngmtoff; i++) {
+ gmtoff[i] = read4(data, base + 6 * i);
+ isdst[i] = data[base + 6 * i + 4];
+ abbrev[i] = data[base + 6 * i + 5];
+ }
+
+ base += 6 * ngmtoff;
+
+ return new ZoneInfo(name, transitions, type, gmtoff, isdst, abbrev, data, base);
+ }
+
+ private static int read4(byte[] data, int off) {
+ return ((data[off ] & 0xFF) << 24) |
+ ((data[off + 1] & 0xFF) << 16) |
+ ((data[off + 2] & 0xFF) << 8) |
+ ((data[off + 3] & 0xFF) << 0);
+ }
+
+ /*package*/ ZoneInfo(String name, int[] transitions, byte[] type,
+ int[] gmtoff, byte[] isdst, byte[] abbrev,
+ byte[] data, int abbrevoff) {
+ mTransitions = transitions;
+ mTypes = type;
+ mGmtOffs = gmtoff;
+ mIsDsts = isdst;
+ mUseDst = false;
+ setID(name);
+
+ // Find the latest GMT and non-GMT offsets for their abbreviations
+
+ int lastdst;
+ for (lastdst = mTransitions.length - 1; lastdst >= 0; lastdst--) {
+ if (mIsDsts[mTypes[lastdst] & 0xFF] != 0)
+ break;
+ }
+
+ int laststd;
+ for (laststd = mTransitions.length - 1; laststd >= 0; laststd--) {
+ if (mIsDsts[mTypes[laststd] & 0xFF] == 0)
+ break;
+ }
+
+ if (lastdst >= 0) {
+ mDaylightName = nullName(data, abbrevoff,
+ abbrev[mTypes[lastdst] & 0xFF]);
+ }
+ if (laststd >= 0) {
+ mStandardName = nullName(data, abbrevoff,
+ abbrev[mTypes[laststd] & 0xFF]);
+ }
+
+ // Use the latest non-DST offset if any as the raw offset
+
+ if (laststd < 0) {
+ laststd = 0;
+ }
+
+ if (laststd >= mTypes.length) {
+ mRawOffset = mGmtOffs[0];
+ } else {
+ mRawOffset = mGmtOffs[mTypes[laststd] & 0xFF];
+ }
+
+ // Subtract the raw offset from all offsets so it can be changed
+ // and affect them too.
+ // Find whether there exist any observances of DST.
+
+ for (int i = 0; i < mGmtOffs.length; i++) {
+ mGmtOffs[i] -= mRawOffset;
+
+ if (mIsDsts[i] != 0) {
+ mUseDst = true;
+ }
+ }
+
+ mRawOffset *= 1000;
+ }
+
+ @Override
+ public int getOffset(@SuppressWarnings("unused") int era,
+ int year, int month, int day,
+ @SuppressWarnings("unused") int dayOfWeek,
+ int millis) {
+ // XXX This assumes Gregorian always; Calendar switches from
+ // Julian to Gregorian in 1582. What calendar system are the
+ // arguments supposed to come from?
+
+ long calc = (year / 400) * MILLISECONDS_PER_400_YEARS;
+ year %= 400;
+
+ calc += year * (365 * MILLISECONDS_PER_DAY);
+ calc += ((year + 3) / 4) * MILLISECONDS_PER_DAY;
+
+ if (year > 0)
+ calc -= ((year - 1) / 100) * MILLISECONDS_PER_DAY;
+
+ boolean isLeap = (year == 0 || (year % 4 == 0 && year % 100 != 0));
+ int[] mlen = isLeap ? LEAP : NORMAL;
+
+ calc += mlen[month] * MILLISECONDS_PER_DAY;
+ calc += (day - 1) * MILLISECONDS_PER_DAY;
+ calc += millis;
+
+ calc -= mRawOffset;
+ calc -= UNIX_OFFSET;
+
+ return getOffset(calc);
+ }
+
+ @Override
+ public int getOffset(long when) {
+ int unix = (int) (when / 1000);
+ int trans = Arrays.binarySearch(mTransitions, unix);
+
+ if (trans == ~0) {
+ return mGmtOffs[0] * 1000 + mRawOffset;
+ }
+ if (trans < 0) {
+ trans = ~trans - 1;
+ }
+
+ return mGmtOffs[mTypes[trans] & 0xFF] * 1000 + mRawOffset;
+ }
+
+ @Override
+ public int getRawOffset() {
+ return mRawOffset;
+ }
+
+ @Override
+ public void setRawOffset(int off) {
+ mRawOffset = off;
+ }
+
+ @Override
+ public boolean inDaylightTime(Date when) {
+ int unix = (int) (when.getTime() / 1000);
+ int trans = Arrays.binarySearch(mTransitions, unix);
+
+ if (trans == ~0) {
+ return mIsDsts[0] != 0;
+ }
+ if (trans < 0) {
+ trans = ~trans - 1;
+ }
+
+ return mIsDsts[mTypes[trans] & 0xFF] != 0;
+ }
+
+ @Override
+ public boolean useDaylightTime() {
+ return mUseDst;
+ }
+
+ private int mRawOffset;
+ private int[] mTransitions;
+ private int[] mGmtOffs;
+ private byte[] mTypes;
+ private byte[] mIsDsts;
+ private boolean mUseDst;
+ private String mDaylightName;
+ private String mStandardName;
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ZoneInfo)) {
+ return false;
+ }
+ ZoneInfo other = (ZoneInfo) obj;
+ return mUseDst == other.mUseDst
+ && (mDaylightName == null ? other.mDaylightName == null :
+ mDaylightName.equals(other.mDaylightName))
+ && (mStandardName == null ? other.mStandardName == null :
+ mStandardName.equals(other.mStandardName))
+ && mRawOffset == other.mRawOffset
+ // Arrays.equals returns true if both arrays are null
+ && Arrays.equals(mGmtOffs, other.mGmtOffs)
+ && Arrays.equals(mIsDsts, other.mIsDsts)
+ && Arrays.equals(mTypes, other.mTypes)
+ && Arrays.equals(mTransitions, other.mTransitions);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mDaylightName == null) ? 0 :
+ mDaylightName.hashCode());
+ result = prime * result + Arrays.hashCode(mGmtOffs);
+ result = prime * result + Arrays.hashCode(mIsDsts);
+ result = prime * result + mRawOffset;
+ result = prime * result + ((mStandardName == null) ? 0 :
+ mStandardName.hashCode());
+ result = prime * result + Arrays.hashCode(mTransitions);
+ result = prime * result + Arrays.hashCode(mTypes);
+ result = prime * result + (mUseDst ? 1231 : 1237);
+ return result;
+ }
+}
diff --git a/libc/tools/zoneinfo/generate b/libc/tools/zoneinfo/generate
new file mode 100755
index 0000000..5755e26
--- /dev/null
+++ b/libc/tools/zoneinfo/generate
@@ -0,0 +1,79 @@
+#!/bin/bash
+# Run with no arguments from any directory, with no special setup required.
+
+# Abort if any command returns an error exit status, or if an undefined
+# variable is used.
+set -e
+set -u
+
+echo "Looking for bionic..."
+bionic_dir=$(cd $(dirname $0)/../../.. && pwd)
+bionic_zoneinfo_dir=$bionic_dir/libc/zoneinfo
+bionic_zoneinfo_tools_dir=$bionic_dir/libc/tools/zoneinfo
+if [[ ! -d "$bionic_zoneinfo_dir" || ! -d "$bionic_zoneinfo_tools_dir" ]]; then
+ echo "Can't find bionic's zoneinfo directories!"
+ exit 1
+fi
+
+echo "Switching to temporary directory..."
+temp_dir=`mktemp -d`
+cd $temp_dir
+trap "rm -rf $temp_dir; exit" INT TERM EXIT
+
+# URL from "Sources for Time Zone and Daylight Saving Time Data"
+# http://www.twinsun.com/tz/tz-link.htm
+echo "Looking for new tzdata..."
+wget -N --no-verbose 'ftp://elsie.nci.nih.gov/pub/tzdata*.tar.gz'
+zoneinfo_version_file=$bionic_zoneinfo_dir/zoneinfo.version
+if [ -f "$zoneinfo_version_file" ]; then
+ current_version=tzdata`sed s/\n// < $zoneinfo_version_file`
+else
+ current_version=missing
+fi
+latest_archive=`ls -r -v tzdata*.tar.gz | head -n1`
+latest_version=`basename $latest_archive .tar.gz`
+if [ "$current_version" == "$latest_version" ]; then
+ echo "You already have the latest tzdata ($latest_version)!"
+ exit 1
+fi
+
+echo "Extracting $latest_version..."
+mkdir $latest_version
+tar -C $latest_version -zxf $latest_archive
+
+echo "Compiling $latest_version..."
+mkdir data
+for i in \
+ africa \
+ antarctica \
+ asia \
+ australasia \
+ etcetera \
+ europe \
+ factory \
+ northamerica \
+ solar87 \
+ solar88 \
+ solar89 \
+ southamerica
+do
+ zic -d data $latest_version/$i
+done
+
+echo "Compacting $latest_version..."
+(
+ cat $latest_version/* | grep '^Link' | awk '{print $1, $2, $3}'
+ (
+ cat $latest_version/* | grep '^Zone' | awk '{print $2}'
+ cat $latest_version/* | grep '^Link' | awk '{print $3}'
+ ) | LC_ALL="C" sort
+) | grep -v Riyadh8 > setup
+
+javac -d . \
+ $bionic_zoneinfo_tools_dir/ZoneCompactor.java \
+ $bionic_zoneinfo_tools_dir/ZoneInfo.java
+java ZoneCompactor setup data
+
+echo "Updating bionic to $latest_version..."
+mv zoneinfo.dat zoneinfo.idx $bionic_zoneinfo_dir
+echo $latest_version | sed 's/tzdata//' > $bionic_zoneinfo_dir/zoneinfo.version