icu4j-4.2.1.1/ 0000755 0001750 0001750 00000000000 11627232417 012636 5 ustar twerner twerner icu4j-4.2.1.1/src/ 0000755 0001750 0001750 00000000000 11627232253 013423 5 ustar twerner twerner icu4j-4.2.1.1/src/META-INF/ 0000755 0001750 0001750 00000000000 11627232253 014563 5 ustar twerner twerner icu4j-4.2.1.1/src/META-INF/services/ 0000755 0001750 0001750 00000000000 11627232253 016406 5 ustar twerner twerner icu4j-4.2.1.1/src/META-INF/services/java.nio.charset.spi.CharsetProvider 0000644 0001750 0001750 00000000240 10473756211 025362 0 ustar twerner twerner # Copyright (C) 2006, International Business Machines Corporation and others. All Rights Reserved.
# icu4j converters
com.ibm.icu.charset.CharsetProviderICU
icu4j-4.2.1.1/src/com/ 0000755 0001750 0001750 00000000000 11627232253 014201 5 ustar twerner twerner icu4j-4.2.1.1/src/com/ibm/ 0000755 0001750 0001750 00000000000 11627232415 014750 5 ustar twerner twerner icu4j-4.2.1.1/src/com/ibm/icu/ 0000755 0001750 0001750 00000000000 11627232355 015533 5 ustar twerner twerner icu4j-4.2.1.1/src/com/ibm/icu/util/ 0000755 0001750 0001750 00000000000 11627232355 016510 5 ustar twerner twerner icu4j-4.2.1.1/src/com/ibm/icu/util/SimpleHoliday.java 0000644 0001750 0001750 00000023106 10666355147 022127 0 ustar twerner twerner /*
*******************************************************************************
* Copyright (C) 1996-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.util.Date;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
/**
* A holiday whose date can be represented by a month, day, and optionally day of week
* in the Gregorian calendar.
*
* @draft ICU 2.8 (retainAll)
* @provisional This API might change or be removed in a future release.
*/
public class SimpleHoliday extends Holiday {
/**
* Construct an object representing a holiday
*
* @param month The month in which this holiday occurs (0-based)
* @param dayOfMonth The date within the month (1-based).
*
* @param name The name of this holiday. This string is used as a key
* to look up the holiday's name a resource bundle.
* If the name is not found in the resource bundle,
* getDisplayName will return this string instead.
*
* @see Holiday#getDisplayName(java.util.Locale)
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public SimpleHoliday(int month, int dayOfMonth, String name)
{
super(name, new SimpleDateRule(month, dayOfMonth));
}
/**
* Construct an object representing a holiday
*
* @param month The month in which this holiday occurs (0-based)
* @param dayOfMonth The date within the month (1-based).
*
* @param name The name of this holiday. This string is used as a key
* to look up the holiday's name a resource bundle.
* If the name is not found in the resource bundle,
* getDisplayName will return this string instead.
*
* @see Holiday#getDisplayName(java.util.Locale)
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public SimpleHoliday(int month, int dayOfMonth, String name,
int startYear)
{
super(name, rangeRule(startYear, 0, new SimpleDateRule(month, dayOfMonth)));
}
/**
* Construct an object representing a holiday
*
* @param month The month in which this holiday occurs (0-based)
* @param dayOfMonth The date within the month (1-based).
*
* @param name The name of this holiday. This string is used as a key
* to look up the holiday's name a resource bundle.
* If the name is not found in the resource bundle,
* getDisplayName will return this string instead.
*
* @see Holiday#getDisplayName(java.util.Locale)
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public SimpleHoliday(int month, int dayOfMonth, String name,
int startYear, int endYear)
{
super(name, rangeRule(startYear, endYear, new SimpleDateRule(month, dayOfMonth)));
}
/** // TODO: remove
* Construct an object representing a holiday
*
* @param month The month in which this holiday occurs (0-based)
*
* @param dayOfMonth A date within the month (1-based). The
* interpretation of this parameter depends on the value of
* dayOfWeek
.
*
* @param dayOfWeek The day of the week on which this holiday occurs.
* The following values are legal:
TimeZoneRule
is an abstract class representing a rule for time zone.
* TimeZoneRule
has a set of time zone attributes, such as zone name,
* raw offset (UTC offset for standard time) and daylight saving time offset.
*
* @see com.ibm.icu.util.TimeZoneTransition
* @see com.ibm.icu.util.RuleBasedTimeZone
*
* @stable ICU 3.8
*/
public abstract class TimeZoneRule implements Serializable {
private static final long serialVersionUID = 6374143828553768100L;
private final String name;
private final int rawOffset;
private final int dstSavings;
/**
* Constructs a TimeZoneRule
with the name, the GMT offset of its
* standard time and the amount of daylight saving offset adjustment.
*
* @param name The time zone name.
* @param rawOffset The UTC offset of its standard time in milliseconds.
* @param dstSavings The amount of daylight saving offset adjustment in milliseconds.
* If this is a rule for standard time, the value of this argument is 0.
*
* @stable ICU 3.8
*/
public TimeZoneRule(String name, int rawOffset, int dstSavings) {
this.name = name;
this.rawOffset = rawOffset;
this.dstSavings = dstSavings;
}
/**
* Gets the name of this time zone.
*
* @return The name of this time zone.
*
* @stable ICU 3.8
*/
public String getName() {
return name;
}
/**
* Gets the standard time offset.
*
* @return The standard time offset from UTC in milliseconds.
*
* @stable ICU 3.8
*/
public int getRawOffset() {
return rawOffset;
}
/**
* Gets the amount of daylight saving delta time from the standard time.
*
* @return The amount of daylight saving offset used by this rule
* in milliseconds.
*
* @stable ICU 3.8
*/
public int getDSTSavings() {
return dstSavings;
}
/**
* Returns if this rule represents the same rule and offsets as another.
* When two TimeZoneRule
objects differ only its names, this method returns
* true.
*
* @param other The TimeZoneRule
object to be compared with.
* @return true if the other TimeZoneRule
is the same as this one.
*
* @stable ICU 3.8
*/
public boolean isEquivalentTo(TimeZoneRule other) {
if (rawOffset == other.rawOffset && dstSavings == other.dstSavings) {
return true;
}
return false;
}
/**
* Gets the very first time when this rule takes effect.
*
* @param prevRawOffset The standard time offset from UTC before this rule
* takes effect in milliseconds.
* @param prevDSTSavings The amount of daylight saving offset from the
* standard time.
*
* @return The very first time when this rule takes effect.
*
* @stable ICU 3.8
*/
public abstract Date getFirstStart(int prevRawOffset, int prevDSTSavings);
/**
* Gets the final time when this rule takes effect.
*
* @param prevRawOffset The standard time offset from UTC before this rule
* takes effect in milliseconds.
* @param prevDSTSavings The amount of daylight saving offset from the
* standard time.
*
* @return The very last time when this rule takes effect,
* or null if this rule is applied for future dates infinitely.
*
* @stable ICU 3.8
*/
public abstract Date getFinalStart(int prevRawOffset, int prevDSTSavings);
/**
* Gets the first time when this rule takes effect after the specified time.
*
* @param base The first time after this time is returned.
* @param prevRawOffset The standard time offset from UTC before this rule
* takes effect in milliseconds.
* @param prevDSTSavings The amount of daylight saving offset from the
* standard time.
* @param inclusive Whether the base time is inclusive or not.
*
* @return The first time when this rule takes effect after the specified time,
* or null when this rule never takes effect after the specified time.
*
* @stable ICU 3.8
*/
public abstract Date getNextStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive);
/**
* Gets the most recent time when this rule takes effect before the specified time.
*
* @param base The most recent time when this rule takes effect before
* this time is returned.
* @param prevRawOffset The standard time offset from UTC before this rule
* takes effect in milliseconds.
* @param prevDSTSavings The amount of daylight saving offset from the
* standard time.
* @param inclusive Whether the base time is inclusive or not.
*
* @return The most recent time when this rule takes effect before the specified time,
* or null when this rule never takes effect before the specified time.
*
* @stable ICU 3.8
*/
public abstract Date getPreviousStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive);
/**
* Returns if this TimeZoneRule
has one or more start times.
*
* @return true if this String
representation of this TimeZoneRule
object.
* This method is used for debugging purpose only. The string representation can be changed
* in future version of ICU without any notice.
* @stable ICU 3.8
*/
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("name=" + name);
buf.append(", stdOffset=" + rawOffset);
buf.append(", dstSaving=" + dstSavings);
return buf.toString();
}
}
icu4j-4.2.1.1/src/com/ibm/icu/util/VTimeZone.java 0000644 0001750 0001750 00000231506 11017153155 021233 0 ustar twerner twerner /*
*******************************************************************************
* Copyright (C) 2007-2008, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.MissingResourceException;
import com.ibm.icu.impl.Grego;
import com.ibm.icu.util.DateTimeRule;
/**
* VTimeZone
is a class implementing RFC2445 VTIMEZONE. You can create a
* VTimeZone
instance from a time zone ID supported by TimeZone
.
* With the VTimeZone
instance created from the ID, you can write out the rule
* in RFC2445 VTIMEZONE format. Also, you can create a VTimeZone
instance
* from RFC2445 VTIMEZONE data stream, which allows you to calculate time
* zone offset by the rules defined by the data.VTimeZone
instance by the time zone ID.
*
* @param tzid The time zone ID, such as America/New_York
* @return A VTimeZone
initialized by the time zone ID, or null
* when the ID is unknown.
*
* @stable ICU 3.8
*/
public static VTimeZone create(String tzid) {
VTimeZone vtz = new VTimeZone();
vtz.tz = (BasicTimeZone)TimeZone.getTimeZone(tzid, TimeZone.TIMEZONE_ICU);
vtz.olsonzid = vtz.tz.getID();
vtz.setID(tzid);
return vtz;
}
/**
* Create a VTimeZone
instance by RFC2445 VTIMEZONE data.
*
* @param reader The Reader for VTIMEZONE data input stream
* @return A VTimeZone
initialized by the VTIMEZONE data or
* null if failed to load the rule from the VTIMEZONE data.
*
* @stable ICU 3.8
*/
public static VTimeZone create(Reader reader) {
VTimeZone vtz = new VTimeZone();
if (vtz.load(reader)) {
return vtz;
}
return null;
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public int getOffset(int era, int year, int month, int day, int dayOfWeek,
int milliseconds) {
return tz.getOffset(era, year, month, day, dayOfWeek, milliseconds);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public void getOffset(long date, boolean local, int[] offsets) {
tz.getOffset(date, local, offsets);
}
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
*/
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
tz.getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public int getRawOffset() {
return tz.getRawOffset();
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public boolean inDaylightTime(Date date) {
return tz.inDaylightTime(date);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public void setRawOffset(int offsetMillis) {
tz.setRawOffset(offsetMillis);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public boolean useDaylightTime() {
return tz.useDaylightTime();
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public boolean hasSameRules(TimeZone other) {
return tz.hasSameRules(other);
}
/**
* Gets the RFC2445 TZURL property value. When a VTimeZone
instance was created from
* VTIMEZONE data, the value is set by the TZURL property value in the data. Otherwise,
* the initial value is null.
*
* @return The RFC2445 TZURL property value
*
* @stable ICU 3.8
*/
public String getTZURL() {
return tzurl;
}
/**
* Sets the RFC2445 TZURL property value.
*
* @param url The TZURL property value.
*
* @stable ICU 3.8
*/
public void setTZURL(String url) {
tzurl = url;
}
/**
* Gets the RFC2445 LAST-MODIFIED property value. When a VTimeZone
instance was created
* from VTIMEZONE data, the value is set by the LAST-MODIFIED property value in the data.
* Otherwise, the initial value is null.
*
* @return The Date represents the RFC2445 LAST-MODIFIED date.
*
* @stable ICU 3.8
*/
public Date getLastModified() {
return lastmod;
}
/**
* Sets the date used for RFC2445 LAST-MODIFIED property value.
*
* @param date The Date
object represents the date for RFC2445 LAST-MODIFIED property value.
*
* @stable ICU 3.8
*/
public void setLastModified(Date date) {
lastmod = date;
}
/**
* Writes RFC2445 VTIMEZONE data for this time zone
*
* @param writer A Writer
used for the output
* @throws IOException
*
* @stable ICU 3.8
*/
public void write(Writer writer) throws IOException {
BufferedWriter bw = new BufferedWriter(writer);
if (vtzlines != null) {
Iterator it = vtzlines.iterator();
while (it.hasNext()) {
String line = (String)it.next();
if (line.startsWith(ICAL_TZURL + COLON)) {
if (tzurl != null) {
bw.write(ICAL_TZURL);
bw.write(COLON);
bw.write(tzurl);
bw.write(NEWLINE);
}
} else if (line.startsWith(ICAL_LASTMOD + COLON)) {
if (lastmod != null) {
bw.write(ICAL_LASTMOD);
bw.write(COLON);
bw.write(getUTCDateTimeString(lastmod.getTime()));
bw.write(NEWLINE);
}
} else {
bw.write(line);
bw.write(NEWLINE);
}
}
bw.flush();
} else {
String[] customProperties = null;
if (olsonzid != null && ICU_TZVERSION != null) {
customProperties = new String[1];
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION + "]";
}
writeZone(writer, tz, customProperties);
}
}
/**
* Writes RFC2445 VTIMEZONE data applicable for dates after
* the specified start time.
*
* @param writer The Writer
used for the output
* @param start The start time
*
* @throws IOException
*
* @stable ICU 3.8
*/
public void write(Writer writer, long start) throws IOException {
// Extract rules applicable to dates after the start time
TimeZoneRule[] rules = tz.getTimeZoneRules(start);
// Create a RuleBasedTimeZone with the subset rule
RuleBasedTimeZone rbtz = new RuleBasedTimeZone(tz.getID(), (InitialTimeZoneRule)rules[0]);
for (int i = 1; i < rules.length; i++) {
rbtz.addTransitionRule(rules[i]);
}
String[] customProperties = null;
if (olsonzid != null && ICU_TZVERSION != null) {
customProperties = new String[1];
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION +
"/Partial@" + start + "]";
}
writeZone(writer, rbtz, customProperties);
}
/**
* Writes RFC2445 VTIMEZONE data applicable near the specified date.
* Some common iCalendar implementations can only handle a single time
* zone property or a pair of standard and daylight time properties using
* BYDAY rule with day of week (such as BYDAY=1SUN). This method produce
* the VTIMEZONE data which can be handled these implementations. The rules
* produced by this method can be used only for calculating time zone offset
* around the specified date.
*
* @param writer The Writer
used for the output
* @param time The date
*
* @throws IOException
*
* @stable ICU 3.8
*/
public void writeSimple(Writer writer, long time) throws IOException {
// Extract simple rules
TimeZoneRule[] rules = tz.getSimpleTimeZoneRulesNear(time);
// Create a RuleBasedTimeZone with the subset rule
RuleBasedTimeZone rbtz = new RuleBasedTimeZone(tz.getID(), (InitialTimeZoneRule)rules[0]);
for (int i = 1; i < rules.length; i++) {
rbtz.addTransitionRule(rules[i]);
}
String[] customProperties = null;
if (olsonzid != null && ICU_TZVERSION != null) {
customProperties = new String[1];
customProperties[0] = ICU_TZINFO_PROP + COLON + olsonzid + "[" + ICU_TZVERSION +
"/Simple@" + time + "]";
}
writeZone(writer, rbtz, customProperties);
}
// BasicTimeZone methods
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneTransition getNextTransition(long base, boolean inclusive) {
return tz.getNextTransition(base, inclusive);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) {
return tz.getPreviousTransition(base, inclusive);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public boolean hasEquivalentTransitions(TimeZone other, long start, long end) {
return tz.hasEquivalentTransitions(other, start, end);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneRule[] getTimeZoneRules() {
return tz.getTimeZoneRules();
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneRule[] getTimeZoneRules(long start) {
return tz.getTimeZoneRules(start);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public Object clone() {
VTimeZone other = (VTimeZone)super.clone();
other.tz = (BasicTimeZone)tz.clone();
return other;
}
// private stuff ------------------------------------------------------
private BasicTimeZone tz;
private List vtzlines;
private String olsonzid = null;
private String tzurl = null;
private Date lastmod = null;
private static String ICU_TZVERSION;
private static final String ICU_TZINFO_PROP = "X-TZINFO";
// Default DST savings
private static final int DEF_DSTSAVINGS = 60*60*1000; // 1 hour
// Default time start
private static final long DEF_TZSTARTTIME = 0;
// minimum/max
private static final long MIN_TIME = Long.MIN_VALUE;
private static final long MAX_TIME = Long.MAX_VALUE;
// Symbol characters used by RFC2445 VTIMEZONE
private static final String COLON = ":";
private static final String SEMICOLON = ";";
private static final String EQUALS_SIGN = "=";
private static final String COMMA = ",";
private static final String NEWLINE = "\r\n"; // CRLF
// RFC2445 VTIMEZONE tokens
private static final String ICAL_BEGIN_VTIMEZONE = "BEGIN:VTIMEZONE";
private static final String ICAL_END_VTIMEZONE = "END:VTIMEZONE";
private static final String ICAL_BEGIN = "BEGIN";
private static final String ICAL_END = "END";
private static final String ICAL_VTIMEZONE = "VTIMEZONE";
private static final String ICAL_TZID = "TZID";
private static final String ICAL_STANDARD = "STANDARD";
private static final String ICAL_DAYLIGHT = "DAYLIGHT";
private static final String ICAL_DTSTART = "DTSTART";
private static final String ICAL_TZOFFSETFROM = "TZOFFSETFROM";
private static final String ICAL_TZOFFSETTO = "TZOFFSETTO";
private static final String ICAL_RDATE = "RDATE";
private static final String ICAL_RRULE = "RRULE";
private static final String ICAL_TZNAME = "TZNAME";
private static final String ICAL_TZURL = "TZURL";
private static final String ICAL_LASTMOD = "LAST-MODIFIED";
private static final String ICAL_FREQ = "FREQ";
private static final String ICAL_UNTIL = "UNTIL";
private static final String ICAL_YEARLY = "YEARLY";
private static final String ICAL_BYMONTH = "BYMONTH";
private static final String ICAL_BYDAY = "BYDAY";
private static final String ICAL_BYMONTHDAY = "BYMONTHDAY";
private static final String[] ICAL_DOW_NAMES =
{"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
// Month length in regular year
private static final int[] MONTHLENGTH = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static {
// Initialize ICU_TZVERSION
try {
UResourceBundle tzbundle = UResourceBundle.getBundleInstance(
"com/ibm/icu/impl/data/icudt" + VersionInfo.ICU_DATA_VERSION, "zoneinfo");
ICU_TZVERSION = tzbundle.getString("TZVersion");
} catch (MissingResourceException e) {
///CLOVER:OFF
ICU_TZVERSION = null;
///CLOVER:ON
}
}
/* Hide the constructor */
private VTimeZone() {
}
/*
* Read the input stream to locate the VTIMEZONE block and
* parse the contents to initialize this VTimeZone object.
* The reader skips other RFC2445 message headers. After
* the parse is completed, the reader points at the beginning
* of the header field just after the end of VTIMEZONE block.
* When VTIMEZONE block is found and this object is successfully
* initialized by the rules described in the data, this method
* returns true. Otherwise, returns false.
*/
private boolean load(Reader reader) {
// Read VTIMEZONE block into string array
try {
vtzlines = new LinkedList();
boolean eol = false;
boolean start = false;
boolean success = false;
StringBuffer line = new StringBuffer();
while (true) {
int ch = reader.read();
if (ch == -1) {
// end of file
if (start && line.toString().startsWith(ICAL_END_VTIMEZONE)) {
vtzlines.add(line.toString());
success = true;
}
break;
}
if (ch == 0x0D) {
// CR, must be followed by LF by the definition in RFC2445
continue;
}
if (eol) {
if (ch != 0x09 && ch != 0x20) {
// NOT followed by TAB/SP -> new line
if (start) {
if (line.length() > 0) {
vtzlines.add(line.toString());
}
}
line.setLength(0);
if (ch != 0x0A) {
line.append((char)ch);
}
}
eol = false;
} else {
if (ch == 0x0A) {
// LF
eol = true;
if (start) {
if (line.toString().startsWith(ICAL_END_VTIMEZONE)) {
vtzlines.add(line.toString());
success = true;
break;
}
} else {
if (line.toString().startsWith(ICAL_BEGIN_VTIMEZONE)) {
vtzlines.add(line.toString());
line.setLength(0);
start = true;
eol = false;
}
}
} else {
line.append((char)ch);
}
}
}
if (!success) {
return false;
}
} catch (IOException ioe) {
///CLOVER:OFF
return false;
///CLOVER:ON
}
return parse();
}
// parser state
private static final int INI = 0; // Initial state
private static final int VTZ = 1; // In VTIMEZONE
private static final int TZI = 2; // In STANDARD or DAYLIGHT
private static final int ERR = 3; // Error state
/*
* Parse VTIMEZONE data and create a RuleBasedTimeZone
*/
private boolean parse() {
///CLOVER:OFF
if (vtzlines == null || vtzlines.size() == 0) {
return false;
}
///CLOVER:ON
// timezone ID
String tzid = null;
int state = INI;
boolean dst = false; // current zone type
String from = null; // current zone from offset
String to = null; // current zone offset
String tzname = null; // current zone name
String dtstart = null; // current zone starts
boolean isRRULE = false;// true if the rule is described by RRULE
List dates = null; // list of RDATE or RRULE strings
List rules = new LinkedList(); // rule list
int initialRawOffset = 0; // initial offset
int initialDSTSavings = 0; // initial offset
long firstStart = MAX_TIME; // the earliest rule start time
Iterator it = vtzlines.iterator();
while (it.hasNext()) {
String line = (String)it.next();
int valueSep = line.indexOf(COLON);
if (valueSep < 0) {
continue;
}
String name = line.substring(0, valueSep);
String value = line.substring(valueSep + 1);
switch (state) {
case INI:
if (name.equals(ICAL_BEGIN) && value.equals(ICAL_VTIMEZONE)) {
state = VTZ;
}
break;
case VTZ:
if (name.equals(ICAL_TZID)) {
tzid = value;
} else if (name.equals(ICAL_TZURL)) {
tzurl = value;
} else if (name.equals(ICAL_LASTMOD)) {
// Always in 'Z' format, so the offset argument for the parse method
// can be any value.
lastmod = new Date(parseDateTimeString(value, 0));
} else if (name.equals(ICAL_BEGIN)) {
boolean isDST = value.equals(ICAL_DAYLIGHT);
if (value.equals(ICAL_STANDARD) || isDST) {
// tzid must be ready at this point
if (tzid == null) {
state = ERR;
break;
}
// initialize current zone properties
dates = null;
isRRULE = false;
from = null;
to = null;
tzname = null;
dst = isDST;
state = TZI;
} else {
// BEGIN property other than STANDARD/DAYLIGHT
// must not be there.
state = ERR;
break;
}
} else if (name.equals(ICAL_END) /* && value.equals(ICAL_VTIMEZONE) */) {
break;
}
break;
case TZI:
if (name.equals(ICAL_DTSTART)) {
dtstart = value;
} else if (name.equals(ICAL_TZNAME)) {
tzname = value;
} else if (name.equals(ICAL_TZOFFSETFROM)) {
from = value;
} else if (name.equals(ICAL_TZOFFSETTO)) {
to = value;
} else if (name.equals(ICAL_RDATE)) {
// RDATE mixed with RRULE is not supported
if (isRRULE) {
state = ERR;
break;
}
if (dates == null) {
dates = new LinkedList();
}
// RDATE value may contain multiple date delimited
// by comma
StringTokenizer st = new StringTokenizer(value, COMMA);
while (st.hasMoreTokens()) {
String date = st.nextToken();
dates.add(date);
}
} else if (name.equals(ICAL_RRULE)) {
// RRULE mixed with RDATE is not supported
if (!isRRULE && dates != null) {
state = ERR;
break;
} else if (dates == null) {
dates = new LinkedList();
}
isRRULE = true;
dates.add(value);
} else if (name.equals(ICAL_END)) {
// Mandatory properties
if (dtstart == null || from == null || to == null) {
state = ERR;
break;
}
// if tzname is not available, create one from tzid
if (tzname == null) {
tzname = getDefaultTZName(tzid, dst);
}
// create a time zone rule
TimeZoneRule rule = null;
int fromOffset = 0;
int toOffset = 0;
int rawOffset = 0;
int dstSavings = 0;
long start = 0;
try {
// Parse TZOFFSETFROM/TZOFFSETTO
fromOffset = offsetStrToMillis(from);
toOffset = offsetStrToMillis(to);
if (dst) {
// If daylight, use the previous offset as rawoffset if positive
if (toOffset - fromOffset > 0) {
rawOffset = fromOffset;
dstSavings = toOffset - fromOffset;
} else {
// This is rare case.. just use 1 hour DST savings
rawOffset = toOffset - DEF_DSTSAVINGS;
dstSavings = DEF_DSTSAVINGS;
}
} else {
rawOffset = toOffset;
dstSavings = 0;
}
// start time
start = parseDateTimeString(dtstart, fromOffset);
// Create the rule
Date actualStart = null;
if (isRRULE) {
rule = createRuleByRRULE(tzname, rawOffset, dstSavings, start, dates, fromOffset);
} else {
rule = createRuleByRDATE(tzname, rawOffset, dstSavings, start, dates, fromOffset);
}
if (rule != null) {
actualStart = rule.getFirstStart(fromOffset, 0);
if (actualStart.getTime() < firstStart) {
// save from offset information for the earliest rule
firstStart = actualStart.getTime();
// If this is STD, assume the time before this transtion
// is DST when the difference is 1 hour. This might not be
// accurate, but VTIMEZONE data does not have such info.
if (dstSavings > 0) {
initialRawOffset = fromOffset;
initialDSTSavings = 0;
} else {
if (fromOffset - toOffset == DEF_DSTSAVINGS) {
initialRawOffset = fromOffset - DEF_DSTSAVINGS;
initialDSTSavings = DEF_DSTSAVINGS;
} else {
initialRawOffset = fromOffset;
initialDSTSavings = 0;
}
}
}
}
} catch (IllegalArgumentException iae) {
// bad format - rule == null..
}
if (rule == null) {
state = ERR;
break;
}
rules.add(rule);
state = VTZ;
}
break;
}
if (state == ERR) {
vtzlines = null;
return false;
}
}
// Must have at least one rule
if (rules.size() == 0) {
return false;
}
// Create a initial rule
InitialTimeZoneRule initialRule = new InitialTimeZoneRule(getDefaultTZName(tzid, false),
initialRawOffset, initialDSTSavings);
// Finally, create the RuleBasedTimeZone
RuleBasedTimeZone rbtz = new RuleBasedTimeZone(tzid, initialRule);
int finalRuleIdx = -1;
int finalRuleCount = 0;
for (int i = 0; i < rules.size(); i++) {
TimeZoneRule r = (TimeZoneRule)rules.get(i);
if (r instanceof AnnualTimeZoneRule) {
if (((AnnualTimeZoneRule)r).getEndYear() == AnnualTimeZoneRule.MAX_YEAR) {
finalRuleCount++;
finalRuleIdx = i;
}
}
}
if (finalRuleCount > 2) {
// Too many final rules
return false;
}
if (finalRuleCount == 1) {
if (rules.size() == 1) {
// Only one final rule, only governs the initial rule,
// which is already initialized, thus, we do not need to
// add this transition rule
rules.clear();
} else {
// Normalize the final rule
AnnualTimeZoneRule finalRule = (AnnualTimeZoneRule)rules.get(finalRuleIdx);
int tmpRaw = finalRule.getRawOffset();
int tmpDST = finalRule.getDSTSavings();
// Find the last non-final rule
Date finalStart = finalRule.getFirstStart(initialRawOffset, initialDSTSavings);
Date start = finalStart;
for (int i = 0; i < rules.size(); i++) {
if (finalRuleIdx == i) {
continue;
}
TimeZoneRule r = (TimeZoneRule)rules.get(i);
Date lastStart = r.getFinalStart(tmpRaw, tmpDST);
if (lastStart.after(start)) {
start = finalRule.getNextStart(lastStart.getTime(),
r.getRawOffset(),
r.getDSTSavings(),
false);
}
}
TimeZoneRule newRule;
if (start == finalStart) {
// Transform this into a single transition
newRule = new TimeArrayTimeZoneRule(
finalRule.getName(),
finalRule.getRawOffset(),
finalRule.getDSTSavings(),
new long[] {finalStart.getTime()},
DateTimeRule.UTC_TIME);
} else {
// Update the end year
int fields[] = Grego.timeToFields(start.getTime(), null);
newRule = new AnnualTimeZoneRule(
finalRule.getName(),
finalRule.getRawOffset(),
finalRule.getDSTSavings(),
finalRule.getRule(),
finalRule.getStartYear(),
fields[0]);
}
rules.set(finalRuleIdx, newRule);
}
}
Iterator rit = rules.iterator();
while(rit.hasNext()) {
rbtz.addTransitionRule((TimeZoneRule)rit.next());
}
tz = rbtz;
setID(tzid);
return true;
}
/*
* Create a default TZNAME from TZID
*/
private static String getDefaultTZName(String tzid, boolean isDST) {
if (isDST) {
return tzid + "(DST)";
}
return tzid + "(STD)";
}
/*
* Create a TimeZoneRule by the RRULE definition
*/
private static TimeZoneRule createRuleByRRULE(String tzname,
int rawOffset, int dstSavings, long start, List dates, int fromOffset) {
if (dates == null || dates.size() == 0) {
return null;
}
// Parse the first rule
String rrule = (String)dates.get(0);
long until[] = new long[1];
int[] ruleFields = parseRRULE(rrule, until);
if (ruleFields == null) {
// Invalid RRULE
return null;
}
int month = ruleFields[0];
int dayOfWeek = ruleFields[1];
int nthDayOfWeek = ruleFields[2];
int dayOfMonth = ruleFields[3];
if (dates.size() == 1) {
// No more rules
if (ruleFields.length > 4) {
// Multiple BYMONTHDAY values
if (ruleFields.length != 10 || month == -1 || dayOfWeek == 0) {
// Only support the rule using 7 continuous days
// BYMONTH and BYDAY must be set at the same time
return null;
}
int firstDay = 31; // max possible number of dates in a month
int days[] = new int[7];
for (int i = 0; i < 7; i++) {
days[i] = ruleFields[3 + i];
// Resolve negative day numbers. A negative day number should
// not be used in February, but if we see such case, we use 28
// as the base.
days[i] = days[i] > 0 ? days[i] : MONTHLENGTH[month] + days[i] + 1;
firstDay = days[i] < firstDay ? days[i] : firstDay;
}
// Make sure days are continuous
for (int i = 1; i < 7; i++) {
boolean found = false;
for (int j = 0; j < 7; j++) {
if (days[j] == firstDay + i) {
found = true;
break;
}
}
if (!found) {
// days are not continuous
return null;
}
}
// Use DOW_GEQ_DOM rule with firstDay as the start date
dayOfMonth = firstDay;
}
} else {
// Check if BYMONTH + BYMONTHDAY + BYDAY rule with multiple RRULE lines.
// Otherwise, not supported.
if (month == -1 || dayOfWeek == 0 || dayOfMonth == 0) {
// This is not the case
return null;
}
// Parse the rest of rules if number of rules is not exceeding 7.
// We can only support 7 continuous days starting from a day of month.
if (dates.size() > 7) {
return null;
}
// Note: To check valid date range across multiple rule is a little
// bit complicated. For now, this code is not doing strict range
// checking across month boundary
int earliestMonth = month;
int daysCount = ruleFields.length - 3;
int earliestDay = 31;
for (int i = 0; i < daysCount; i++) {
int dom = ruleFields[3 + i];
dom = dom > 0 ? dom : MONTHLENGTH[month] + dom + 1;
earliestDay = dom < earliestDay ? dom : earliestDay;
}
int anotherMonth = -1;
for (int i = 1; i < dates.size(); i++) {
rrule = (String)dates.get(i);
long[] unt = new long[1];
int[] fields = parseRRULE(rrule, unt);
// If UNTIL is newer than previous one, use the one
if (unt[0] > until[0]) {
until = unt;
}
// Check if BYMONTH + BYMONTHDAY + BYDAY rule
if (fields[0] == -1 || fields[1] == 0 || fields[3] == 0) {
return null;
}
// Count number of BYMONTHDAY
int count = fields.length - 3;
if (daysCount + count > 7) {
// We cannot support BYMONTHDAY more than 7
return null;
}
// Check if the same BYDAY is used. Otherwise, we cannot
// support the rule
if (fields[1] != dayOfWeek) {
return null;
}
// Check if the month is same or right next to the primary month
if (fields[0] != month) {
if (anotherMonth == -1) {
int diff = fields[0] - month;
if (diff == -11 || diff == -1) {
// Previous month
anotherMonth = fields[0];
earliestMonth = anotherMonth;
// Reset earliest day
earliestDay = 31;
} else if (diff == 11 || diff == 1) {
// Next month
anotherMonth = fields[0];
} else {
// The day range cannot exceed more than 2 months
return null;
}
} else if (fields[0] != month && fields[0] != anotherMonth) {
// The day range cannot exceed more than 2 months
return null;
}
}
// If ealier month, go through days to find the earliest day
if (fields[0] == earliestMonth) {
for (int j = 0; j < count; j++) {
int dom = fields[3 + j];
dom = dom > 0 ? dom : MONTHLENGTH[fields[0]] + dom + 1;
earliestDay = dom < earliestDay ? dom : earliestDay;
}
}
daysCount += count;
}
if (daysCount != 7) {
// Number of BYMONTHDAY entries must be 7
return null;
}
month = earliestMonth;
dayOfMonth = earliestDay;
}
// Calculate start/end year and missing fields
int[] dfields = Grego.timeToFields(start + fromOffset, null);
int startYear = dfields[0];
if (month == -1) {
// If MYMONTH is not set, use the month of DTSTART
month = dfields[1];
}
if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth == 0) {
// If only YEARLY is set, use the day of DTSTART as BYMONTHDAY
dayOfMonth = dfields[2];
}
int timeInDay = dfields[5];
int endYear = AnnualTimeZoneRule.MAX_YEAR;
if (until[0] != MIN_TIME) {
Grego.timeToFields(until[0], dfields);
endYear = dfields[0];
}
// Create the AnnualDateTimeRule
DateTimeRule adtr = null;
if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
// Day in month rule, for example, 15th day in the month
adtr = new DateTimeRule(month, dayOfMonth, timeInDay, DateTimeRule.WALL_TIME);
} else if (dayOfWeek != 0 && nthDayOfWeek != 0 && dayOfMonth == 0) {
// Nth day of week rule, for example, last Sunday
adtr = new DateTimeRule(month, nthDayOfWeek, dayOfWeek, timeInDay, DateTimeRule.WALL_TIME);
} else if (dayOfWeek != 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
// First day of week after day of month rule, for example,
// first Sunday after 15th day in the month
adtr = new DateTimeRule(month, dayOfMonth, dayOfWeek, true, timeInDay, DateTimeRule.WALL_TIME);
} else {
// RRULE attributes are insufficient
return null;
}
return new AnnualTimeZoneRule(tzname, rawOffset, dstSavings, adtr, startYear, endYear);
}
/*
* Parse individual RRULE
*
* On return -
*
* int[0] month calculated by BYMONTH - 1, or -1 when not found
* int[1] day of week in BYDAY, or 0 when not found
* int[2] day of week ordinal number in BYDAY, or 0 when not found
* int[i >= 3] day of month, which could be multiple values, or 0 when not found
*
* or
*
* null on any error cases, for exmaple, FREQ=YEARLY is not available
*
* When UNTIL attribute is available, the time will be set to until[0],
* otherwise, MIN_TIME
*/
private static int[] parseRRULE(String rrule, long[] until) {
int month = -1;
int dayOfWeek = 0;
int nthDayOfWeek = 0;
int[] dayOfMonth = null;
long untilTime = MIN_TIME;
boolean yearly = false;
boolean parseError = false;
StringTokenizer st= new StringTokenizer(rrule, SEMICOLON);
while (st.hasMoreTokens()) {
String attr, value;
String prop = st.nextToken();
int sep = prop.indexOf(EQUALS_SIGN);
if (sep != -1) {
attr = prop.substring(0, sep);
value = prop.substring(sep + 1);
} else {
parseError = true;
break;
}
if (attr.equals(ICAL_FREQ)) {
// only support YEARLY frequency type
if (value.equals(ICAL_YEARLY)) {
yearly = true;
} else {
parseError = true;
break;
}
} else if (attr.equals(ICAL_UNTIL)) {
// ISO8601 UTC format, for example, "20060315T020000Z"
try {
untilTime = parseDateTimeString(value, 0);
} catch (IllegalArgumentException iae) {
parseError = true;
break;
}
} else if (attr.equals(ICAL_BYMONTH)) {
// Note: BYMONTH may contain multiple months, but only single month make sense for
// VTIMEZONE property.
if (value.length() > 2) {
parseError = true;
break;
}
try {
month = Integer.parseInt(value) - 1;
if (month < 0 || month >= 12) {
parseError = true;
break;
}
} catch (NumberFormatException nfe) {
parseError = true;
break;
}
} else if (attr.equals(ICAL_BYDAY)) {
// Note: BYDAY may contain multiple day of week separated by comma. It is unlikely used for
// VTIMEZONE property. We do not support the case.
// 2-letter format is used just for representing a day of week, for example, "SU" for Sunday
// 3 or 4-letter format is used for represeinging Nth day of week, for example, "-1SA" for last Saturday
int length = value.length();
if (length < 2 || length > 4) {
parseError = true;
break;
}
if (length > 2) {
// Nth day of week
int sign = 1;
if (value.charAt(0) == '+') {
sign = 1;
} else if (value.charAt(0) == '-') {
sign = -1;
} else if (length == 4) {
parseError = true;
break;
}
try {
int n = Integer.parseInt(value.substring(length - 3, length - 2));
if (n == 0 || n > 4) {
parseError = true;
break;
}
nthDayOfWeek = n * sign;
} catch(NumberFormatException nfe) {
parseError = true;
break;
}
value = value.substring(length - 2);
}
int wday;
for (wday = 0; wday < ICAL_DOW_NAMES.length; wday++) {
if (value.equals(ICAL_DOW_NAMES[wday])) {
break;
}
}
if (wday < ICAL_DOW_NAMES.length) {
// Sunday(1) - Saturday(7)
dayOfWeek = wday + 1;
} else {
parseError = true;
break;
}
} else if (attr.equals(ICAL_BYMONTHDAY)) {
// Note: BYMONTHDAY may contain multiple days delimited by comma
//
// A value of BYMONTHDAY could be negative, for example, -1 means
// the last day in a month
StringTokenizer days = new StringTokenizer(value, COMMA);
int count = days.countTokens();
dayOfMonth = new int[count];
int index = 0;
while(days.hasMoreTokens()) {
try {
dayOfMonth[index++] = Integer.parseInt(days.nextToken());
} catch (NumberFormatException nfe) {
parseError = true;
break;
}
}
}
}
if (parseError) {
return null;
}
if (!yearly) {
// FREQ=YEARLY must be set
return null;
}
until[0] = untilTime;
int[] results;
if (dayOfMonth == null) {
results = new int[4];
results[3] = 0;
} else {
results = new int[3 + dayOfMonth.length];
for (int i = 0; i < dayOfMonth.length; i++) {
results[3 + i] = dayOfMonth[i];
}
}
results[0] = month;
results[1] = dayOfWeek;
results[2] = nthDayOfWeek;
return results;
}
/*
* Create a TimeZoneRule by the RDATE definition
*/
private static TimeZoneRule createRuleByRDATE(String tzname,
int rawOffset, int dstSavings, long start, List dates, int fromOffset) {
// Create an array of transition times
long[] times;
if (dates == null || dates.size() == 0) {
// When no RDATE line is provided, use start (DTSTART)
// as the transition time
times = new long[1];
times[0] = start;
} else {
times = new long[dates.size()];
Iterator it = dates.iterator();
int idx = 0;
try {
while(it.hasNext()) {
times[idx++] = parseDateTimeString((String)it.next(), fromOffset);
}
} catch (IllegalArgumentException iae) {
return null;
}
}
return new TimeArrayTimeZoneRule(tzname, rawOffset, dstSavings, times, DateTimeRule.UTC_TIME);
}
/*
* Write the time zone rules in RFC2445 VTIMEZONE format
*/
private void writeZone(Writer w, BasicTimeZone basictz, String[] customProperties) throws IOException {
// Write the header
writeHeader(w);
if (customProperties != null && customProperties.length > 0) {
for (int i = 0; i < customProperties.length; i++) {
if (customProperties[i] != null) {
w.write(customProperties[i]);
w.write(NEWLINE);
}
}
}
long t = MIN_TIME;
String dstName = null;
int dstFromOffset = 0;
int dstFromDSTSavings = 0;
int dstToOffset = 0;
int dstStartYear = 0;
int dstMonth = 0;
int dstDayOfWeek = 0;
int dstWeekInMonth = 0;
int dstMillisInDay = 0;
long dstStartTime = 0;
long dstUntilTime = 0;
int dstCount = 0;
AnnualTimeZoneRule finalDstRule = null;
String stdName = null;
int stdFromOffset = 0;
int stdFromDSTSavings = 0;
int stdToOffset = 0;
int stdStartYear = 0;
int stdMonth = 0;
int stdDayOfWeek = 0;
int stdWeekInMonth = 0;
int stdMillisInDay = 0;
long stdStartTime = 0;
long stdUntilTime = 0;
int stdCount = 0;
AnnualTimeZoneRule finalStdRule = null;
int[] dtfields = new int[6];
boolean hasTransitions = false;
// Going through all transitions
while(true) {
TimeZoneTransition tzt = basictz.getNextTransition(t, false);
if (tzt == null) {
break;
}
hasTransitions = true;
t = tzt.getTime();
String name = tzt.getTo().getName();
boolean isDst = (tzt.getTo().getDSTSavings() != 0);
int fromOffset = tzt.getFrom().getRawOffset() + tzt.getFrom().getDSTSavings();
int fromDSTSavings = tzt.getFrom().getDSTSavings();
int toOffset = tzt.getTo().getRawOffset() + tzt.getTo().getDSTSavings();
Grego.timeToFields(tzt.getTime() + fromOffset, dtfields);
int weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], dtfields[2]);
int year = dtfields[0];
boolean sameRule = false;
if (isDst) {
if (finalDstRule == null && tzt.getTo() instanceof AnnualTimeZoneRule) {
if (((AnnualTimeZoneRule)tzt.getTo()).getEndYear() == AnnualTimeZoneRule.MAX_YEAR) {
finalDstRule = (AnnualTimeZoneRule)tzt.getTo();
}
}
if (dstCount > 0) {
if (year == dstStartYear + dstCount
&& name.equals(dstName)
&& dstFromOffset == fromOffset
&& dstToOffset == toOffset
&& dstMonth == dtfields[1]
&& dstDayOfWeek == dtfields[3]
&& dstWeekInMonth == weekInMonth
&& dstMillisInDay == dtfields[5]) {
// Update until time
dstUntilTime = t;
dstCount++;
sameRule = true;
}
if (!sameRule) {
if (dstCount == 1) {
writeZonePropsByTime(w, true, dstName, dstFromOffset, dstToOffset,
dstStartTime, true);
} else {
writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime);
}
}
}
if (!sameRule) {
// Reset this DST information
dstName = name;
dstFromOffset = fromOffset;
dstFromDSTSavings = fromDSTSavings;
dstToOffset = toOffset;
dstStartYear = year;
dstMonth = dtfields[1];
dstDayOfWeek = dtfields[3];
dstWeekInMonth = weekInMonth;
dstMillisInDay = dtfields[5];
dstStartTime = dstUntilTime = t;
dstCount = 1;
}
if (finalStdRule != null && finalDstRule != null) {
break;
}
} else {
if (finalStdRule == null && tzt.getTo() instanceof AnnualTimeZoneRule) {
if (((AnnualTimeZoneRule)tzt.getTo()).getEndYear() == AnnualTimeZoneRule.MAX_YEAR) {
finalStdRule = (AnnualTimeZoneRule)tzt.getTo();
}
}
if (stdCount > 0) {
if (year == stdStartYear + stdCount
&& name.equals(stdName)
&& stdFromOffset == fromOffset
&& stdToOffset == toOffset
&& stdMonth == dtfields[1]
&& stdDayOfWeek == dtfields[3]
&& stdWeekInMonth == weekInMonth
&& stdMillisInDay == dtfields[5]) {
// Update until time
stdUntilTime = t;
stdCount++;
sameRule = true;
}
if (!sameRule) {
if (stdCount == 1) {
writeZonePropsByTime(w, false, stdName, stdFromOffset, stdToOffset,
stdStartTime, true);
} else {
writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime);
}
}
}
if (!sameRule) {
// Reset this STD information
stdName = name;
stdFromOffset = fromOffset;
stdFromDSTSavings = fromDSTSavings;
stdToOffset = toOffset;
stdStartYear = year;
stdMonth = dtfields[1];
stdDayOfWeek = dtfields[3];
stdWeekInMonth = weekInMonth;
stdMillisInDay = dtfields[5];
stdStartTime = stdUntilTime = t;
stdCount = 1;
}
if (finalStdRule != null && finalDstRule != null) {
break;
}
}
}
if (!hasTransitions) {
// No transition - put a single non transition RDATE
int offset = basictz.getOffset(0 /* any time */);
boolean isDst = (offset != basictz.getRawOffset());
writeZonePropsByTime(w, isDst, getDefaultTZName(basictz.getID(), isDst),
offset, offset, DEF_TZSTARTTIME - offset, false);
} else {
if (dstCount > 0) {
if (finalDstRule == null) {
if (dstCount == 1) {
writeZonePropsByTime(w, true, dstName, dstFromOffset, dstToOffset,
dstStartTime, true);
} else {
writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime);
}
} else {
if (dstCount == 1) {
writeFinalRule(w, true, finalDstRule,
dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime);
} else {
// Use a single rule if possible
if (isEquivalentDateRule(dstMonth, dstWeekInMonth, dstDayOfWeek, finalDstRule.getRule())) {
writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, MAX_TIME);
} else {
// Not equivalent rule - write out two different rules
writeZonePropsByDOW(w, true, dstName, dstFromOffset, dstToOffset,
dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime);
writeFinalRule(w, true, finalDstRule,
dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime);
}
}
}
}
if (stdCount > 0) {
if (finalStdRule == null) {
if (stdCount == 1) {
writeZonePropsByTime(w, false, stdName, stdFromOffset, stdToOffset,
stdStartTime, true);
} else {
writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime);
}
} else {
if (stdCount == 1) {
writeFinalRule(w, false, finalStdRule,
stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime);
} else {
// Use a single rule if possible
if (isEquivalentDateRule(stdMonth, stdWeekInMonth, stdDayOfWeek, finalStdRule.getRule())) {
writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, MAX_TIME);
} else {
// Not equivalent rule - write out two different rules
writeZonePropsByDOW(w, false, stdName, stdFromOffset, stdToOffset,
stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime);
writeFinalRule(w, false, finalStdRule,
stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime);
}
}
}
}
}
writeFooter(w);
}
/*
* Check if the DOW rule specified by month, weekInMonth and dayOfWeek is equivalent
* to the DateTimerule.
*/
private static boolean isEquivalentDateRule(int month, int weekInMonth, int dayOfWeek, DateTimeRule dtrule) {
if (month != dtrule.getRuleMonth() || dayOfWeek != dtrule.getRuleDayOfWeek()) {
return false;
}
if (dtrule.getTimeRuleType() != DateTimeRule.WALL_TIME) {
// Do not try to do more intelligent comparison for now.
return false;
}
if (dtrule.getDateRuleType() == DateTimeRule.DOW
&& dtrule.getRuleWeekInMonth() == weekInMonth) {
return true;
}
int ruleDOM = dtrule.getRuleDayOfMonth();
if (dtrule.getDateRuleType() == DateTimeRule.DOW_GEQ_DOM) {
if (ruleDOM%7 == 1 && (ruleDOM + 6)/7 == weekInMonth) {
return true;
}
if (month != Calendar.FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 6
&& weekInMonth == -1*((MONTHLENGTH[month]-ruleDOM+1)/7)) {
return true;
}
}
if (dtrule.getDateRuleType() == DateTimeRule.DOW_LEQ_DOM) {
if (ruleDOM%7 == 0 && ruleDOM/7 == weekInMonth) {
return true;
}
if (month != Calendar.FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 0
&& weekInMonth == -1*((MONTHLENGTH[month] - ruleDOM)/7 + 1)) {
return true;
}
}
return false;
}
/*
* Write a single start time
*/
private static void writeZonePropsByTime(Writer writer, boolean isDst, String tzname,
int fromOffset, int toOffset, long time, boolean withRDATE) throws IOException {
beginZoneProps(writer, isDst, tzname, fromOffset, toOffset, time);
if (withRDATE) {
writer.write(ICAL_RDATE);
writer.write(COLON);
writer.write(getDateTimeString(time + fromOffset));
writer.write(NEWLINE);
}
endZoneProps(writer, isDst);
}
/*
* Write start times defined by a DOM rule using VTIMEZONE RRULE
*/
private static void writeZonePropsByDOM(Writer writer, boolean isDst, String tzname, int fromOffset, int toOffset,
int month, int dayOfMonth, long startTime, long untilTime) throws IOException {
beginZoneProps(writer, isDst, tzname, fromOffset, toOffset, startTime);
beginRRULE(writer, month);
writer.write(ICAL_BYMONTHDAY);
writer.write(EQUALS_SIGN);
writer.write(Integer.toString(dayOfMonth));
if (untilTime != MAX_TIME) {
appendUNTIL(writer, getDateTimeString(untilTime + fromOffset));
}
writer.write(NEWLINE);
endZoneProps(writer, isDst);
}
/*
* Write start times defined by a DOW rule using VTIMEZONE RRULE
*/
private static void writeZonePropsByDOW(Writer writer, boolean isDst, String tzname, int fromOffset, int toOffset,
int month, int weekInMonth, int dayOfWeek, long startTime, long untilTime) throws IOException {
beginZoneProps(writer, isDst, tzname, fromOffset, toOffset, startTime);
beginRRULE(writer, month);
writer.write(ICAL_BYDAY);
writer.write(EQUALS_SIGN);
writer.write(Integer.toString(weekInMonth)); // -4, -3, -2, -1, 1, 2, 3, 4
writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU...
if (untilTime != MAX_TIME) {
appendUNTIL(writer, getDateTimeString(untilTime + fromOffset));
}
writer.write(NEWLINE);
endZoneProps(writer, isDst);
}
/*
* Write start times defined by a DOW_GEQ_DOM rule using VTIMEZONE RRULE
*/
private static void writeZonePropsByDOW_GEQ_DOM(Writer writer, boolean isDst, String tzname, int fromOffset, int toOffset,
int month, int dayOfMonth, int dayOfWeek, long startTime, long untilTime) throws IOException {
// Check if this rule can be converted to DOW rule
if (dayOfMonth%7 == 1) {
// Can be represented by DOW rule
writeZonePropsByDOW(writer, isDst, tzname, fromOffset, toOffset,
month, (dayOfMonth + 6)/7, dayOfWeek, startTime, untilTime);
} else if (month != Calendar.FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 6) {
// Can be represented by DOW rule with negative week number
writeZonePropsByDOW(writer, isDst, tzname, fromOffset, toOffset,
month, -1*((MONTHLENGTH[month] - dayOfMonth + 1)/7), dayOfWeek, startTime, untilTime);
} else {
// Otherwise, use BYMONTHDAY to include all possible dates
beginZoneProps(writer, isDst, tzname, fromOffset, toOffset, startTime);
// Check if all days are in the same month
int startDay = dayOfMonth;
int currentMonthDays = 7;
if (dayOfMonth <= 0) {
// The start day is in previous month
int prevMonthDays = 1 - dayOfMonth;
currentMonthDays -= prevMonthDays;
int prevMonth = (month - 1) < 0 ? 11 : month - 1;
// Note: When a rule is separated into two, UNTIL attribute needs to be
// calculated for each of them. For now, we skip this, because we basically use this method
// only for final rules, which does not have the UNTIL attribute
writeZonePropsByDOW_GEQ_DOM_sub(writer, prevMonth, -prevMonthDays, dayOfWeek, prevMonthDays, MAX_TIME /* Do not use UNTIL */, fromOffset);
// Start from 1 for the rest
startDay = 1;
} else if (dayOfMonth + 6 > MONTHLENGTH[month]) {
// Note: This code does not actually work well in February. For now, days in month in
// non-leap year.
int nextMonthDays = dayOfMonth + 6 - MONTHLENGTH[month];
currentMonthDays -= nextMonthDays;
int nextMonth = (month + 1) > 11 ? 0 : month + 1;
writeZonePropsByDOW_GEQ_DOM_sub(writer, nextMonth, 1, dayOfWeek, nextMonthDays, MAX_TIME /* Do not use UNTIL */, fromOffset);
}
writeZonePropsByDOW_GEQ_DOM_sub(writer, month, startDay, dayOfWeek, currentMonthDays, untilTime, fromOffset);
endZoneProps(writer, isDst);
}
}
/*
* Called from writeZonePropsByDOW_GEQ_DOM
*/
private static void writeZonePropsByDOW_GEQ_DOM_sub(Writer writer, int month,
int dayOfMonth, int dayOfWeek, int numDays, long untilTime, int fromOffset) throws IOException {
int startDayNum = dayOfMonth;
boolean isFeb = (month == Calendar.FEBRUARY);
if (dayOfMonth < 0 && !isFeb) {
// Use positive number if possible
startDayNum = MONTHLENGTH[month] + dayOfMonth + 1;
}
beginRRULE(writer, month);
writer.write(ICAL_BYDAY);
writer.write(EQUALS_SIGN);
writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU...
writer.write(SEMICOLON);
writer.write(ICAL_BYMONTHDAY);
writer.write(EQUALS_SIGN);
writer.write(Integer.toString(startDayNum));
for (int i = 1; i < numDays; i++) {
writer.write(COMMA);
writer.write(Integer.toString(startDayNum + i));
}
if (untilTime != MAX_TIME) {
appendUNTIL(writer, getDateTimeString(untilTime + fromOffset));
}
writer.write(NEWLINE);
}
/*
* Write start times defined by a DOW_LEQ_DOM rule using VTIMEZONE RRULE
*/
private static void writeZonePropsByDOW_LEQ_DOM(Writer writer, boolean isDst, String tzname, int fromOffset, int toOffset,
int month, int dayOfMonth, int dayOfWeek, long startTime, long untilTime) throws IOException {
// Check if this rule can be converted to DOW rule
if (dayOfMonth%7 == 0) {
// Can be represented by DOW rule
writeZonePropsByDOW(writer, isDst, tzname, fromOffset, toOffset,
month, dayOfMonth/7, dayOfWeek, startTime, untilTime);
} else if (month != Calendar.FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 0){
// Can be represented by DOW rule with negative week number
writeZonePropsByDOW(writer, isDst, tzname, fromOffset, toOffset,
month, -1*((MONTHLENGTH[month] - dayOfMonth)/7 + 1), dayOfWeek, startTime, untilTime);
} else if (month == Calendar.FEBRUARY && dayOfMonth == 29) {
// Specical case for February
writeZonePropsByDOW(writer, isDst, tzname, fromOffset, toOffset,
Calendar.FEBRUARY, -1, dayOfWeek, startTime, untilTime);
} else {
// Otherwise, convert this to DOW_GEQ_DOM rule
writeZonePropsByDOW_GEQ_DOM(writer, isDst, tzname, fromOffset, toOffset,
month, dayOfMonth - 6, dayOfWeek, startTime, untilTime);
}
}
/*
* Write the final time zone rule using RRULE, with no UNTIL attribute
*/
private static void writeFinalRule(Writer writer, boolean isDst, AnnualTimeZoneRule rule,
int fromRawOffset, int fromDSTSavings, long startTime) throws IOException{
DateTimeRule dtrule = toWallTimeRule(rule.getRule(), fromRawOffset, fromDSTSavings);
int toOffset = rule.getRawOffset() + rule.getDSTSavings();
switch (dtrule.getDateRuleType()) {
case DateTimeRule.DOM:
writeZonePropsByDOM(writer, isDst, rule.getName(), fromRawOffset + fromDSTSavings, toOffset,
dtrule.getRuleMonth(), dtrule.getRuleDayOfMonth(), startTime, MAX_TIME);
break;
case DateTimeRule.DOW:
writeZonePropsByDOW(writer, isDst, rule.getName(), fromRawOffset + fromDSTSavings, toOffset,
dtrule.getRuleMonth(), dtrule.getRuleWeekInMonth(), dtrule.getRuleDayOfWeek(), startTime, MAX_TIME);
break;
case DateTimeRule.DOW_GEQ_DOM:
writeZonePropsByDOW_GEQ_DOM(writer, isDst, rule.getName(), fromRawOffset + fromDSTSavings, toOffset,
dtrule.getRuleMonth(), dtrule.getRuleDayOfMonth(), dtrule.getRuleDayOfWeek(), startTime, MAX_TIME);
break;
case DateTimeRule.DOW_LEQ_DOM:
writeZonePropsByDOW_LEQ_DOM(writer, isDst, rule.getName(), fromRawOffset + fromDSTSavings, toOffset,
dtrule.getRuleMonth(), dtrule.getRuleDayOfMonth(), dtrule.getRuleDayOfWeek(), startTime, MAX_TIME);
break;
}
}
/*
* Convert the rule to its equivalent rule using WALL_TIME mode
*/
private static DateTimeRule toWallTimeRule(DateTimeRule rule, int rawOffset, int dstSavings) {
if (rule.getTimeRuleType() == DateTimeRule.WALL_TIME) {
return rule;
}
int wallt = rule.getRuleMillisInDay();
if (rule.getTimeRuleType() == DateTimeRule.UTC_TIME) {
wallt += (rawOffset + dstSavings);
} else if (rule.getTimeRuleType() == DateTimeRule.STANDARD_TIME) {
wallt += dstSavings;
}
int month = -1, dom = 0, dow = 0, dtype = -1;
int dshift = 0;
if (wallt < 0) {
dshift = -1;
wallt += Grego.MILLIS_PER_DAY;
} else if (wallt >= Grego.MILLIS_PER_DAY) {
dshift = 1;
wallt -= Grego.MILLIS_PER_DAY;
}
month = rule.getRuleMonth();
dom = rule.getRuleDayOfMonth();
dow = rule.getRuleDayOfWeek();
dtype = rule.getDateRuleType();
if (dshift != 0) {
if (dtype == DateTimeRule.DOW) {
// Convert to DOW_GEW_DOM or DOW_LEQ_DOM rule first
int wim = rule.getRuleWeekInMonth();
if (wim > 0) {
dtype = DateTimeRule.DOW_GEQ_DOM;
dom = 7 * (wim - 1) + 1;
} else {
dtype = DateTimeRule.DOW_LEQ_DOM;
dom = MONTHLENGTH[month] + 7 * (wim + 1);
}
}
// Shift one day before or after
dom += dshift;
if (dom == 0) {
month--;
month = month < Calendar.JANUARY ? Calendar.DECEMBER : month;
dom = MONTHLENGTH[month];
} else if (dom > MONTHLENGTH[month]) {
month++;
month = month > Calendar.DECEMBER ? Calendar.JANUARY : month;
dom = 1;
}
if (dtype != DateTimeRule.DOM) {
// Adjust day of week
dow += dshift;
if (dow < Calendar.SUNDAY) {
dow = Calendar.SATURDAY;
} else if (dow > Calendar.SATURDAY) {
dow = Calendar.SUNDAY;
}
}
}
// Create a new rule
DateTimeRule modifiedRule;
if (dtype == DateTimeRule.DOM) {
modifiedRule = new DateTimeRule(month, dom, wallt, DateTimeRule.WALL_TIME);
} else {
modifiedRule = new DateTimeRule(month, dom, dow,
(dtype == DateTimeRule.DOW_GEQ_DOM), wallt, DateTimeRule.WALL_TIME);
}
return modifiedRule;
}
/*
* Write the opening section of zone properties
*/
private static void beginZoneProps(Writer writer, boolean isDst, String tzname, int fromOffset, int toOffset, long startTime) throws IOException {
writer.write(ICAL_BEGIN);
writer.write(COLON);
if (isDst) {
writer.write(ICAL_DAYLIGHT);
} else {
writer.write(ICAL_STANDARD);
}
writer.write(NEWLINE);
// TZOFFSETTO
writer.write(ICAL_TZOFFSETTO);
writer.write(COLON);
writer.write(millisToOffset(toOffset));
writer.write(NEWLINE);
// TZOFFSETFROM
writer.write(ICAL_TZOFFSETFROM);
writer.write(COLON);
writer.write(millisToOffset(fromOffset));
writer.write(NEWLINE);
// TZNAME
writer.write(ICAL_TZNAME);
writer.write(COLON);
writer.write(tzname);
writer.write(NEWLINE);
// DTSTART
writer.write(ICAL_DTSTART);
writer.write(COLON);
writer.write(getDateTimeString(startTime + fromOffset));
writer.write(NEWLINE);
}
/*
* Writes the closing section of zone properties
*/
private static void endZoneProps(Writer writer, boolean isDst) throws IOException{
// END:STANDARD or END:DAYLIGHT
writer.write(ICAL_END);
writer.write(COLON);
if (isDst) {
writer.write(ICAL_DAYLIGHT);
} else {
writer.write(ICAL_STANDARD);
}
writer.write(NEWLINE);
}
/*
* Write the beginning part of RRULE line
*/
private static void beginRRULE(Writer writer, int month) throws IOException {
writer.write(ICAL_RRULE);
writer.write(COLON);
writer.write(ICAL_FREQ);
writer.write(EQUALS_SIGN);
writer.write(ICAL_YEARLY);
writer.write(SEMICOLON);
writer.write(ICAL_BYMONTH);
writer.write(EQUALS_SIGN);
writer.write(Integer.toString(month + 1));
writer.write(SEMICOLON);
}
/*
* Append the UNTIL attribute after RRULE line
*/
private static void appendUNTIL(Writer writer, String until) throws IOException {
if (until != null) {
writer.write(SEMICOLON);
writer.write(ICAL_UNTIL);
writer.write(EQUALS_SIGN);
writer.write(until);
}
}
/*
* Write the opening section of the VTIMEZONE block
*/
private void writeHeader(Writer writer)throws IOException {
writer.write(ICAL_BEGIN);
writer.write(COLON);
writer.write(ICAL_VTIMEZONE);
writer.write(NEWLINE);
writer.write(ICAL_TZID);
writer.write(COLON);
writer.write(tz.getID());
writer.write(NEWLINE);
if (tzurl != null) {
writer.write(ICAL_TZURL);
writer.write(COLON);
writer.write(tzurl);
writer.write(NEWLINE);
}
if (lastmod != null) {
writer.write(ICAL_LASTMOD);
writer.write(COLON);
writer.write(getUTCDateTimeString(lastmod.getTime()));
writer.write(NEWLINE);
}
}
/*
* Write the closing section of the VTIMEZONE definition block
*/
private static void writeFooter(Writer writer) throws IOException {
writer.write(ICAL_END);
writer.write(COLON);
writer.write(ICAL_VTIMEZONE);
writer.write(NEWLINE);
}
/*
* Convert date/time to RFC2445 Date-Time form #1 DATE WITH LOCAL TIME
*/
private static String getDateTimeString(long time) {
int[] fields = Grego.timeToFields(time, null);
StringBuffer sb = new StringBuffer(15);
sb.append(numToString(fields[0], 4));
sb.append(numToString(fields[1] + 1, 2));
sb.append(numToString(fields[2], 2));
sb.append('T');
int t = fields[5];
int hour = t / Grego.MILLIS_PER_HOUR;
t %= Grego.MILLIS_PER_HOUR;
int min = t / Grego.MILLIS_PER_MINUTE;
t %= Grego.MILLIS_PER_MINUTE;
int sec = t / Grego.MILLIS_PER_SECOND;
sb.append(numToString(hour, 2));
sb.append(numToString(min, 2));
sb.append(numToString(sec, 2));
return sb.toString();
}
/*
* Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME
*/
private static String getUTCDateTimeString(long time) {
return getDateTimeString(time) + "Z";
}
/*
* Parse RFC2445 Date-Time form #1 DATE WITH LOCAL TIME and
* #2 DATE WITH UTC TIME
*/
private static long parseDateTimeString(String str, int offset) {
int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;
boolean isUTC = false;
boolean isValid = false;
do {
if (str == null) {
break;
}
int length = str.length();
if (length != 15 && length != 16) {
// FORM#1 15 characters, such as "20060317T142115"
// FORM#2 16 characters, such as "20060317T142115Z"
break;
}
if (str.charAt(8) != 'T') {
// charcter "T" must be used for separating date and time
break;
}
if (length == 16) {
if (str.charAt(15) != 'Z') {
// invalid format
break;
}
isUTC = true;
}
try {
year = Integer.parseInt(str.substring(0, 4));
month = Integer.parseInt(str.substring(4, 6)) - 1; // 0-based
day = Integer.parseInt(str.substring(6, 8));
hour = Integer.parseInt(str.substring(9, 11));
min = Integer.parseInt(str.substring(11, 13));
sec = Integer.parseInt(str.substring(13, 15));
} catch (NumberFormatException nfe) {
break;
}
// check valid range
int maxDayOfMonth = Grego.monthLength(year, month);
if (year < 0 || month < 0 || month > 11 || day < 1 || day > maxDayOfMonth ||
hour < 0 || hour >= 24 || min < 0 || min >= 60 || sec < 0 || sec >= 60) {
break;
}
isValid = true;
} while(false);
if (!isValid) {
throw new IllegalArgumentException("Invalid date time string format");
}
// Calculate the time
long time = Grego.fieldsToDay(year, month, day) * Grego.MILLIS_PER_DAY;
time += (hour*Grego.MILLIS_PER_HOUR + min*Grego.MILLIS_PER_MINUTE + sec*Grego.MILLIS_PER_SECOND);
if (!isUTC) {
time -= offset;
}
return time;
}
/*
* Convert RFC2445 utc-offset string to milliseconds
*/
private static int offsetStrToMillis(String str) {
boolean isValid = false;
int sign = 0, hour = 0, min = 0, sec = 0;
do {
if (str == null) {
break;
}
int length = str.length();
if (length != 5 && length != 7) {
// utf-offset must be 5 or 7 characters
break;
}
// sign
char s = str.charAt(0);
if (s == '+') {
sign = 1;
} else if (s == '-') {
sign = -1;
} else {
// utf-offset must start with "+" or "-"
break;
}
try {
hour = Integer.parseInt(str.substring(1, 3));
min = Integer.parseInt(str.substring(3, 5));
if (length == 7) {
sec = Integer.parseInt(str.substring(5, 7));
}
} catch (NumberFormatException nfe) {
break;
}
isValid = true;
} while(false);
if (!isValid) {
throw new IllegalArgumentException("Bad offset string");
}
int millis = sign * ((hour * 60 + min) * 60 + sec) * 1000;
return millis;
}
/*
* Convert milliseconds to RFC2445 utc-offset string
*/
private static String millisToOffset(int millis) {
StringBuffer sb = new StringBuffer(7);
if (millis >= 0) {
sb.append('+');
} else {
sb.append('-');
millis = -millis;
}
int hour, min, sec;
int t = millis / 1000;
sec = t % 60;
t = (t - sec) / 60;
min = t % 60;
hour = t / 60;
sb.append(numToString(hour, 2));
sb.append(numToString(min, 2));
sb.append(numToString(sec, 2));
return sb.toString();
}
/*
* Format integer number
*/
private static String numToString(int num, int width) {
String str = Integer.toString(num);
int len = str.length();
if (len >= width) {
return str.substring(len - width, len);
}
StringBuffer sb = new StringBuffer(width);
for (int i = len; i < width; i++) {
sb.append('0');
}
sb.append(str);
return sb.toString();
}
}
icu4j-4.2.1.1/src/com/ibm/icu/util/Measure.java 0000644 0001750 0001750 00000006515 11157614653 020766 0 ustar twerner twerner /*
**********************************************************************
* Copyright (c) 2004-2009, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
* Created: April 20, 2004
* Since: ICU 3.0
**********************************************************************
*/
package com.ibm.icu.util;
import java.lang.Number;
/**
* An amount of a specified unit, consisting of a Number and a Unit.
* For example, a length measure consists of a Number and a length
* unit, such as feet or meters. This is an abstract class.
* Subclasses specify a concrete Unit type.
*
* Measure objects are parsed and formatted by subclasses of * MeasureFormat. * *
Measure objects are immutable. * * @see java.lang.Number * @see com.ibm.icu.util.MeasureUnit * @see com.ibm.icu.text.MeasureFormat * @author Alan Liu * @stable ICU 3.0 */ public abstract class Measure { private Number number; private MeasureUnit unit; /** * Constructs a new object given a number and a unit. * @param number the number * @param unit the unit * @stable ICU 3.0 */ protected Measure(Number number, MeasureUnit unit) { if (number == null || unit == null) { throw new NullPointerException(); } this.number = number; this.unit = unit; } /** * Returns true if the given object is equal to this object. * @return true if this object is equal to the given object * @stable ICU 3.0 */ public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; try { Measure m = (Measure) obj; return unit.equals(m.unit) && numbersEqual(number, m.number); } catch (ClassCastException e) { return false; } } /* * See if two numbers are identical or have the same double value. * @param a A number * @param b Another number to be compared with * @return Returns true if two numbers are identical or have the same double value. */ // TODO improve this to catch more cases (two different longs that have same double values, BigDecimals, etc) private static boolean numbersEqual(Number a, Number b) { if (a.equals(b)) { return true; } if (a.doubleValue() == b.doubleValue()) { return true; } return false; } /** * Returns a hashcode for this object. * @return a 32-bit hash * @stable ICU 3.0 */ public int hashCode() { return number.hashCode() ^ unit.hashCode(); } /** * Returns a string representation of this object. * @return a string representation consisting of the ISO currency * code together with the numeric amount * @stable ICU 3.0 */ public String toString() { return number.toString() + ' ' + unit.toString(); } /** * Returns the numeric value of this object. * @return this object's Number * @stable ICU 3.0 */ public Number getNumber() { return number; } /** * Returns the unit of this object. * @return this object's Unit * @stable ICU 3.0 */ public MeasureUnit getUnit() { return unit; } } icu4j-4.2.1.1/src/com/ibm/icu/util/UResourceTypeMismatchException.java 0000644 0001750 0001750 00000001630 10501435666 025476 0 ustar twerner twerner /* ****************************************************************************** * Copyright (C) 2004-2006, International Business Machines Corporation and * * others. All Rights Reserved. * ****************************************************************************** */ package com.ibm.icu.util; /** * Exception thrown when the requested resource type * is not the same type as the available resource * @author ram * @stable ICU 3.0 */ public class UResourceTypeMismatchException extends RuntimeException { // Generated by serialver from JDK 1.4.1_01 static final long serialVersionUID = 1286569061095434541L; /** * Constuct the exception with the given message * @param msg the error message for this exception * @stable ICU 3.0 */ public UResourceTypeMismatchException(String msg){ super(msg); } } icu4j-4.2.1.1/src/com/ibm/icu/util/CopticCalendar.java 0000644 0001750 0001750 00000024134 11015072544 022223 0 ustar twerner twerner /* ******************************************************************************* * Copyright (C) 2005-2008, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ package com.ibm.icu.util; import java.util.Date; import java.util.Locale; /** * Implement the Coptic calendar system. *
* CopticCalendar usually should be instantiated using
* {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale
* with the tag "@calendar=coptic"
.
CopticCalendar
using the current time
* in the default time zone with the default locale.
* @stable ICU 3.4
*/
public CopticCalendar() {
super();
}
/**
* Constructs a CopticCalendar
based on the current time
* in the given time zone with the default locale.
*
* @param zone The time zone for the new calendar.
* @stable ICU 3.4
*/
public CopticCalendar(TimeZone zone) {
super(zone);
}
/**
* Constructs a CopticCalendar
based on the current time
* in the default time zone with the given locale.
*
* @param aLocale The locale for the new calendar.
* @stable ICU 3.4
*/
public CopticCalendar(Locale aLocale) {
super(aLocale);
}
/**
* Constructs a CopticCalendar
based on the current time
* in the default time zone with the given locale.
*
* @param locale The icu locale for the new calendar.
* @stable ICU 3.4
*/
public CopticCalendar(ULocale locale) {
super(locale);
}
/**
* Constructs a CopticCalendar
based on the current time
* in the given time zone with the given locale.
*
* @param zone The time zone for the new calendar.
* @param aLocale The locale for the new calendar.
* @stable ICU 3.4
*/
public CopticCalendar(TimeZone zone, Locale aLocale) {
super(zone, aLocale);
}
/**
* Constructs a CopticCalendar
based on the current time
* in the given time zone with the given locale.
*
* @param zone The time zone for the new calendar.
* @param locale The icu locale for the new calendar.
* @stable ICU 3.4
*/
public CopticCalendar(TimeZone zone, ULocale locale) {
super(zone, locale);
}
/**
* Constructs a CopticCalendar
with the given date set
* in the default time zone with the default locale.
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
* @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
* The value is 0-based. e.g., 0 for Tout.
* @param date The value used to set the calendar's {@link #DATE DATE} time field.
* @stable ICU 3.4
*/
public CopticCalendar(int year, int month, int date) {
super(year, month, date);
}
/**
* Constructs a CopticCalendar
with the given date set
* in the default time zone with the default locale.
*
* @param date The date to which the new calendar is set.
* @stable ICU 3.4
*/
public CopticCalendar(Date date) {
super(date);
}
/**
* Constructs a CopticCalendar
with the given date
* and time set for the default time zone with the default locale.
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} time field.
* @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
* The value is 0-based. e.g., 0 for Tout.
* @param date The value used to set the calendar's {@link #DATE DATE} time field.
* @param hour The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
* @param minute The value used to set the calendar's {@link #MINUTE MINUTE} time field.
* @param second The value used to set the calendar's {@link #SECOND SECOND} time field.
* @stable ICU 3.4
*/
public CopticCalendar(int year, int month, int date, int hour,
int minute, int second) {
super(year, month, date, hour, minute, second);
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public String getType() {
return "coptic";
}
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
*/
protected int handleGetExtendedYear() {
int eyear;
if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR) {
eyear = internalGet(EXTENDED_YEAR, 1); // Default to year 1
} else {
// The year defaults to the epoch start, the era to AD
int era = internalGet(ERA, CE);
if (era == BCE) {
eyear = 1 - internalGet(YEAR, 1); // Convert to extended year
} else {
eyear = internalGet(YEAR, 1); // Default to year 1
}
}
return eyear;
}
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
*/
protected void handleComputeFields(int julianDay) {
int era, year;
int[] fields = new int[3];
jdToCE(julianDay, getJDEpochOffset(), fields);
// fields[0] eyear
// fields[1] month
// fields[2] day
if (fields[0] <= 0) {
era = BCE;
year = 1 - fields[0];
} else {
era = CE;
year = fields[0];
}
internalSet(EXTENDED_YEAR, fields[0]);
internalSet(ERA, era);
internalSet(YEAR, year);
internalSet(MONTH, fields[1]);
internalSet(DAY_OF_MONTH, fields[2]);
internalSet(DAY_OF_YEAR, (30 * fields[1]) + fields[2]);
}
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
*/
protected int getJDEpochOffset() {
return JD_EPOCH_OFFSET;
}
/**
* Convert an Coptic year, month, and day to a Julian day.
*
* @param year the year
* @param month the month
* @param date the day
* @draft ICU 3.4 (retain)
* @provisional This API might change or be removed in a future release.
*/
// The equivalent operation can be done by public Calendar API.
// This API was accidentally marked as @draft, but we have no good
// reason to keep this. For now, we leave it as is, but may be
// removed in future. 2008-03-21 yoshito
public static int copticToJD(long year, int month, int date) {
return ceToJD(year, month, date, JD_EPOCH_OFFSET);
}
}
icu4j-4.2.1.1/src/com/ibm/icu/util/VersionInfo.java 0000644 0001750 0001750 00000036522 11335042151 021611 0 ustar twerner twerner /*
*******************************************************************************
* Copyright (C) 1996-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.util.HashMap;
/**
* Class to store version numbers of the form major.minor.milli.micro.
* @author synwee
* @stable ICU 2.6
*/
public final class VersionInfo implements Comparable
{
// public data members -------------------------------------------------
/**
* Unicode 1.0 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_1_0;
/**
* Unicode 1.0.1 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_1_0_1;
/**
* Unicode 1.1.0 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_1_1_0;
/**
* Unicode 1.1.5 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_1_1_5;
/**
* Unicode 2.0 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_2_0;
/**
* Unicode 2.1.2 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_2_1_2;
/**
* Unicode 2.1.5 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_2_1_5;
/**
* Unicode 2.1.8 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_2_1_8;
/**
* Unicode 2.1.9 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_2_1_9;
/**
* Unicode 3.0 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_3_0;
/**
* Unicode 3.0.1 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_3_0_1;
/**
* Unicode 3.1.0 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_3_1_0;
/**
* Unicode 3.1.1 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_3_1_1;
/**
* Unicode 3.2 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_3_2;
/**
* Unicode 4.0 version
* @stable ICU 2.6
*/
public static final VersionInfo UNICODE_4_0;
/**
* Unicode 4.0.1 version
* @stable ICU 3.4
*/
public static final VersionInfo UNICODE_4_0_1;
/**
* Unicode 4.1 version
* @stable ICU 3.4
*/
public static final VersionInfo UNICODE_4_1;
/**
* Unicode 5.0 version
* @stable ICU 3.4
*/
public static final VersionInfo UNICODE_5_0;
/**
* Unicode 5.1 version
* @stable ICU 4.2
*/
public static final VersionInfo UNICODE_5_1;
/**
* ICU4J current release version
* @stable ICU 2.8
*/
public static final VersionInfo ICU_VERSION;
/**
* Data version string for ICU's internal data
* @internal
* @deprecated This API is ICU internal only.
*/
public static final String ICU_DATA_VERSION = "42b";
/**
* ICU4J collator runtime version
* @stable ICU 2.8
*/
public static final VersionInfo UCOL_RUNTIME_VERSION;
/**
* ICU4J collator builder version
* @stable ICU 2.8
*/
public static final VersionInfo UCOL_BUILDER_VERSION;
/**
* ICU4J collator tailorings version
* @stable ICU 2.8
*/
public static final VersionInfo UCOL_TAILORINGS_VERSION;
// public methods ------------------------------------------------------
/**
* Returns an instance of VersionInfo with the argument version.
* @param version version String in the format of "major.minor.milli.micro"
* or "major.minor.milli" or "major.minor" or "major",
* where major, minor, milli, micro are non-negative numbers
* <= 255. If the trailing version numbers are
* not specified they are taken as 0s. E.g. Version "3.1" is
* equivalent to "3.1.0.0".
* @return an instance of VersionInfo with the argument version.
* @exception throws an IllegalArgumentException when the argument version
* is not in the right format
* @stable ICU 2.6
*/
public static VersionInfo getInstance(String version)
{
int length = version.length();
int array[] = {0, 0, 0, 0};
int count = 0;
int index = 0;
while (count < 4 && index < length) {
char c = version.charAt(index);
if (c == '.') {
count ++;
}
else {
c -= '0';
if (c < 0 || c > 9) {
throw new IllegalArgumentException(INVALID_VERSION_NUMBER_);
}
array[count] *= 10;
array[count] += c;
}
index ++;
}
if (index != length) {
throw new IllegalArgumentException(
"Invalid version number: String '" + version + "' exceeds version format");
}
for (int i = 0; i < 4; i ++) {
if (array[i] < 0 || array[i] > 255) {
throw new IllegalArgumentException(INVALID_VERSION_NUMBER_);
}
}
return getInstance(array[0], array[1], array[2], array[3]);
}
/**
* Returns an instance of VersionInfo with the argument version.
* @param major major version, non-negative number <= 255.
* @param minor minor version, non-negative number <= 255.
* @param milli milli version, non-negative number <= 255.
* @param micro micro version, non-negative number <= 255.
* @exception throws an IllegalArgumentException when either arguments are
* negative or > 255
* @stable ICU 2.6
*/
public static VersionInfo getInstance(int major, int minor, int milli,
int micro)
{
// checks if it is in the hashmap
// else
if (major < 0 || major > 255 || minor < 0 || minor > 255 ||
milli < 0 || milli > 255 || micro < 0 || micro > 255) {
throw new IllegalArgumentException(INVALID_VERSION_NUMBER_);
}
int version = getInt(major, minor, milli, micro);
Integer key = new Integer(version);
Object result = MAP_.get(key);
if (result == null) {
result = new VersionInfo(version);
MAP_.put(key, result);
}
return (VersionInfo)result;
}
/**
* Returns an instance of VersionInfo with the argument version.
* Equivalent to getInstance(major, minor, milli, 0).
* @param major major version, non-negative number <= 255.
* @param minor minor version, non-negative number <= 255.
* @param milli milli version, non-negative number <= 255.
* @exception throws an IllegalArgumentException when either arguments are
* negative or > 255
* @stable ICU 2.6
*/
public static VersionInfo getInstance(int major, int minor, int milli)
{
return getInstance(major, minor, milli, 0);
}
/**
* Returns an instance of VersionInfo with the argument version.
* Equivalent to getInstance(major, minor, 0, 0).
* @param major major version, non-negative number <= 255.
* @param minor minor version, non-negative number <= 255.
* @exception throws an IllegalArgumentException when either arguments are
* negative or > 255
* @stable ICU 2.6
*/
public static VersionInfo getInstance(int major, int minor)
{
return getInstance(major, minor, 0, 0);
}
/**
* Returns an instance of VersionInfo with the argument version.
* Equivalent to getInstance(major, 0, 0, 0).
* @param major major version, non-negative number <= 255.
* @exception throws an IllegalArgumentException when either arguments are
* negative or > 255
* @stable ICU 2.6
*/
public static VersionInfo getInstance(int major)
{
return getInstance(major, 0, 0, 0);
}
private static VersionInfo javaVersion;
/**
* @internal
* @deprecated This API is ICU internal only.
*/
public static VersionInfo javaVersion() {
if (javaVersion == null) {
String s = System.getProperty("java.version");
// clean string
// preserve only digits, separated by single '.'
// ignore over 4 digit sequences
// does not test < 255, very odd...
char[] chars = s.toCharArray();
int r = 0, w = 0, count = 0;
boolean numeric = false; // ignore leading non-numerics
while (r < chars.length) {
char c = chars[r++];
if (c < '0' || c > '9') {
if (numeric) {
if (count == 3) {
// only four digit strings allowed
break;
}
numeric = false;
chars[w++] = '.';
++count;
}
} else {
numeric = true;
chars[w++] = c;
}
}
while (w > 0 && chars[w-1] == '.') {
--w;
}
String vs = new String(chars, 0, w);
javaVersion = VersionInfo.getInstance(vs);
}
return javaVersion;
}
/**
* Returns the String representative of VersionInfo in the format of
* "major.minor.milli.micro"
* @return String representative of VersionInfo
* @stable ICU 2.6
*/
public String toString()
{
StringBuffer result = new StringBuffer(7);
result.append(getMajor());
result.append('.');
result.append(getMinor());
result.append('.');
result.append(getMilli());
result.append('.');
result.append(getMicro());
return result.toString();
}
/**
* Returns the major version number
* @return the major version number
* @stable ICU 2.6
*/
public int getMajor()
{
return (m_version_ >> 24) & LAST_BYTE_MASK_ ;
}
/**
* Returns the minor version number
* @return the minor version number
* @stable ICU 2.6
*/
public int getMinor()
{
return (m_version_ >> 16) & LAST_BYTE_MASK_ ;
}
/**
* Returns the milli version number
* @return the milli version number
* @stable ICU 2.6
*/
public int getMilli()
{
return (m_version_ >> 8) & LAST_BYTE_MASK_ ;
}
/**
* Returns the micro version number
* @return the micro version number
* @stable ICU 2.6
*/
public int getMicro()
{
return m_version_ & LAST_BYTE_MASK_ ;
}
/**
* Checks if this version information is equals to the argument version
* @param other object to be compared
* @return true if other is equals to this object's version information,
* false otherwise
* @stable ICU 2.6
*/
public boolean equals(Object other)
{
return other == this;
}
/**
* Compares other with this VersionInfo.
* @param other VersionInfo to be compared
* @return 0 if the argument is a VersionInfo object that has version
* information equals to this object.
* Less than 0 if the argument is a VersionInfo object that has
* version information greater than this object.
* Greater than 0 if the argument is a VersionInfo object that
* has version information less than this object.
* @stable ICU 2.6
*/
public int compareTo(VersionInfo other)
{
return m_version_ - other.m_version_;
}
/**
* Compares other with this VersionInfo.
* @param other VersionInfo to be compared. Throws ClassCastException if not a VersionInfo.
* @return 0 if the argument is a VersionInfo object that has version
* information equals to this object.
* Less than 0 if the argument is a VersionInfo object that has
* version information greater than this object.
* Greater than 0 if the argument is a VersionInfo object that
* has version information less than this object.
* @draft ICU 4.2
* @provisional This API might change or be removed in a future release.
*/
public int compareTo(Object other) {
return compareTo((VersionInfo)other);
}
// private data members ----------------------------------------------
/**
* Version number stored as a byte for each of the major, minor, milli and
* micro numbers in the 32 bit int.
* Most significant for the major and the least significant contains the
* micro numbers.
*/
private int m_version_;
/**
* Map of singletons
*/
private static final HashMap MAP_ = new HashMap();
/**
* Last byte mask
*/
private static final int LAST_BYTE_MASK_ = 0xFF;
/**
* Error statement string
*/
private static final String INVALID_VERSION_NUMBER_ =
"Invalid version number: Version number may be negative or greater than 255";
// static declaration ------------------------------------------------
/**
* Initialize versions only after MAP_ has been created
*/
static {
UNICODE_1_0 = getInstance(1, 0, 0, 0);
UNICODE_1_0_1 = getInstance(1, 0, 1, 0);
UNICODE_1_1_0 = getInstance(1, 1, 0, 0);
UNICODE_1_1_5 = getInstance(1, 1, 5, 0);
UNICODE_2_0 = getInstance(2, 0, 0, 0);
UNICODE_2_1_2 = getInstance(2, 1, 2, 0);
UNICODE_2_1_5 = getInstance(2, 1, 5, 0);
UNICODE_2_1_8 = getInstance(2, 1, 8, 0);
UNICODE_2_1_9 = getInstance(2, 1, 9, 0);
UNICODE_3_0 = getInstance(3, 0, 0, 0);
UNICODE_3_0_1 = getInstance(3, 0, 1, 0);
UNICODE_3_1_0 = getInstance(3, 1, 0, 0);
UNICODE_3_1_1 = getInstance(3, 1, 1, 0);
UNICODE_3_2 = getInstance(3, 2, 0, 0);
UNICODE_4_0 = getInstance(4, 0, 0, 0);
UNICODE_4_0_1 = getInstance(4, 0, 1, 0);
UNICODE_4_1 = getInstance(4, 1, 0, 0);
UNICODE_5_0 = getInstance(5, 0, 0, 0);
UNICODE_5_1 = getInstance(5, 1, 0, 0);
ICU_VERSION = getInstance(4, 2, 1, 1);
UCOL_RUNTIME_VERSION = getInstance(6);
UCOL_BUILDER_VERSION = getInstance(7);
UCOL_TAILORINGS_VERSION = getInstance(1);
}
// private constructor -----------------------------------------------
/**
* Constructor with int
* @param compactversion a 32 bit int with each byte representing a number
*/
private VersionInfo(int compactversion)
{
m_version_ = compactversion;
}
/**
* Gets the int from the version numbers
* @param major non-negative version number
* @param minor non-negativeversion number
* @param milli non-negativeversion number
* @param micro non-negativeversion number
*/
private static int getInt(int major, int minor, int milli, int micro)
{
return (major << 24) | (minor << 16) | (milli << 8) | micro;
}
}
icu4j-4.2.1.1/src/com/ibm/icu/util/JapaneseCalendar.java 0000644 0001750 0001750 00000060055 11157074147 022542 0 ustar twerner twerner /*
*******************************************************************************
* Copyright (C) 1996-2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import com.ibm.icu.util.TimeZone;
import java.util.Date;
import java.util.Locale;
/**
* JapaneseCalendar
is a subclass of GregorianCalendar
* that numbers years and eras based on the reigns of the Japanese emperors.
* The Japanese calendar is identical to the Gregorian calendar in all respects
* except for the year and era. The ascension of each emperor to the throne
* begins a new era, and the years of that era are numbered starting with the
* year of ascension as year 1.
* * Note that in the year of an imperial ascension, there are two possible sets * of year and era values: that for the old era and for the new. For example, a * new era began on January 7, 1989 AD. Strictly speaking, the first six days * of that year were in the Showa era, e.g. "January 6, 64 Showa", while the rest * of the year was in the Heisei era, e.g. "January 7, 1 Heisei". This class * handles this distinction correctly when computing dates. However, in lenient * mode either form of date is acceptable as input. *
* In modern times, eras have started on January 8, 1868 AD, Gregorian (Meiji),
* July 30, 1912 (Taisho), December 25, 1926 (Showa), and January 7, 1989 (Heisei). Constants
* for these eras, suitable for use in the ERA
field, are provided
* in this class. Note that the number used for each era is more or
* less arbitrary. Currently, the era starting in 1053 AD is era #0; however this
* may change in the future as we add more historical data. Use the predefined
* constants rather than using actual, absolute numbers.
*
* This class should not be subclassed.
*
* JapaneseCalendar usually should be instantiated using
* {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a ULocale
* with the tag "@calendar=japanese"
.
JapaneseCalendar
using the current time
* in the default time zone with the default locale.
* @stable ICU 2.8
*/
public JapaneseCalendar() {
super();
}
/**
* Constructs a JapaneseCalendar
based on the current time
* in the given time zone with the default locale.
* @param zone the given time zone.
* @stable ICU 2.8
*/
public JapaneseCalendar(TimeZone zone) {
super(zone);
}
/**
* Constructs a JapaneseCalendar
based on the current time
* in the default time zone with the given locale.
* @param aLocale the given locale.
* @stable ICU 2.8
*/
public JapaneseCalendar(Locale aLocale) {
super(aLocale);
}
/**
* Constructs a JapaneseCalendar
based on the current time
* in the default time zone with the given locale.
* @param locale the given ulocale.
* @stable ICU 3.2
*/
public JapaneseCalendar(ULocale locale) {
super(locale);
}
/**
* Constructs a JapaneseCalendar
based on the current time
* in the given time zone with the given locale.
*
* @param zone the given time zone.
*
* @param aLocale the given locale.
* @stable ICU 2.8
*/
public JapaneseCalendar(TimeZone zone, Locale aLocale) {
super(zone, aLocale);
}
/**
* Constructs a JapaneseCalendar
based on the current time
* in the given time zone with the given locale.
*
* @param zone the given time zone.
*
* @param locale the given ulocale.
* @stable ICU 3.2
*/
public JapaneseCalendar(TimeZone zone, ULocale locale) {
super(zone, locale);
}
/**
* Constructs a JapaneseCalendar
with the given date set
* in the default time zone with the default locale.
*
* @param date The date to which the new calendar is set.
* @stable ICU 2.8
*/
public JapaneseCalendar(Date date) {
this();
setTime(date);
}
/**
* Constructs a JapaneseCalendar
with the given date set
* in the default time zone with the default locale.
*
* @param era The imperial era used to set the calendar's {@link #ERA ERA} field.
* Eras are numbered starting with the Tenki era, which
* began in 1053 AD Gregorian, as era zero. Recent
* eras can be specified using the constants
* {@link #MEIJI} (which started in 1868 AD),
* {@link #TAISHO} (1912 AD),
* {@link #SHOWA} (1926 AD), and
* {@link #HEISEI} (1989 AD).
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} field,
* in terms of the era.
*
* @param month The value used to set the calendar's {@link #MONTH MONTH} field.
* The value is 0-based. e.g., 0 for January.
*
* @param date The value used to set the calendar's DATE field.
* @stable ICU 2.8
*/
public JapaneseCalendar(int era, int year, int month, int date) {
super(year, month, date);
set(ERA, era);
}
/**
* Constructs a JapaneseCalendar
with the given date set
* in the default time zone with the default locale.
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} field,
* in the era Heisei, the most current at the time this
* class was last updated.
*
* @param month The value used to set the calendar's {@link #MONTH MONTH} field.
* The value is 0-based. e.g., 0 for January.
*
* @param date The value used to set the calendar's {@link #DATE DATE} field.
* @stable ICU 2.8
*/
public JapaneseCalendar(int year, int month, int date) {
super(year, month, date);
set(ERA, CURRENT_ERA);
}
/**
* Constructs a JapaneseCalendar
with the given date
* and time set for the default time zone with the default locale.
*
* @param year The value used to set the calendar's {@link #YEAR YEAR} time field,
* in the era Heisei, the most current at the time of this
* writing.
*
* @param month The value used to set the calendar's {@link #MONTH MONTH} time field.
* The value is 0-based. e.g., 0 for January.
*
* @param date The value used to set the calendar's {@link #DATE DATE} time field.
*
* @param hour The value used to set the calendar's {@link #HOUR_OF_DAY HOUR_OF_DAY} time field.
*
* @param minute The value used to set the calendar's {@link #MINUTE MINUTE} time field.
*
* @param second The value used to set the calendar's {@link #SECOND SECOND} time field.
* @stable ICU 2.8
*/
public JapaneseCalendar(int year, int month, int date, int hour,
int minute, int second)
{
super(year, month, date, hour, minute, second);
set(ERA, CURRENT_ERA);
}
//-------------------------------------------------------------------------
// Use 1970 as the default value of EXTENDED_YEAR
private static final int GREGORIAN_EPOCH = 1970;
/**
* @stable ICU 2.8
*/
protected int handleGetExtendedYear() {
// EXTENDED_YEAR in JapaneseCalendar is a Gregorian year
// The default value of EXTENDED_YEAR is 1970 (Showa 45)
int year;
if (newerField(EXTENDED_YEAR, YEAR) == EXTENDED_YEAR &&
newerField(EXTENDED_YEAR, ERA) == EXTENDED_YEAR) {
year = internalGet(EXTENDED_YEAR, GREGORIAN_EPOCH);
} else {
// extended year is a gregorian year, where 1 = 1AD, 0 = 1BC, -1 = 2BC, etc
year = internalGet(YEAR, 1) // pin to minimum of year 1 (first year)
+ ERAS[internalGet(ERA, CURRENT_ERA) * 3] // add gregorian starting year
- 1; // Subtract one because year starts at 1
}
return year;
}
/**
* Called by handleComputeJulianDay. Returns the default month (0-based) for the year,
* taking year and era into account. Defaults to 0 (JANUARY) for Gregorian.
* @param extendedYear the extendedYear, as returned by handleGetExtendedYear
* @return the default month
* @provisional ICU 3.6
* @draft ICU 3.6 (retain)
* @see #MONTH
*/
protected int getDefaultMonthInYear(int extendedYear)
{
int era = internalGet(ERA, CURRENT_ERA);
//computeFields(status); // No need to compute fields here - expect the caller already did so.
// Find out if we are at the edge of an era
if(extendedYear == ERAS[era*3]) {
return ERAS[(era*3)+1] // month..
-1; // return 0-based month
} else {
return super.getDefaultMonthInYear(extendedYear);
}
}
/**
* Called by handleComputeJulianDay. Returns the default day (1-based) for the month,
* taking currently-set year and era into account. Defaults to 1 for Gregorian.
* @param extendedYear the extendedYear, as returned by handleGetExtendedYear
* @param month the month, as returned by getDefaultMonthInYear
* @return the default day of the month
* @draft ICU 3.6 (retain)
* @provisional ICU 3.6
* @see #DAY_OF_MONTH
*/
protected int getDefaultDayInMonth(int extendedYear, int month) {
int era = internalGet(ERA, CURRENT_ERA);
if(extendedYear == ERAS[era*3]) { // if it is year 1..
if(month == ((ERAS[(era*3)+1])-1)) { // if it is the emperor's first month..
return ERAS[(era*3)+2]; // return the D_O_M of acession
}
}
return super.getDefaultDayInMonth(extendedYear, month);
}
/**
* @stable ICU 2.8
*/
protected void handleComputeFields(int julianDay) {
super.handleComputeFields(julianDay);
int year = internalGet(EXTENDED_YEAR);
int low = 0;
// Short circuit for recent years. Most modern computations will
// occur in the current era and won't require the binary search.
// Note that if the year is == the current era year, then we use
// the binary search to handle the month/dom comparison.
if (year > ERAS[ERAS.length - 3]) {
low = CURRENT_ERA;
} else {
// Binary search
int high = ERAS.length / 3;
while (low < high - 1) {
int i = (low + high) / 2;
int diff = year - ERAS[i*3];
// If years are the same, then compare the months, and if those
// are the same, compare days of month. In the ERAS array
// months are 1-based for easier maintenance.
if (diff == 0) {
diff = internalGet(MONTH) - (ERAS[i*3 + 1] - 1);
if (diff == 0) {
diff = internalGet(DAY_OF_MONTH) - ERAS[i*3 + 2];
}
}
if (diff >= 0) {
low = i;
} else {
high = i;
}
}
}
// Now we've found the last era that starts before this date, so
// adjust the year to count from the start of that era. Note that
// all dates before the first era will fall into the first era by
// the algorithm.
internalSet(ERA, low);
internalSet(YEAR, year - ERAS[low*3] + 1);
}
private static final int[] ERAS = {
// Gregorian date of each emperor's ascension
// Years are AD, months are 1-based.
// Year Month Day
645, 6, 19, // Taika
650, 2, 15, // Hakuchi
672, 1, 1, // Hakuho
686, 7, 20, // Shucho
701, 3, 21, // Taiho
704, 5, 10, // Keiun
708, 1, 11, // Wado
715, 9, 2, // Reiki
717, 11, 17, // Yoro
724, 2, 4, // Jinki
729, 8, 5, // Tempyo
749, 4, 14, // Tempyo-kampo
749, 7, 2, // Tempyo-shoho
757, 8, 18, // Tempyo-hoji
765, 1, 7, // Tempho-jingo
767, 8, 16, // Jingo-keiun
770, 10, 1, // Hoki
781, 1, 1, // Ten-o
782, 8, 19, // Enryaku
806, 5, 18, // Daido
810, 9, 19, // Konin
824, 1, 5, // Tencho
834, 1, 3, // Showa
848, 6, 13, // Kajo
851, 4, 28, // Ninju
854, 11, 30, // Saiko
857, 2, 21, // Tennan
859, 4, 15, // Jogan
877, 4, 16, // Genkei
885, 2, 21, // Ninna
889, 4, 27, // Kampyo
898, 4, 26, // Shotai
901, 7, 15, // Engi
923, 4, 11, // Encho
931, 4, 26, // Shohei
938, 5, 22, // Tengyo
947, 4, 22, // Tenryaku
957, 10, 27, // Tentoku
961, 2, 16, // Owa
964, 7, 10, // Koho
968, 8, 13, // Anna
970, 3, 25, // Tenroku
973, 12, 20, // Ten-en
976, 7, 13, // Jogen
978, 11, 29, // Tengen
983, 4, 15, // Eikan
985, 4, 27, // Kanna
987, 4, 5, // Ei-en
989, 8, 8, // Eiso
990, 11, 7, // Shoryaku
995, 2, 22, // Chotoku
999, 1, 13, // Choho
1004, 7, 20, // Kanko
1012, 12, 25, // Chowa
1017, 4, 23, // Kannin
1021, 2, 2, // Jian
1024, 7, 13, // Manju
1028, 7, 25, // Chogen
1037, 4, 21, // Choryaku
1040, 11, 10, // Chokyu
1044, 11, 24, // Kantoku
1046, 4, 14, // Eisho
1053, 1, 11, // Tengi
1058, 8, 29, // Kohei
1065, 8, 2, // Jiryaku
1069, 4, 13, // Enkyu
1074, 8, 23, // Shoho
1077, 11, 17, // Shoryaku
1081, 2, 10, // Eiho
1084, 2, 7, // Otoku
1087, 4, 7, // Kanji
1094, 12, 15, // Kaho
1096, 12, 17, // Eicho
1097, 11, 21, // Shotoku
1099, 8, 28, // Kowa
1104, 2, 10, // Choji
1106, 4, 9, // Kasho
1108, 8, 3, // Tennin
1110, 7, 13, // Ten-ei
1113, 7, 13, // Eikyu
1118, 4, 3, // Gen-ei
1120, 4, 10, // Hoan
1124, 4, 3, // Tenji
1126, 1, 22, // Daiji
1131, 1, 29, // Tensho
1132, 8, 11, // Chosho
1135, 4, 27, // Hoen
1141, 7, 10, // Eiji
1142, 4, 28, // Koji
1144, 2, 23, // Tenyo
1145, 7, 22, // Kyuan
1151, 1, 26, // Ninpei
1154, 10, 28, // Kyuju
1156, 4, 27, // Hogen
1159, 4, 20, // Heiji
1160, 1, 10, // Eiryaku
1161, 9, 4, // Oho
1163, 3, 29, // Chokan
1165, 6, 5, // Eiman
1166, 8, 27, // Nin-an
1169, 4, 8, // Kao
1171, 4, 21, // Shoan
1175, 7, 28, // Angen
1177, 8, 4, // Jisho
1181, 7, 14, // Yowa
1182, 5, 27, // Juei
1184, 4, 16, // Genryuku
1185, 8, 14, // Bunji
1190, 4, 11, // Kenkyu
1199, 4, 27, // Shoji
1201, 2, 13, // Kennin
1204, 2, 20, // Genkyu
1206, 4, 27, // Ken-ei
1207, 10, 25, // Shogen
1211, 3, 9, // Kenryaku
1213, 12, 6, // Kenpo
1219, 4, 12, // Shokyu
1222, 4, 13, // Joo
1224, 11, 20, // Gennin
1225, 4, 20, // Karoku
1227, 12, 10, // Antei
1229, 3, 5, // Kanki
1232, 4, 2, // Joei
1233, 4, 15, // Tempuku
1234, 11, 5, // Bunryaku
1235, 9, 19, // Katei
1238, 11, 23, // Ryakunin
1239, 2, 7, // En-o
1240, 7, 16, // Ninji
1243, 2, 26, // Kangen
1247, 2, 28, // Hoji
1249, 3, 18, // Kencho
1256, 10, 5, // Kogen
1257, 3, 14, // Shoka
1259, 3, 26, // Shogen
1260, 4, 13, // Bun-o
1261, 2, 20, // Kocho
1264, 2, 28, // Bun-ei
1275, 4, 25, // Kenji
1278, 2, 29, // Koan
1288, 4, 28, // Shoo
1293, 8, 55, // Einin
1299, 4, 25, // Shoan
1302, 11, 21, // Kengen
1303, 8, 5, // Kagen
1306, 12, 14, // Tokuji
1308, 10, 9, // Enkei
1311, 4, 28, // Ocho
1312, 3, 20, // Showa
1317, 2, 3, // Bunpo
1319, 4, 28, // Geno
1321, 2, 23, // Genkyo
1324, 12, 9, // Shochu
1326, 4, 26, // Kareki
1329, 8, 29, // Gentoku
1331, 8, 9, // Genko
1334, 1, 29, // Kemmu
1336, 2, 29, // Engen
1340, 4, 28, // Kokoku
1346, 12, 8, // Shohei
1370, 7, 24, // Kentoku
1372, 4, 1, // Bunch\u0169
1375, 5, 27, // Tenju
1379, 3, 22, // Koryaku
1381, 2, 10, // Kowa
1384, 4, 28, // Gench\u0169
1384, 2, 27, // Meitoku
1387, 8, 23, // Kakei
1389, 2, 9, // Koo
1390, 3, 26, // Meitoku
1394, 7, 5, // Oei
1428, 4, 27, // Shocho
1429, 9, 5, // Eikyo
1441, 2, 17, // Kakitsu
1444, 2, 5, // Bun-an
1449, 7, 28, // Hotoku
1452, 7, 25, // Kyotoku
1455, 7, 25, // Kosho
1457, 9, 28, // Choroku
1460, 12, 21, // Kansho
1466, 2, 28, // Bunsho
1467, 3, 3, // Onin
1469, 4, 28, // Bunmei
1487, 7, 29, // Chokyo
1489, 8, 21, // Entoku
1492, 7, 19, // Meio
1501, 2, 29, // Bunki
1504, 2, 30, // Eisho
1521, 8, 23, // Taiei
1528, 8, 20, // Kyoroku
1532, 7, 29, // Tenmon
1555, 10, 23, // Koji
1558, 2, 28, // Eiroku
1570, 4, 23, // Genki
1573, 7, 28, // Tensho
1592, 12, 8, // Bunroku
1596, 10, 27, // Keicho
1615, 7, 13, // Genwa
1624, 2, 30, // Kan-ei
1644, 12, 16, // Shoho
1648, 2, 15, // Keian
1652, 9, 18, // Shoo
1655, 4, 13, // Meiryaku
1658, 7, 23, // Manji
1661, 4, 25, // Kanbun
1673, 9, 21, // Enpo
1681, 9, 29, // Tenwa
1684, 2, 21, // Jokyo
1688, 9, 30, // Genroku
1704, 3, 13, // Hoei
1711, 4, 25, // Shotoku
1716, 6, 22, // Kyoho
1736, 4, 28, // Genbun
1741, 2, 27, // Kanpo
1744, 2, 21, // Enkyo
1748, 7, 12, // Kan-en
1751, 10, 27, // Horyaku
1764, 6, 2, // Meiwa
1772, 11, 16, // An-ei
1781, 4, 2, // Tenmei
1789, 1, 25, // Kansei
1801, 2, 5, // Kyowa
1804, 2, 11, // Bunka
1818, 4, 22, // Bunsei
1830, 12, 10, // Tenpo
1844, 12, 2, // Koka
1848, 2, 28, // Kaei
1854, 11, 27, // Ansei
1860, 3, 18, // Man-en
1861, 2, 19, // Bunkyu
1864, 2, 20, // Genji
1865, 4, 7, // Keio
1868, 9, 8, // Meiji
1912, 7, 30, // Taisho
1926, 12, 25, // Showa
1989, 1, 8, // Heisei
};
//-------------------------------------------------------------------------
// Public constants for some of the recent eras that folks might use...
//-------------------------------------------------------------------------
// Constant for the current era. This must be regularly updated.
/**
* @stable ICU 2.8
*/
static public final int CURRENT_ERA = (ERAS.length / 3) - 1;
/**
* Constant for the era starting on Sept. 8, 1868 AD.
* @stable ICU 2.8
*/
static public final int MEIJI = CURRENT_ERA - 3;
/**
* Constant for the era starting on July 30, 1912 AD.
* @stable ICU 2.8
*/
static public final int TAISHO = CURRENT_ERA - 2;
/**
* Constant for the era starting on Dec. 25, 1926 AD.
* @stable ICU 2.8
*/
static public final int SHOWA = CURRENT_ERA - 1;
/**
* Constant for the era starting on Jan. 7, 1989 AD.
* @stable ICU 2.8
*/
static public final int HEISEI = CURRENT_ERA;
/**
* Override GregorianCalendar. We should really handle YEAR_WOY and
* EXTENDED_YEAR here too to implement the 1..5000000 range, but it's
* not critical.
* @stable ICU 2.8
*/
protected int handleGetLimit(int field, int limitType) {
switch (field) {
case ERA:
if (limitType == MINIMUM || limitType == GREATEST_MINIMUM) {
return 1;
}
return CURRENT_ERA;
case YEAR:
{
switch (limitType) {
case MINIMUM:
case GREATEST_MINIMUM:
return 1;
case LEAST_MAXIMUM:
return 1;
case MAXIMUM:
return super.handleGetLimit(field, MAXIMUM) - ERAS[CURRENT_ERA*3];
}
}
default:
return super.handleGetLimit(field, limitType);
}
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public String getType() {
return "japanese";
}
/**
* {@inheritDoc}
* @stable ICU 4.0
*/
public int getActualMaximum(int field) {
if (field == YEAR) {
int era = get(Calendar.ERA);
if (era == CURRENT_ERA) {
// TODO: Investigate what value should be used here - revisit after 4.0.
return handleGetLimit(YEAR, MAXIMUM);
} else {
int nextEraYear = ERAS[(era+1)*3];
int nextEraMonth = ERAS[(era+1)*3 + 1];
int nextEraDate = ERAS[(era+1)*3 + 2];
int maxYear = nextEraYear - ERAS[era*3] + 1; // 1-base
if (nextEraMonth == 1 && nextEraDate == 1) {
// Substract 1, because the next era starts at Jan 1
maxYear--;
}
return maxYear;
}
}
return super.getActualMaximum(field);
}
}
icu4j-4.2.1.1/src/com/ibm/icu/util/IllformedLocaleException.java 0000644 0001750 0001750 00000003547 11156630077 024300 0 ustar twerner twerner /*
*******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
/**
* Thrown by methods in Locale class to indicate that a locale string
* is illformed.
*
* @draft ICU 4.2
* @provisional This API might change or be removed in a future release.
*/
public class IllformedLocaleException extends IllegalArgumentException {
private static final long serialVersionUID = 1L;
private int _errIdx = -1;
/**
* Constructs a new IllformedLocaleException
with
* the detail message.
* @param msg the detail message
* @draft ICU 4.2
* @provisional This API might change or be removed in a future release.
*/
public IllformedLocaleException(String msg) {
this(msg, -1);
}
/**
* Constructs a new IllformedLocaleException
with
* the detail message and the error index.
* @param msg the detail message
* @param errIdx the index where the error is found in a locale string
* @draft ICU 4.2
* @provisional This API might change or be removed in a future release.
*/
public IllformedLocaleException(String msg, int errIdx) {
super(msg + ((errIdx == -1) ? "" : " [at index " + errIdx + "]"));
_errIdx = errIdx;
}
/**
* Returns the index where the error is found in a locale string
* @return the index where the error is found in a locale string or
* -1 if unknown.
* @draft ICU 4.2
* @provisional This API might change or be removed in a future release.
*/
public int getErrorIndex() {
return _errIdx;
}
}
icu4j-4.2.1.1/src/com/ibm/icu/util/EasterHoliday.java 0000644 0001750 0001750 00000024124 10666355147 022122 0 ustar twerner twerner /*
*******************************************************************************
* Copyright (C) 1996-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.util.Date;
/**
* A Holiday subclass which represents holidays that occur
* a fixed number of days before or after Easter. Supports both the
* Western and Orthodox methods for calculating Easter.
* @draft ICU 2.8 (retainAll)
* @provisional This API might change or be removed in a future release.
*/
public class EasterHoliday extends Holiday
{
/**
* Construct a holiday that falls on Easter Sunday every year
*
* @param name The name of the holiday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public EasterHoliday(String name)
{
super(name, new EasterRule(0, false));
}
/**
* Construct a holiday that falls a specified number of days before
* or after Easter Sunday each year.
*
* @param daysAfter The number of days before (-) or after (+) Easter
* @param name The name of the holiday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public EasterHoliday(int daysAfter, String name)
{
super(name, new EasterRule(daysAfter, false));
}
/**
* Construct a holiday that falls a specified number of days before
* or after Easter Sunday each year, using either the Western
* or Orthodox calendar.
*
* @param daysAfter The number of days before (-) or after (+) Easter
* @param orthodox Use the Orthodox calendar?
* @param name The name of the holiday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
public EasterHoliday(int daysAfter, boolean orthodox, String name)
{
super(name, new EasterRule(daysAfter, orthodox));
}
/**
* Shrove Tuesday, aka Mardi Gras, 48 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday SHROVE_TUESDAY = new EasterHoliday(-48, "Shrove Tuesday");
/**
* Ash Wednesday, start of Lent, 47 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday ASH_WEDNESDAY = new EasterHoliday(-47, "Ash Wednesday");
/**
* Palm Sunday, 7 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday PALM_SUNDAY = new EasterHoliday( -7, "Palm Sunday");
/**
* Maundy Thursday, 3 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday MAUNDY_THURSDAY = new EasterHoliday( -3, "Maundy Thursday");
/**
* Good Friday, 2 days before Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday GOOD_FRIDAY = new EasterHoliday( -2, "Good Friday");
/**
* Easter Sunday
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday EASTER_SUNDAY = new EasterHoliday( 0, "Easter Sunday");
/**
* Easter Monday, 1 day after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday EASTER_MONDAY = new EasterHoliday( 1, "Easter Monday");
/**
* Ascension, 39 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday ASCENSION = new EasterHoliday( 39, "Ascension");
/**
* Pentecost (aka Whit Sunday), 49 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday PENTECOST = new EasterHoliday( 49, "Pentecost");
/**
* Whit Sunday (aka Pentecost), 49 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday WHIT_SUNDAY = new EasterHoliday( 49, "Whit Sunday");
/**
* Whit Monday, 50 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday WHIT_MONDAY = new EasterHoliday( 50, "Whit Monday");
/**
* Corpus Christi, 60 days after Easter
* @draft ICU 2.8
* @provisional This API might change or be removed in a future release.
*/
static public final EasterHoliday CORPUS_CHRISTI = new EasterHoliday( 60, "Corpus Christi");
}
class EasterRule implements DateRule {
public EasterRule(int daysAfterEaster, boolean isOrthodox) {
this.daysAfterEaster = daysAfterEaster;
if (isOrthodox) {
orthodox.setGregorianChange(new Date(Long.MAX_VALUE));
calendar = orthodox;
}
}
/**
* Return the first occurrance of this rule on or after the given date
*/
public Date firstAfter(Date start)
{
return doFirstBetween(start, null);
}
/**
* Return the first occurrance of this rule on or after
* the given start date and before the given end date.
*/
public Date firstBetween(Date start, Date end)
{
return doFirstBetween(start, end);
}
/**
* Return true if the given Date is on the same day as Easter
*/
public boolean isOn(Date date)
{
synchronized(calendar) {
calendar.setTime(date);
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
calendar.setTime(computeInYear(calendar.getTime(), calendar));
return calendar.get(Calendar.DAY_OF_YEAR) == dayOfYear;
}
}
/**
* Return true if Easter occurs between the two dates given
*/
public boolean isBetween(Date start, Date end)
{
return firstBetween(start, end) != null; // TODO: optimize?
}
private Date doFirstBetween(Date start, Date end)
{
//System.out.println("doFirstBetween: start = " + start.toString());
//System.out.println("doFirstBetween: end = " + end.toString());
synchronized(calendar) {
// Figure out when this holiday lands in the given year
Date result = computeInYear(start, calendar);
//System.out.println(" result = " + result.toString());
// We might have gotten a date that's in the same year as "start", but
// earlier in the year. If so, go to next year
if (result.before(start))
{
calendar.setTime(start);
calendar.get(Calendar.YEAR); // JDK 1.1.2 bug workaround
calendar.add(Calendar.YEAR, 1);
//System.out.println(" Result before start, going to next year: "
// + calendar.getTime().toString());
result = computeInYear(calendar.getTime(), calendar);
//System.out.println(" result = " + result.toString());
}
if (end != null && result.after(end)) {
//System.out.println("Result after end, returning null");
return null;
}
return result;
}
}
/**
* Compute the month and date on which this holiday falls in the year
* containing the date "date". First figure out which date Easter
* lands on in this year, and then add the offset for this holiday to get
* the right date.
* * The algorithm here is taken from the * Calendar FAQ. */ private Date computeInYear(Date date, GregorianCalendar cal) { if (cal == null) cal = calendar; synchronized(cal) { cal.setTime(date); int year = cal.get(Calendar.YEAR); int g = year % 19; // "Golden Number" of year - 1 int i = 0; // # of days from 3/21 to the Paschal full moon int j = 0; // Weekday (0-based) of Paschal full moon if (cal.getTime().after( cal.getGregorianChange())) { // We're past the Gregorian switchover, so use the Gregorian rules. int c = year / 100; int h = (c - c/4 - (8*c+13)/25 + 19*g + 15) % 30; i = h - (h/28)*(1 - (h/28)*(29/(h+1))*((21-g)/11)); j = (year + year/4 + i + 2 - c + c/4) % 7; } else { // Use the old Julian rules. i = (19*g + 15) % 30; j = (year + year/4 + i) % 7; } int l = i - j; int m = 3 + (l+40)/44; // 1-based month in which Easter falls int d = l + 28 - 31*(m/4); // Date of Easter within that month cal.clear(); cal.set(Calendar.ERA, GregorianCalendar.AD); cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, m-1); // 0-based cal.set(Calendar.DATE, d); cal.getTime(); // JDK 1.1.2 bug workaround cal.add(Calendar.DATE, daysAfterEaster); return cal.getTime(); } } private static GregorianCalendar gregorian = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */); private static GregorianCalendar orthodox = new GregorianCalendar(/* new SimpleTimeZone(0, "UTC") */); private int daysAfterEaster; private GregorianCalendar calendar = gregorian; } icu4j-4.2.1.1/src/com/ibm/icu/util/package.html 0000644 0001750 0001750 00000000432 10666355147 020777 0 ustar twerner twerner
International calendars and other utility classes.