ZoneCompactor was using the system time zone database to get the offsets instead of using the data it was compiling, so for newly added or recently changed zones the index could be inconsistent with the data. Affected zones: San_Luis, Casey, Davis, Mawson, Kathmandu, Novokuznetsk
167 lines
5.4 KiB
Java
167 lines
5.4 KiB
Java
|
|
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 l = new ArrayList();
|
|
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]);
|
|
}
|
|
|
|
}
|