Move the zoneinfo generation tool into bionic.
This also incorporates a bunch of changes to the previous script; this one requires no setup, can be run from anywhere, and leaves no droppings. Change-Id: I38f299f03e33950d2a64e9336f4ba7cb3c5cf6f0
This commit is contained in:
parent
3a3c1853ac
commit
d40e63ee47
166
libc/tools/zoneinfo/ZoneCompactor.java
Normal file
166
libc/tools/zoneinfo/ZoneCompactor.java
Normal file
@ -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]);
|
||||
}
|
||||
|
||||
}
|
272
libc/tools/zoneinfo/ZoneInfo.java
Normal file
272
libc/tools/zoneinfo/ZoneInfo.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
79
libc/tools/zoneinfo/generate
Executable file
79
libc/tools/zoneinfo/generate
Executable file
@ -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
|
Loading…
x
Reference in New Issue
Block a user