blob: 2b3695922ab13de3487b9fd337ed6f0e02670200 [file] [log] [blame]
/*
***********************************************************************
* Copyright (C) 2007, International Business Machines *
* Corporation and others. All Rights Reserved. *
***********************************************************************
*
*/
package com.ibm.icu.dev.tool.timezone;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.SimpleTimeZone;
import com.ibm.icu.util.ULocale;
/**
* TimeZone transition dump tool.
*/
public class ICUZDump {
private static final String DEFAULT_LINE_SEP;
static {
String sep = System.getProperty("line.separator");
DEFAULT_LINE_SEP = (sep == null) ? "\n" : sep;
}
private TimeZoneImpl tz = null;
private int loyear = 1900;
private int hiyear = 2050;
private long tick = 1000;
private DumpFormatter formatter;
private String linesep = DEFAULT_LINE_SEP;
public ICUZDump() {
}
public void setLowYear(int loyear) {
this.loyear = loyear;
}
public void setHighYear(int hiyear) {
this.hiyear = hiyear;
}
public void setTick(int tick) {
if (tick <= 0) {
throw new IllegalArgumentException("tick must be positive");
}
this.tick = tick;
}
public void setTimeZone(Object tzimpl) {
this.tz = new TimeZoneImpl(tzimpl);
}
public void setDumpFormatter(DumpFormatter formatter) {
this.formatter = formatter;
}
public void setLineSeparator(String sep) {
this.linesep = sep;
}
public void dump(Writer w) throws IOException {
if (tz == null) {
throw new IllegalStateException("timezone is not initialized");
}
if (formatter == null) {
formatter = new DumpFormatter();
}
final long SEARCH_INCREMENT = 12 * 60 * 60 * 1000; // half day
long cutovers[] = getCutOverTimes();
long t = cutovers[0];
int offset = tz.getOffset(t);
boolean inDst = tz.inDaylightTime(t);
while (t < cutovers[1]) {
long newt = t + SEARCH_INCREMENT;
int newOffset = tz.getOffset(newt);
boolean newInDst = tz.inDaylightTime(newt);
if (offset != newOffset || inDst != newInDst) {
// find the boundary
long lot = t;
long hit = newt;
while (true) {
long diff = hit - lot;
if (diff <= tick) {
break;
}
long medt = lot + ((diff / 2) / tick) * tick;
int medOffset = tz.getOffset(medt);
boolean medInDst = tz.inDaylightTime(medt);
if (medOffset != offset || medInDst != inDst) {
hit = medt;
} else {
lot = medt;
}
}
w.write(formatter.format(lot, offset, tz.inDaylightTime(lot)));
w.write(" > ");
w.write(formatter.format(hit, newOffset, tz.inDaylightTime(hit)));
w.write(linesep);
offset = newOffset;
inDst = newInDst;
}
t = newt;
}
}
private long[] getCutOverTimes() {
long[] cutovers = new long[2];
cutovers[0] = tz.getTime(loyear, 0, 1, 0, 0, 0);
cutovers[1] = tz.getTime(hiyear, 0, 1, 0, 0, 0);
return cutovers;
}
private class TimeZoneImpl {
private Object tzobj;
public TimeZoneImpl(Object tzobj) {
this.tzobj = tzobj;
}
public int getOffset(long time) {
try {
Method method = tzobj.getClass().getMethod("getOffset", new Class[] {long.class});
Object result = method.invoke(tzobj, new Object[] {new Long(time)});
return ((Integer)result).intValue();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public boolean inDaylightTime(long time) {
try {
Method method = tzobj.getClass().getMethod("inDaylightTime", new Class[] {Date.class});
Object result = method.invoke(tzobj, new Object[] {new Date(time)});
return ((Boolean)result).booleanValue();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public long getTime(int year, int month, int dayOfMonth, int hour, int minute, int second) {
long time;
if (tzobj instanceof com.ibm.icu.util.TimeZone) {
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeZone((com.ibm.icu.util.TimeZone)tzobj);
cal.clear();
cal.set(year, month, dayOfMonth, hour, minute, second);
time = cal.getTimeInMillis();
} else if (tzobj instanceof java.util.TimeZone) {
java.util.GregorianCalendar cal = new java.util.GregorianCalendar();
cal.setTimeZone((java.util.TimeZone)tzobj);
cal.clear();
cal.set(year, month, dayOfMonth, hour, minute, second);
time = cal.getTimeInMillis();
} else {
throw new IllegalStateException("Unsupported TimeZone implementation");
}
return time;
}
}
public class DumpFormatter {
private SimpleTimeZone stz = new SimpleTimeZone(0, "");
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd EEE HH:mm:ss", ULocale.US);
private DecimalFormat decf;
public DumpFormatter() {
DecimalFormatSymbols decfs = new DecimalFormatSymbols(ULocale.US);
decf = new DecimalFormat("00", decfs);
}
public String format(long time, int offset, boolean isDst) {
StringBuffer buf = new StringBuffer();
stz.setRawOffset(offset);
sdf.setTimeZone(stz);
buf.append(sdf.format(new Date(time)));
if (offset < 0) {
buf.append("-");
offset = -offset;
} else {
buf.append("+");
}
int hour, min, sec;
offset /= 1000;
sec = offset % 60;
offset = (offset - sec) / 60;
min = offset % 60;
hour = offset / 60;
buf.append(decf.format(hour));
buf.append(decf.format(min));
buf.append(decf.format(sec));
buf.append("[DST=");
buf.append(isDst ? "1" : "0");
buf.append("]");
return buf.toString();
}
}
/*
* Usage:
*
* java -cp icu4j.jar com.ibm.icu.dev.tool.timezone [-j] [-a] [-c[<low_year>,]<high_year>] [-d<dir>] [-l<sep>] [<zone_name> [<zone_name>]]
*
* Options:
* -j : Use JDK TimeZone. By default, ICU TimeZone is used.
* -a : Dump all available zones.
* -c[<low_year>,]<high_year>
* : When specified, dump transitions starting <low_year> (inclusive) up to
* <high_year> (exclusive). The default values are 1902(low) and 2038(high).
* -d<dir> : When specified, write transitions in a file under the directory for each zone.
* -l<sep> : New line code type CR/LF/CRLF.
*/
public static void main(String[] args) {
boolean jdk = false;
int low = 1902;
int high = 2038;
List idlist = new ArrayList();
boolean all = false;
String dir = null;
String newLineMode = null;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-j")) {
jdk = true;
} else if (args[i].startsWith("-c")) {
String val = args[i].substring(2);
String[] years = val.split(",");
if (years.length == 1) {
high = Integer.parseInt(years[0]);
} else if (years.length == 2) {
low = Integer.parseInt(years[0]);
high = Integer.parseInt(years[1]);
}
} else if (args[i].equals("-a")) {
all = true;
} else if (args[i].startsWith("-d")) {
dir = args[i].substring(2);
} else if (args[i].startsWith("-l")) {
newLineMode = args[i].substring(2);
} else if (!args[i].startsWith("-")){
idlist.add(args[i].trim());
}
}
String lineSep = System.getProperty("line.separator");
if (newLineMode != null && newLineMode.length() > 0) {
if (newLineMode.equalsIgnoreCase("CR")) {
lineSep = "\r";
} else if (newLineMode.equalsIgnoreCase("LF")) {
lineSep = "\n";
} else if (newLineMode.equalsIgnoreCase("CRLF")) {
lineSep = "\r\n";
}
}
String[] tzids = null;
if (all) {
if (jdk) {
tzids = java.util.TimeZone.getAvailableIDs();
} else {
tzids = com.ibm.icu.util.TimeZone.getAvailableIDs();
}
// sort tzids
TreeSet set = new TreeSet();
for (int i = 0; i < tzids.length; i++) {
set.add(tzids[i]);
}
Iterator it = set.iterator();
int i = 0;
while (it.hasNext()) {
tzids[i++] = (String)it.next();
}
} else {
int len = idlist.size();
if (len == 0) {
tzids = new String[1];
tzids[0] = java.util.TimeZone.getDefault().getID();
} else {
tzids = new String[idlist.size()];
idlist.toArray(tzids);
}
}
File dirfile = null;
if (dir == null || dir.length() == 0) {
PrintWriter pw = new PrintWriter(System.out);
try {
for (int i = 0; i < tzids.length; i++) {
if (i != 0) {
pw.println();
}
pw.write("ZONE: ");
pw.write(tzids[i]);
pw.println();
dumpZone(pw, lineSep, tzids[i], low, high, jdk);
}
pw.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
}
} else {
dirfile = new File(dir);
dirfile.mkdirs();
try {
for (int i = 0; i < tzids.length; i++) {
FileOutputStream fos = new FileOutputStream(new File(dirfile, tzids[i].replace('/', '-')));
Writer w = new BufferedWriter(new OutputStreamWriter(fos));
dumpZone(w, lineSep, tzids[i], low, high, jdk);
w.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
private static void dumpZone(Writer w, String lineSep, String tzid, int low, int high, boolean isJdk) throws IOException {
ICUZDump dumper = new ICUZDump();
Object tzimpl;
if (isJdk) {
tzimpl = java.util.TimeZone.getTimeZone(tzid);
} else {
tzimpl = com.ibm.icu.util.TimeZone.getTimeZone(tzid);
}
dumper.setTimeZone(tzimpl);
dumper.setLowYear(low);
dumper.setHighYear(high);
dumper.setLineSeparator(lineSep);
dumper.dump(w);
}
}