mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1161 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1161 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			Java
		
	
	
	
/* gnu.java.util.ZoneInfo
 | 
						|
   Copyright (C) 2007 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package gnu.java.util;
 | 
						|
 | 
						|
import java.io.BufferedInputStream;
 | 
						|
import java.io.DataInputStream;
 | 
						|
import java.io.EOFException;
 | 
						|
import java.io.FileInputStream;
 | 
						|
import java.io.InputStream;
 | 
						|
import java.io.IOException;
 | 
						|
import java.util.Calendar;
 | 
						|
import java.util.Date;
 | 
						|
import java.util.GregorianCalendar;
 | 
						|
import java.util.SimpleTimeZone;
 | 
						|
import java.util.TimeZone;
 | 
						|
 | 
						|
/**
 | 
						|
 * This class represents more advanced variant of java.util.SimpleTimeZone.
 | 
						|
 * It can handle zic(8) compiled transition dates plus uses a SimpleTimeZone
 | 
						|
 * for years beyond last precomputed transition.  Before first precomputed
 | 
						|
 * transition it assumes no daylight saving was in effect.
 | 
						|
 * Timezones that never used daylight saving time should use just
 | 
						|
 * SimpleTimeZone instead of this class.
 | 
						|
 *
 | 
						|
 * This object is tightly bound to the Gregorian calendar.  It assumes
 | 
						|
 * a regular seven days week, and the month lengths are that of the
 | 
						|
 * Gregorian Calendar.
 | 
						|
 *
 | 
						|
 * @see Calendar
 | 
						|
 * @see GregorianCalendar
 | 
						|
 * @see SimpleTimeZone
 | 
						|
 * @author Jakub Jelinek
 | 
						|
 */
 | 
						|
public class ZoneInfo extends TimeZone
 | 
						|
{
 | 
						|
  private static final int SECS_SHIFT = 22;
 | 
						|
  private static final long OFFSET_MASK = (1 << 21) - 1;
 | 
						|
  private static final int OFFSET_SHIFT = 64 - 21;
 | 
						|
  private static final long IS_DST = 1 << 21;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The raw time zone offset in milliseconds to GMT, ignoring
 | 
						|
   * daylight savings.
 | 
						|
   * @serial
 | 
						|
   */
 | 
						|
  private int rawOffset;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Cached DST savings for the last transition rule.
 | 
						|
   */
 | 
						|
  private int dstSavings;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Cached flag whether last transition rule uses DST saving.
 | 
						|
   */
 | 
						|
  private boolean useDaylight;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Array of encoded transitions.
 | 
						|
   * Transition time in UTC seconds since epoch is in the most
 | 
						|
   * significant 64 - SECS_SHIFT bits, then one bit flag
 | 
						|
   * whether DST is active and the least significant bits
 | 
						|
   * containing offset relative to rawOffset.  Both the DST
 | 
						|
   * flag and relative offset apply to time before the transition
 | 
						|
   * and after or equal to previous transition if any.
 | 
						|
   * The array must be sorted.
 | 
						|
   */
 | 
						|
  private long[] transitions;
 | 
						|
 | 
						|
  /**
 | 
						|
   * SimpleTimeZone rule which applies on or after the latest
 | 
						|
   * transition.  If the DST changes are not expresible as a
 | 
						|
   * SimpleTimeZone rule, then the rule should just contain
 | 
						|
   * the standard time and no DST time.
 | 
						|
   */
 | 
						|
  private SimpleTimeZone lastRule;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Cached GMT SimpleTimeZone object for internal use in
 | 
						|
   * getOffset method.
 | 
						|
   */
 | 
						|
  private static SimpleTimeZone gmtZone = null;
 | 
						|
 | 
						|
  static final long serialVersionUID = -3740626706860383657L;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Create a <code>ZoneInfo</code> with the given time offset
 | 
						|
   * from GMT and with daylight savings.
 | 
						|
   *
 | 
						|
   * @param rawOffset The time offset from GMT in milliseconds.
 | 
						|
   * @param id  The identifier of this time zone.
 | 
						|
   * @param transitions  Array of transition times in UTC seconds since
 | 
						|
   * Epoch in topmost 42 bits, below that 1 boolean bit whether the time
 | 
						|
   * before that transition used daylight saving and in bottommost 21
 | 
						|
   * bits relative daylight saving offset against rawOffset in seconds
 | 
						|
   * that applies before this transition.
 | 
						|
   * @param endRule SimpleTimeZone class describing the daylight saving
 | 
						|
   * rules after the last transition.
 | 
						|
   */
 | 
						|
  public ZoneInfo(int rawOffset, String id, long[] transitions,
 | 
						|
                  SimpleTimeZone lastRule)
 | 
						|
  {
 | 
						|
    if (transitions == null || transitions.length < 1)
 | 
						|
      throw new IllegalArgumentException("transitions must not be null");
 | 
						|
    if (lastRule == null)
 | 
						|
      throw new IllegalArgumentException("lastRule must not be null");
 | 
						|
    this.rawOffset = rawOffset;
 | 
						|
    this.transitions = transitions;
 | 
						|
    this.lastRule = lastRule;
 | 
						|
    setID(id);
 | 
						|
    computeDSTSavings();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets the time zone offset, for current date, modified in case of
 | 
						|
   * daylight savings.  This is the offset to add to UTC to get the local
 | 
						|
   * time.
 | 
						|
   *
 | 
						|
   * The day must be a positive number and dayOfWeek must be a positive value
 | 
						|
   * from Calendar.  dayOfWeek is redundant, but must match the other values
 | 
						|
   * or an inaccurate result may be returned.
 | 
						|
   *
 | 
						|
   * @param era the era of the given date
 | 
						|
   * @param year the year of the given date
 | 
						|
   * @param month the month of the given date, 0 for January.
 | 
						|
   * @param day the day of month
 | 
						|
   * @param dayOfWeek the day of week; this must match the other fields.
 | 
						|
   * @param millis the millis in the day (in local standard time)
 | 
						|
   * @return the time zone offset in milliseconds.
 | 
						|
   * @throws IllegalArgumentException if arguments are incorrect.
 | 
						|
   */
 | 
						|
  public int getOffset(int era, int year, int month, int day, int dayOfWeek,
 | 
						|
                       int millis)
 | 
						|
  {
 | 
						|
    if (gmtZone == null)
 | 
						|
      gmtZone = new SimpleTimeZone(0, "GMT");
 | 
						|
 | 
						|
    if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
 | 
						|
      throw new IllegalArgumentException("dayOfWeek out of range");
 | 
						|
    if (month < Calendar.JANUARY || month > Calendar.DECEMBER)
 | 
						|
      throw new IllegalArgumentException("month out of range:" + month);
 | 
						|
 | 
						|
    if (era != GregorianCalendar.AD)
 | 
						|
      return (int) (((transitions[0] << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000);
 | 
						|
 | 
						|
    GregorianCalendar cal = new GregorianCalendar((TimeZone) gmtZone);
 | 
						|
    cal.set(year, month, day, 0, 0, 0);
 | 
						|
    if (cal.get(Calendar.DAY_OF_MONTH) != day)
 | 
						|
      throw new IllegalArgumentException("day out of range");
 | 
						|
 | 
						|
    return getOffset(cal.getTimeInMillis() - rawOffset + millis);
 | 
						|
  }
 | 
						|
 | 
						|
  private long findTransition(long secs)
 | 
						|
  {
 | 
						|
    if (secs < (transitions[0] >> SECS_SHIFT))
 | 
						|
      return transitions[0];
 | 
						|
 | 
						|
    if (secs >= (transitions[transitions.length-1] >> SECS_SHIFT))
 | 
						|
      return Long.MAX_VALUE;
 | 
						|
 | 
						|
    long val = (secs + 1) << SECS_SHIFT;
 | 
						|
    int lo = 1;
 | 
						|
    int hi = transitions.length;
 | 
						|
    int mid = 1;
 | 
						|
    while (lo < hi)
 | 
						|
      {
 | 
						|
        mid = (lo + hi) / 2;
 | 
						|
        // secs < (transitions[mid-1] >> SECS_SHIFT)
 | 
						|
        if (val <= transitions[mid-1])
 | 
						|
          hi = mid;
 | 
						|
        // secs >= (transitions[mid] >> SECS_SHIFT)
 | 
						|
        else if (val > transitions[mid])
 | 
						|
          lo = mid + 1;
 | 
						|
        else
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    return transitions[mid];
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Get the time zone offset for the specified date, modified in case of
 | 
						|
   * daylight savings.  This is the offset to add to UTC to get the local
 | 
						|
   * time.
 | 
						|
   * @param date the date represented in millisecends
 | 
						|
   * since January 1, 1970 00:00:00 GMT.
 | 
						|
   */
 | 
						|
  public int getOffset(long date)
 | 
						|
  {
 | 
						|
    long d = (date >= 0 ? date / 1000 : (date + 1) / 1000 - 1);
 | 
						|
    long transition = findTransition(d);
 | 
						|
 | 
						|
    // For times on or after last transition use lastRule.
 | 
						|
    if (transition == Long.MAX_VALUE)
 | 
						|
      return lastRule.getOffset(date);
 | 
						|
 | 
						|
    return (int) (((transition << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the time zone offset to GMT in milliseconds, ignoring
 | 
						|
   * day light savings.
 | 
						|
   * @return the time zone offset.
 | 
						|
   */
 | 
						|
  public int getRawOffset()
 | 
						|
  {
 | 
						|
    return rawOffset;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the standard time zone offset to GMT.
 | 
						|
   * @param rawOffset The time offset from GMT in milliseconds.
 | 
						|
   */
 | 
						|
  public void setRawOffset(int rawOffset)
 | 
						|
  {
 | 
						|
    this.rawOffset = rawOffset;
 | 
						|
    lastRule.setRawOffset(rawOffset);
 | 
						|
  }
 | 
						|
 | 
						|
  private void computeDSTSavings()
 | 
						|
  {
 | 
						|
    if (lastRule.useDaylightTime())
 | 
						|
      {
 | 
						|
        dstSavings = lastRule.getDSTSavings();
 | 
						|
        useDaylight = true;
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        dstSavings = 0;
 | 
						|
        useDaylight = false;
 | 
						|
        // lastRule might say no DST is in effect simply because
 | 
						|
        // the DST rules are too complex for SimpleTimeZone, say
 | 
						|
        // for Asia/Jerusalem.
 | 
						|
        // Look at the last DST offset if it is newer than current time.
 | 
						|
        long currentSecs = System.currentTimeMillis() / 1000;
 | 
						|
        int i;
 | 
						|
        for (i = transitions.length - 1;
 | 
						|
             i >= 0 && currentSecs < (transitions[i] >> SECS_SHIFT);
 | 
						|
             i--)
 | 
						|
          if ((transitions[i] & IS_DST) != 0)
 | 
						|
            {
 | 
						|
              dstSavings = (int) (((transitions[i] << OFFSET_SHIFT)
 | 
						|
                                   >> OFFSET_SHIFT) * 1000)
 | 
						|
                           - rawOffset;
 | 
						|
              useDaylight = true;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Gets the daylight savings offset.  This is a positive offset in
 | 
						|
   * milliseconds with respect to standard time.  Typically this
 | 
						|
   * is one hour, but for some time zones this may be half an our.
 | 
						|
   * @return the daylight savings offset in milliseconds.
 | 
						|
   */
 | 
						|
  public int getDSTSavings()
 | 
						|
  {
 | 
						|
    return dstSavings;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns if this time zone uses daylight savings time.
 | 
						|
   * @return true, if we use daylight savings time, false otherwise.
 | 
						|
   */
 | 
						|
  public boolean useDaylightTime()
 | 
						|
  {
 | 
						|
    return useDaylight;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines if the given date is in daylight savings time.
 | 
						|
   * @return true, if it is in daylight savings time, false otherwise.
 | 
						|
   */
 | 
						|
  public boolean inDaylightTime(Date date)
 | 
						|
  {
 | 
						|
    long d = date.getTime();
 | 
						|
    d = (d >= 0 ? d / 1000 : (d + 1) / 1000 - 1);
 | 
						|
    long transition = findTransition(d);
 | 
						|
 | 
						|
    // For times on or after last transition use lastRule.
 | 
						|
    if (transition == Long.MAX_VALUE)
 | 
						|
      return lastRule.inDaylightTime(date);
 | 
						|
 | 
						|
    return (transition & IS_DST) != 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Generates the hashCode for the SimpleDateFormat object.  It is
 | 
						|
   * the rawOffset, possibly, if useDaylightSavings is true, xored
 | 
						|
   * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
 | 
						|
   */
 | 
						|
  public synchronized int hashCode()
 | 
						|
  {
 | 
						|
    int hash = lastRule.hashCode();
 | 
						|
    // FIXME - hash transitions?
 | 
						|
    return hash;
 | 
						|
  }
 | 
						|
 | 
						|
  public synchronized boolean equals(Object o)
 | 
						|
  {
 | 
						|
    if (! hasSameRules((TimeZone) o))
 | 
						|
      return false;
 | 
						|
 | 
						|
    ZoneInfo zone = (ZoneInfo) o;
 | 
						|
    return getID().equals(zone.getID());
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Test if the other time zone uses the same rule and only
 | 
						|
   * possibly differs in ID.  This implementation for this particular
 | 
						|
   * class will return true if the other object is a ZoneInfo,
 | 
						|
   * the raw offsets and useDaylight are identical and if useDaylight
 | 
						|
   * is true, also the start and end datas are identical.
 | 
						|
   * @return true if this zone uses the same rule.
 | 
						|
   */
 | 
						|
  public boolean hasSameRules(TimeZone o)
 | 
						|
  {
 | 
						|
    if (this == o)
 | 
						|
      return true;
 | 
						|
    if (! (o instanceof ZoneInfo))
 | 
						|
      return false;
 | 
						|
    ZoneInfo zone = (ZoneInfo) o;
 | 
						|
    if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset)
 | 
						|
      return false;
 | 
						|
    if (! lastRule.equals(zone.lastRule))
 | 
						|
      return false;
 | 
						|
    // FIXME - compare transitions?
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a string representation of this ZoneInfo object.
 | 
						|
   * @return a string representation of this ZoneInfo object.
 | 
						|
   */
 | 
						|
  public String toString()
 | 
						|
  {
 | 
						|
    return getClass().getName() + "[" + "id=" + getID() + ",offset="
 | 
						|
           + rawOffset + ",transitions=" + transitions.length
 | 
						|
           + ",useDaylight=" + useDaylight
 | 
						|
           + (useDaylight ? (",dstSavings=" + dstSavings) : "")
 | 
						|
           + ",lastRule=" + lastRule.toString() + "]";
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Reads zic(8) compiled timezone data file from file
 | 
						|
   * and returns a TimeZone class describing it, either
 | 
						|
   * SimpleTimeZone or ZoneInfo depending on whether
 | 
						|
   * it can be described by SimpleTimeZone rule or not.
 | 
						|
   */
 | 
						|
  public static TimeZone readTZFile(String id, String file)
 | 
						|
  {
 | 
						|
    DataInputStream dis = null;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        FileInputStream fis = new FileInputStream(file);
 | 
						|
        BufferedInputStream bis = new BufferedInputStream(fis);
 | 
						|
        dis = new DataInputStream(bis);
 | 
						|
 | 
						|
        // Make sure we are reading a tzfile.
 | 
						|
        byte[] tzif = new byte[5];
 | 
						|
        dis.readFully(tzif);
 | 
						|
        int tzif2 = 4;
 | 
						|
        if (tzif[0] == 'T' && tzif[1] == 'Z'
 | 
						|
            && tzif[2] == 'i' && tzif[3] == 'f')
 | 
						|
          {
 | 
						|
            if (tzif[4] >= '2')
 | 
						|
              tzif2 = 8;
 | 
						|
            // Reserved bytes
 | 
						|
            skipFully(dis, 16 - 1);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          // Darwin has tzdata files that don't start with the TZif marker
 | 
						|
          skipFully(dis, 16 - 5);
 | 
						|
 | 
						|
        int ttisgmtcnt = dis.readInt();
 | 
						|
        int ttisstdcnt = dis.readInt();
 | 
						|
        int leapcnt = dis.readInt();
 | 
						|
        int timecnt = dis.readInt();
 | 
						|
        int typecnt = dis.readInt();
 | 
						|
        int charcnt = dis.readInt();
 | 
						|
        if (tzif2 == 8)
 | 
						|
          {
 | 
						|
            skipFully(dis, timecnt * (4 + 1) + typecnt * (4 + 1 + 1) + charcnt
 | 
						|
                           + leapcnt * (4 + 4) + ttisgmtcnt + ttisstdcnt);
 | 
						|
 | 
						|
            dis.readFully(tzif);
 | 
						|
            if (tzif[0] != 'T' || tzif[1] != 'Z' || tzif[2] != 'i'
 | 
						|
                || tzif[3] != 'f' || tzif[4] < '2')
 | 
						|
              return null;
 | 
						|
 | 
						|
            // Reserved bytes
 | 
						|
            skipFully(dis, 16 - 1);
 | 
						|
            ttisgmtcnt = dis.readInt();
 | 
						|
            ttisstdcnt = dis.readInt();
 | 
						|
            leapcnt = dis.readInt();
 | 
						|
            timecnt = dis.readInt();
 | 
						|
            typecnt = dis.readInt();
 | 
						|
            charcnt = dis.readInt();
 | 
						|
          }
 | 
						|
 | 
						|
        // Sanity checks
 | 
						|
        if (typecnt <= 0 || timecnt < 0 || charcnt < 0
 | 
						|
            || leapcnt < 0 || ttisgmtcnt < 0 || ttisstdcnt < 0
 | 
						|
            || ttisgmtcnt > typecnt || ttisstdcnt > typecnt)
 | 
						|
          return null;
 | 
						|
 | 
						|
        // Transition times
 | 
						|
        long[] times = new long[timecnt];
 | 
						|
        for (int i = 0; i < timecnt; i++)
 | 
						|
          if (tzif2 == 8)
 | 
						|
            times[i] = dis.readLong();
 | 
						|
          else
 | 
						|
            times[i] = (long) dis.readInt();
 | 
						|
 | 
						|
        // Transition types
 | 
						|
        int[] types = new int[timecnt];
 | 
						|
        for (int i = 0; i < timecnt; i++)
 | 
						|
          {
 | 
						|
            types[i] = dis.readByte();
 | 
						|
            if (types[i] < 0)
 | 
						|
              types[i] += 256;
 | 
						|
            if (types[i] >= typecnt)
 | 
						|
              return null;
 | 
						|
          }
 | 
						|
 | 
						|
        // Types
 | 
						|
        int[] offsets = new int[typecnt];
 | 
						|
        int[] typeflags = new int[typecnt];
 | 
						|
        for (int i = 0; i < typecnt; i++)
 | 
						|
          {
 | 
						|
            offsets[i] = dis.readInt();
 | 
						|
            if (offsets[i] >= IS_DST / 2 || offsets[i] <= -IS_DST / 2)
 | 
						|
              return null;
 | 
						|
            int dst = dis.readByte();
 | 
						|
            int abbrind = dis.readByte();
 | 
						|
            if (abbrind < 0)
 | 
						|
              abbrind += 256;
 | 
						|
            if (abbrind >= charcnt)
 | 
						|
              return null;
 | 
						|
            typeflags[i] = (dst != 0 ? (1 << 8) : 0) + abbrind;
 | 
						|
          }
 | 
						|
 | 
						|
        // Abbrev names
 | 
						|
        byte[] names = new byte[charcnt];
 | 
						|
        dis.readFully(names);
 | 
						|
 | 
						|
        // Leap transitions, for now ignore
 | 
						|
        skipFully(dis, leapcnt * (tzif2 + 4) + ttisstdcnt + ttisgmtcnt);
 | 
						|
 | 
						|
        // tzIf2 format has optional POSIX TZ env string
 | 
						|
        String tzstr = null;
 | 
						|
        if (tzif2 == 8 && dis.readByte() == '\n')
 | 
						|
          {
 | 
						|
            tzstr = dis.readLine();
 | 
						|
            if (tzstr.length() <= 0)
 | 
						|
              tzstr = null;
 | 
						|
          }
 | 
						|
 | 
						|
        // Get std/dst_offset and dst/non-dst time zone names.
 | 
						|
        int std_ind = -1;
 | 
						|
        int dst_ind = -1;
 | 
						|
        if (timecnt == 0)
 | 
						|
          std_ind = 0;
 | 
						|
        else
 | 
						|
          for (int i = timecnt - 1; i >= 0; i--)
 | 
						|
            {
 | 
						|
              if (std_ind == -1 && (typeflags[types[i]] & (1 << 8)) == 0)
 | 
						|
                std_ind = types[i];
 | 
						|
              else if (dst_ind == -1 && (typeflags[types[i]] & (1 << 8)) != 0)
 | 
						|
                dst_ind = types[i];
 | 
						|
              if (dst_ind != -1 && std_ind != -1)
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
        if (std_ind == -1)
 | 
						|
          return null;
 | 
						|
 | 
						|
        int j = typeflags[std_ind] & 255;
 | 
						|
        while (j < charcnt && names[j] != 0)
 | 
						|
          j++;
 | 
						|
        String std_zonename = new String(names, typeflags[std_ind] & 255,
 | 
						|
                                         j - (typeflags[std_ind] & 255),
 | 
						|
                                         "ASCII");
 | 
						|
 | 
						|
        String dst_zonename = "";
 | 
						|
        if (dst_ind != -1)
 | 
						|
          {
 | 
						|
            j = typeflags[dst_ind] & 255;
 | 
						|
            while (j < charcnt && names[j] != 0)
 | 
						|
              j++;
 | 
						|
            dst_zonename = new String(names, typeflags[dst_ind] & 255,
 | 
						|
                                      j - (typeflags[dst_ind] & 255), "ASCII");
 | 
						|
          }
 | 
						|
 | 
						|
        // Only use gmt offset when necessary.
 | 
						|
        // Also special case GMT+/- timezones.
 | 
						|
        String std_offset_string = "";
 | 
						|
        String dst_offset_string = "";
 | 
						|
        if (tzstr == null
 | 
						|
            && (dst_ind != -1
 | 
						|
                || (offsets[std_ind] != 0
 | 
						|
                    && !std_zonename.startsWith("GMT+")
 | 
						|
                    && !std_zonename.startsWith("GMT-"))))
 | 
						|
          {
 | 
						|
            std_offset_string = Integer.toString(-offsets[std_ind] / 3600);
 | 
						|
            int seconds = -offsets[std_ind] % 3600;
 | 
						|
            if (seconds != 0)
 | 
						|
              {
 | 
						|
                if (seconds < 0)
 | 
						|
                  seconds *= -1;
 | 
						|
                if (seconds < 600)
 | 
						|
                  std_offset_string += ":0" + Integer.toString(seconds / 60);
 | 
						|
                else
 | 
						|
                  std_offset_string += ":" + Integer.toString(seconds / 60);
 | 
						|
                seconds = seconds % 60;
 | 
						|
                if (seconds >= 10)
 | 
						|
                  std_offset_string += ":" + Integer.toString(seconds);
 | 
						|
                else if (seconds > 0)
 | 
						|
                  std_offset_string += ":0" + Integer.toString(seconds);
 | 
						|
              }
 | 
						|
 | 
						|
            if (dst_ind != -1 && offsets[dst_ind] != offsets[std_ind] + 3600)
 | 
						|
              {
 | 
						|
                dst_offset_string = Integer.toString(-offsets[dst_ind] / 3600);
 | 
						|
                seconds = -offsets[dst_ind] % 3600;
 | 
						|
                if (seconds != 0)
 | 
						|
                  {
 | 
						|
                    if (seconds < 0)
 | 
						|
                      seconds *= -1;
 | 
						|
                    if (seconds < 600)
 | 
						|
                      dst_offset_string
 | 
						|
                        += ":0" + Integer.toString(seconds / 60);
 | 
						|
                    else
 | 
						|
                      dst_offset_string
 | 
						|
                        += ":" + Integer.toString(seconds / 60);
 | 
						|
                    seconds = seconds % 60;
 | 
						|
                    if (seconds >= 10)
 | 
						|
                      dst_offset_string += ":" + Integer.toString(seconds);
 | 
						|
                    else if (seconds > 0)
 | 
						|
                      dst_offset_string += ":0" + Integer.toString(seconds);
 | 
						|
                  }
 | 
						|
              }
 | 
						|
          }
 | 
						|
 | 
						|
        /*
 | 
						|
         * If no tzIf2 POSIX TZ string is available and the timezone
 | 
						|
         * uses DST, try to guess the last rule by trying to make
 | 
						|
         * sense from transitions at 5 years in the future and onwards.
 | 
						|
         * tzdata actually uses only 3 forms of rules:
 | 
						|
         * fixed date within a month, e.g. change on April, 5th
 | 
						|
         * 1st weekday on or after Nth: change on Sun>=15 in April
 | 
						|
         * last weekday in a month: change on lastSun in April
 | 
						|
         */
 | 
						|
        String[] change_spec = { null, null };
 | 
						|
        if (tzstr == null && dst_ind != -1 && timecnt > 10)
 | 
						|
          {
 | 
						|
            long nowPlus5y = System.currentTimeMillis() / 1000
 | 
						|
                             + 5 * 365 * 86400;
 | 
						|
            int first;
 | 
						|
 | 
						|
            for (first = timecnt - 1; first >= 0; first--)
 | 
						|
              if (times[first] < nowPlus5y
 | 
						|
                  || (types[first] != std_ind && types[first] != dst_ind)
 | 
						|
                  || types[first] != types[timecnt - 2 + ((first ^ timecnt) & 1)])
 | 
						|
                break;
 | 
						|
            first++;
 | 
						|
 | 
						|
            if (timecnt - first >= 10 && types[timecnt - 1] != types[timecnt - 2])
 | 
						|
              {
 | 
						|
                GregorianCalendar cal
 | 
						|
                  = new GregorianCalendar(new SimpleTimeZone(0, "GMT"));
 | 
						|
 | 
						|
                int[] values = new int[2 * 11];
 | 
						|
                int i;
 | 
						|
                for (i = timecnt - 1; i >= first; i--)
 | 
						|
                  {
 | 
						|
                    int base = (i % 2) * 11;
 | 
						|
                    int offset = offsets[types[i > first ? i - 1 : i + 1]];
 | 
						|
                    cal.setTimeInMillis((times[i] + offset) * 1000);
 | 
						|
                    if (i >= timecnt - 2)
 | 
						|
                      {
 | 
						|
                        values[base + 0] = cal.get(Calendar.YEAR);
 | 
						|
                        values[base + 1] = cal.get(Calendar.MONTH);
 | 
						|
                        values[base + 2] = cal.get(Calendar.DAY_OF_MONTH);
 | 
						|
                        values[base + 3]
 | 
						|
                          = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
 | 
						|
                        values[base + 4] = cal.get(Calendar.DAY_OF_WEEK);
 | 
						|
                        values[base + 5] = cal.get(Calendar.HOUR_OF_DAY);
 | 
						|
                        values[base + 6] = cal.get(Calendar.MINUTE);
 | 
						|
                        values[base + 7] = cal.get(Calendar.SECOND);
 | 
						|
                        values[base + 8] = values[base + 2]; // Range start
 | 
						|
                        values[base + 9] = values[base + 2]; // Range end
 | 
						|
                        values[base + 10] = 0; // Determined type
 | 
						|
                      }
 | 
						|
                    else
 | 
						|
                      {
 | 
						|
                        int year = cal.get(Calendar.YEAR);
 | 
						|
                        int month = cal.get(Calendar.MONTH);
 | 
						|
                        int day_of_month = cal.get(Calendar.DAY_OF_MONTH);
 | 
						|
                        int month_days
 | 
						|
                          = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
 | 
						|
                        int day_of_week = cal.get(Calendar.DAY_OF_WEEK);
 | 
						|
                        int hour = cal.get(Calendar.HOUR_OF_DAY);
 | 
						|
                        int minute = cal.get(Calendar.MINUTE);
 | 
						|
                        int second = cal.get(Calendar.SECOND);
 | 
						|
                        if (year != values[base + 0] - 1
 | 
						|
                            || month != values[base + 1]
 | 
						|
                            || hour != values[base + 5]
 | 
						|
                            || minute != values[base + 6]
 | 
						|
                            || second != values[base + 7])
 | 
						|
                          break;
 | 
						|
                        if (day_of_week == values[base + 4])
 | 
						|
                          {
 | 
						|
                            // Either a Sun>=8 or lastSun rule.
 | 
						|
                            if (day_of_month < values[base + 8])
 | 
						|
                              values[base + 8] = day_of_month;
 | 
						|
                            if (day_of_month > values[base + 9])
 | 
						|
                              values[base + 9] = day_of_month;
 | 
						|
                            if (values[base + 10] < 0)
 | 
						|
                              break;
 | 
						|
                            if (values[base + 10] == 0)
 | 
						|
                              {
 | 
						|
                                values[base + 10] = 1;
 | 
						|
                                // If day of month > 28, this is
 | 
						|
                                // certainly lastSun rule.
 | 
						|
                                if (values[base + 2] > 28)
 | 
						|
                                  values[base + 2] = 3;
 | 
						|
                                // If day of month isn't in the last
 | 
						|
                                // week, it can't be lastSun rule.
 | 
						|
                                else if (values[base + 2]
 | 
						|
                                         <= values[base + 3] - 7)
 | 
						|
                                  values[base + 3] = 2;
 | 
						|
                              }
 | 
						|
                            if (values[base + 10] == 1)
 | 
						|
                              {
 | 
						|
                                // If day of month is > 28, this is
 | 
						|
                                // certainly lastSun rule.
 | 
						|
                                if (day_of_month > 28)
 | 
						|
                                  values[base + 10] = 3;
 | 
						|
                                // If day of month isn't in the last
 | 
						|
                                // week, it can't be lastSun rule.
 | 
						|
                                else if (day_of_month <= month_days - 7)
 | 
						|
                                  values[base + 10] = 2;
 | 
						|
                              }
 | 
						|
                            else if ((values[base + 10] == 2
 | 
						|
                                      && day_of_month > 28)
 | 
						|
                                     || (values[base + 10] == 3
 | 
						|
                                         && day_of_month <= month_days - 7))
 | 
						|
                              break;
 | 
						|
                          }
 | 
						|
                        else
 | 
						|
                          {
 | 
						|
                            // Must be fixed day in month rule.
 | 
						|
                            if (day_of_month != values[base + 2]
 | 
						|
                                || values[base + 10] > 0)
 | 
						|
                              break;
 | 
						|
                            values[base + 4] = day_of_week;
 | 
						|
                            values[base + 10] = -1;
 | 
						|
                          }
 | 
						|
                        values[base + 0] -= 1;
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
 | 
						|
                if (i < first)
 | 
						|
                  {
 | 
						|
                    for (i = 0; i < 2; i++)
 | 
						|
                      {
 | 
						|
                        int base = 11 * i;
 | 
						|
                        if (values[base + 10] == 0)
 | 
						|
                          continue;
 | 
						|
                        if (values[base + 10] == -1)
 | 
						|
                          {
 | 
						|
                            int[] dayCount
 | 
						|
                              = { 0, 31, 59, 90, 120, 151,
 | 
						|
                                  181, 212, 243, 273, 304, 334 };
 | 
						|
                            int d = dayCount[values[base + 1]
 | 
						|
                                             - Calendar.JANUARY];
 | 
						|
                            d += values[base + 2];
 | 
						|
                            change_spec[i] = ",J" + Integer.toString(d);
 | 
						|
                          }
 | 
						|
                        else if (values[base + 10] == 2)
 | 
						|
                          {
 | 
						|
                            // If we haven't seen all days of the week,
 | 
						|
                            // we can't be sure what the rule really is.
 | 
						|
                            if (values[base + 8] + 6 != values[base + 9])
 | 
						|
                              continue;
 | 
						|
 | 
						|
                            int d;
 | 
						|
                            d = values[base + 1] - Calendar.JANUARY + 1;
 | 
						|
                            // E.g. Sun >= 5 is not representable in POSIX
 | 
						|
                            // TZ env string, use ",Am.n.d" extension
 | 
						|
                            // where m is month 1 .. 12, n is the date on
 | 
						|
                            // or after which it happens and d is day
 | 
						|
                            // of the week, 0 .. 6.  So Sun >= 5 in April
 | 
						|
                            // is ",A4.5.0".
 | 
						|
                            if ((values[base + 8] % 7) == 1)
 | 
						|
                              {
 | 
						|
                                change_spec[i] = ",M" + Integer.toString(d);
 | 
						|
                                d = (values[base + 8] + 6) / 7;
 | 
						|
                              }
 | 
						|
                            else
 | 
						|
                              {
 | 
						|
                                change_spec[i] = ",A" + Integer.toString(d);
 | 
						|
                                d = values[base + 8];
 | 
						|
                              }
 | 
						|
                            change_spec[i] += "." + Integer.toString(d);
 | 
						|
                            d = values[base + 4] - Calendar.SUNDAY;
 | 
						|
                            change_spec[i] += "." + Integer.toString(d);
 | 
						|
                          }
 | 
						|
                        else
 | 
						|
                          {
 | 
						|
                            // If we don't know whether this is lastSun or
 | 
						|
                            // Sun >= 22 rule.  That can be either because
 | 
						|
                            // there was insufficient number of
 | 
						|
                            // transitions, or February, where it is quite
 | 
						|
                            // probable we haven't seen any 29th dates.
 | 
						|
                            // For February, assume lastSun rule, otherwise
 | 
						|
                            // punt.
 | 
						|
                            if (values[base + 10] == 1
 | 
						|
                                && values[base + 1] != Calendar.FEBRUARY)
 | 
						|
                              continue;
 | 
						|
 | 
						|
                            int d;
 | 
						|
                            d = values[base + 1] - Calendar.JANUARY + 1;
 | 
						|
                            change_spec[i] = ",M" + Integer.toString(d);
 | 
						|
                            d = values[base + 4] - Calendar.SUNDAY;
 | 
						|
                            change_spec[i] += ".5." + Integer.toString(d);
 | 
						|
                          }
 | 
						|
 | 
						|
                        // Don't add time specification if time is
 | 
						|
                        // 02:00:00.
 | 
						|
                        if (values[base + 5] != 2
 | 
						|
                            || values[base + 6] != 0
 | 
						|
                            || values[base + 7] != 0)
 | 
						|
                          {
 | 
						|
                            int d = values[base + 5];
 | 
						|
                            change_spec[i] += "/" + Integer.toString(d);
 | 
						|
                            if (values[base + 6] != 0 || values[base + 7] != 0)
 | 
						|
                              {
 | 
						|
                                d = values[base + 6];
 | 
						|
                                if (d < 10)
 | 
						|
                                  change_spec[i]
 | 
						|
                                    += ":0" + Integer.toString(d);
 | 
						|
                                else
 | 
						|
                                  change_spec[i] += ":" + Integer.toString(d);
 | 
						|
                                d = values[base + 7];
 | 
						|
                                if (d >= 10)
 | 
						|
                                   change_spec[i]
 | 
						|
                                     += ":" + Integer.toString(d);
 | 
						|
                                else if (d > 0)
 | 
						|
                                  change_spec[i]
 | 
						|
                                    += ":0" + Integer.toString(d);
 | 
						|
                              }
 | 
						|
                          }
 | 
						|
                      }
 | 
						|
                    if (types[(timecnt - 1) & -2] == std_ind)
 | 
						|
                      {
 | 
						|
                        String tmp = change_spec[0];
 | 
						|
                        change_spec[0] = change_spec[1];
 | 
						|
                        change_spec[1] = tmp;
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
              }
 | 
						|
          }
 | 
						|
 | 
						|
        if (tzstr == null)
 | 
						|
          {
 | 
						|
            tzstr = std_zonename + std_offset_string;
 | 
						|
            if (change_spec[0] != null && change_spec[1] != null)
 | 
						|
              tzstr += dst_zonename + dst_offset_string
 | 
						|
                       + change_spec[0] + change_spec[1];
 | 
						|
          }
 | 
						|
 | 
						|
        if (timecnt == 0)
 | 
						|
          return new SimpleTimeZone(offsets[std_ind] * 1000,
 | 
						|
                                    id != null ? id : tzstr);
 | 
						|
 | 
						|
        SimpleTimeZone endRule = createLastRule(tzstr);
 | 
						|
        if (endRule == null)
 | 
						|
          return null;
 | 
						|
 | 
						|
        /* Finally adjust the times array into the form the constructor
 | 
						|
         * expects.  times[0] is special, the offset and DST flag there
 | 
						|
         * are for all times before that transition.  Use the first non-DST
 | 
						|
         * type.  For all other transitions, the data file has the type
 | 
						|
         * (<offset, isdst, zonename>) for the time interval starting
 | 
						|
         */
 | 
						|
        for (int i = 0; i < typecnt; i++)
 | 
						|
          if ((typeflags[i] & (1 << 8)) == 0)
 | 
						|
            {
 | 
						|
              times[0] = (times[0] << SECS_SHIFT) | (offsets[i] & OFFSET_MASK);
 | 
						|
              break;
 | 
						|
            }
 | 
						|
 | 
						|
        for (int i = 1; i < timecnt; i++)
 | 
						|
          times[i] = (times[i] << SECS_SHIFT)
 | 
						|
                     | (offsets[types[i - 1]] & OFFSET_MASK)
 | 
						|
                     | ((typeflags[types[i - 1]] & (1 << 8)) != 0 ? IS_DST : 0);
 | 
						|
 | 
						|
        return new ZoneInfo(offsets[std_ind] * 1000, id != null ? id : tzstr,
 | 
						|
                            times, endRule);
 | 
						|
      }
 | 
						|
    catch (IOException ioe)
 | 
						|
      {
 | 
						|
        // Parse error, not a proper tzfile.
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
    finally
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            if (dis != null)
 | 
						|
              dis.close();
 | 
						|
          }
 | 
						|
        catch(IOException ioe)
 | 
						|
          {
 | 
						|
            // Error while close, nothing we can do.
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Skips the requested number of bytes in the given InputStream.
 | 
						|
   * Throws EOFException if not enough bytes could be skipped.
 | 
						|
   * Negative numbers of bytes to skip are ignored.
 | 
						|
   */
 | 
						|
  private static void skipFully(InputStream is, long l) throws IOException
 | 
						|
  {
 | 
						|
    while (l > 0)
 | 
						|
      {
 | 
						|
        long k = is.skip(l);
 | 
						|
        if (k <= 0)
 | 
						|
          throw new EOFException();
 | 
						|
        l -= k;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Create a SimpleTimeZone from a POSIX TZ environment string,
 | 
						|
   * see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
 | 
						|
   * for details.
 | 
						|
   * It supports also an extension, where Am.n.d rule (m 1 .. 12, n 1 .. 25, d
 | 
						|
   * 0 .. 6) describes day of week d on or after nth day of month m.
 | 
						|
   * Say A4.5.0 is Sun>=5 in April.
 | 
						|
   */
 | 
						|
  private static SimpleTimeZone createLastRule(String tzstr)
 | 
						|
  {
 | 
						|
    String stdName = null;
 | 
						|
    int stdOffs;
 | 
						|
    int dstOffs;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        int idLength = tzstr.length();
 | 
						|
 | 
						|
        int index = 0;
 | 
						|
        int prevIndex;
 | 
						|
        char c;
 | 
						|
 | 
						|
        // get std
 | 
						|
        do
 | 
						|
          c = tzstr.charAt(index);
 | 
						|
        while (c != '+' && c != '-' && c != ',' && c != ':'
 | 
						|
               && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
 | 
						|
 | 
						|
        if (index >= idLength)
 | 
						|
          return new SimpleTimeZone(0, tzstr);
 | 
						|
 | 
						|
        stdName = tzstr.substring(0, index);
 | 
						|
        prevIndex = index;
 | 
						|
 | 
						|
        // get the std offset
 | 
						|
        do
 | 
						|
          c = tzstr.charAt(index++);
 | 
						|
        while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
 | 
						|
               && index < idLength);
 | 
						|
        if (index < idLength)
 | 
						|
          index--;
 | 
						|
 | 
						|
        { // convert the dst string to a millis number
 | 
						|
            String offset = tzstr.substring(prevIndex, index);
 | 
						|
            prevIndex = index;
 | 
						|
 | 
						|
            if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
 | 
						|
              stdOffs = parseTime(offset.substring(1));
 | 
						|
            else
 | 
						|
              stdOffs = parseTime(offset);
 | 
						|
 | 
						|
            if (offset.charAt(0) == '-')
 | 
						|
              stdOffs = -stdOffs;
 | 
						|
 | 
						|
            // TZ timezone offsets are positive when WEST of the meridian.
 | 
						|
            stdOffs = -stdOffs;
 | 
						|
        }
 | 
						|
 | 
						|
        // Done yet? (Format: std offset)
 | 
						|
        if (index >= idLength)
 | 
						|
          return new SimpleTimeZone(stdOffs, stdName);
 | 
						|
 | 
						|
        // get dst
 | 
						|
        do
 | 
						|
          c = tzstr.charAt(index);
 | 
						|
        while (c != '+' && c != '-' && c != ',' && c != ':'
 | 
						|
               && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
 | 
						|
 | 
						|
        // Done yet? (Format: std offset dst)
 | 
						|
        if (index >= idLength)
 | 
						|
          return new SimpleTimeZone(stdOffs, stdName);
 | 
						|
 | 
						|
        // get the dst offset
 | 
						|
        prevIndex = index;
 | 
						|
        do
 | 
						|
          c = tzstr.charAt(index++);
 | 
						|
        while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
 | 
						|
               && index < idLength);
 | 
						|
        if (index < idLength)
 | 
						|
          index--;
 | 
						|
 | 
						|
        if (index == prevIndex && (c == ',' || c == ';'))
 | 
						|
          {
 | 
						|
            // Missing dst offset defaults to one hour ahead of standard
 | 
						|
            // time.
 | 
						|
            dstOffs = stdOffs + 60 * 60 * 1000;
 | 
						|
          }
 | 
						|
        else
 | 
						|
          { // convert the dst string to a millis number
 | 
						|
            String offset = tzstr.substring(prevIndex, index);
 | 
						|
            prevIndex = index;
 | 
						|
 | 
						|
            if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
 | 
						|
              dstOffs = parseTime(offset.substring(1));
 | 
						|
            else
 | 
						|
              dstOffs = parseTime(offset);
 | 
						|
 | 
						|
            if (offset.charAt(0) == '-')
 | 
						|
              dstOffs = -dstOffs;
 | 
						|
 | 
						|
            // TZ timezone offsets are positive when WEST of the meridian.
 | 
						|
            dstOffs = -dstOffs;
 | 
						|
          }
 | 
						|
 | 
						|
        // Done yet? (Format: std offset dst offset)
 | 
						|
        if (index >= idLength)
 | 
						|
          return new SimpleTimeZone(stdOffs, stdName);
 | 
						|
 | 
						|
        // get the DST rule
 | 
						|
        if (tzstr.charAt(index) == ','
 | 
						|
            || tzstr.charAt(index) == ';')
 | 
						|
          {
 | 
						|
            index++;
 | 
						|
            int offs = index;
 | 
						|
            while (tzstr.charAt(index) != ','
 | 
						|
                   && tzstr.charAt(index) != ';')
 | 
						|
              index++;
 | 
						|
            String startTime = tzstr.substring(offs, index);
 | 
						|
            index++;
 | 
						|
            String endTime = tzstr.substring(index);
 | 
						|
 | 
						|
            index = startTime.indexOf('/');
 | 
						|
            int startMillis;
 | 
						|
            int endMillis;
 | 
						|
            String startDate;
 | 
						|
            String endDate;
 | 
						|
            if (index != -1)
 | 
						|
              {
 | 
						|
                startDate = startTime.substring(0, index);
 | 
						|
                startMillis = parseTime(startTime.substring(index + 1));
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                startDate = startTime;
 | 
						|
                // if time isn't given, default to 2:00:00 AM.
 | 
						|
                startMillis = 2 * 60 * 60 * 1000;
 | 
						|
              }
 | 
						|
            index = endTime.indexOf('/');
 | 
						|
            if (index != -1)
 | 
						|
              {
 | 
						|
                endDate = endTime.substring(0, index);
 | 
						|
                endMillis = parseTime(endTime.substring(index + 1));
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                endDate = endTime;
 | 
						|
                // if time isn't given, default to 2:00:00 AM.
 | 
						|
                endMillis = 2 * 60 * 60 * 1000;
 | 
						|
              }
 | 
						|
 | 
						|
            int[] start = getDateParams(startDate);
 | 
						|
            int[] end = getDateParams(endDate);
 | 
						|
            return new SimpleTimeZone(stdOffs, tzstr, start[0], start[1],
 | 
						|
                                      start[2], startMillis, end[0], end[1],
 | 
						|
                                      end[2], endMillis, (dstOffs - stdOffs));
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
    catch (IndexOutOfBoundsException _)
 | 
						|
      {
 | 
						|
      }
 | 
						|
    catch (NumberFormatException _)
 | 
						|
      {
 | 
						|
      }
 | 
						|
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Parses and returns the params for a POSIX TZ date field,
 | 
						|
   * in the format int[]{ month, day, dayOfWeek }, following the
 | 
						|
   * SimpleTimeZone constructor rules.
 | 
						|
   */
 | 
						|
  private static int[] getDateParams(String date)
 | 
						|
  {
 | 
						|
    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
 | 
						|
    int month;
 | 
						|
    int type = 0;
 | 
						|
 | 
						|
    if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
 | 
						|
      type = 1;
 | 
						|
    else if (date.charAt(0) == 'A' || date.charAt(0) == 'a')
 | 
						|
      type = 2;
 | 
						|
 | 
						|
    if (type > 0)
 | 
						|
      {
 | 
						|
        int day;
 | 
						|
 | 
						|
        // Month, week of month, day of week
 | 
						|
        // "Mm.w.d".  d is between 0 (Sunday) and 6.  Week w is
 | 
						|
        // between 1 and 5; Week 1 is the first week in which day d
 | 
						|
        // occurs and Week 5 specifies the last d day in the month.
 | 
						|
        // Month m is between 1 and 12.
 | 
						|
 | 
						|
        // Month, day of month, day of week
 | 
						|
        // ZoneInfo extension, not in POSIX
 | 
						|
        // "Am.n.d".  d is between 0 (Sunday) and 6.  Day of month n is
 | 
						|
        // between 1 and 25.  Month m is between 1 and 12.
 | 
						|
 | 
						|
        month = Integer.parseInt(date.substring(1, date.indexOf('.')));
 | 
						|
        int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
 | 
						|
                                                   date.lastIndexOf('.')));
 | 
						|
        int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
 | 
						|
                                                        + 1));
 | 
						|
        dayOfWeek++; // Java day of week is one-based, Sunday is first day.
 | 
						|
 | 
						|
        if (type == 2)
 | 
						|
          {
 | 
						|
            day = week;
 | 
						|
            dayOfWeek = -dayOfWeek;
 | 
						|
          }
 | 
						|
        else if (week == 5)
 | 
						|
          day = -1; // last day of month is -1 in java, 5 in TZ
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // First day of week starting on or after.  For example,
 | 
						|
            // to specify the second Sunday of April, set month to
 | 
						|
            // APRIL, day-of-month to 8, and day-of-week to -SUNDAY.
 | 
						|
            day = (week - 1) * 7 + 1;
 | 
						|
            dayOfWeek = -dayOfWeek;
 | 
						|
          }
 | 
						|
 | 
						|
        month--; // Java month is zero-based.
 | 
						|
        return new int[] { month, day, dayOfWeek };
 | 
						|
      }
 | 
						|
 | 
						|
    // julian day, either zero-based 0<=n<=365 (incl feb 29)
 | 
						|
    // or one-based 1<=n<=365 (no feb 29)
 | 
						|
    int julianDay; // Julian day
 | 
						|
 | 
						|
    if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
 | 
						|
      {
 | 
						|
        julianDay = Integer.parseInt(date.substring(1));
 | 
						|
        julianDay++; // make 1-based
 | 
						|
        // Adjust day count to include feb 29.
 | 
						|
        dayCount = new int[]
 | 
						|
                   {
 | 
						|
                     0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
 | 
						|
                   };
 | 
						|
      }
 | 
						|
    else
 | 
						|
      // 1-based julian day
 | 
						|
      julianDay = Integer.parseInt(date);
 | 
						|
 | 
						|
    int i = 11;
 | 
						|
    while (i > 0)
 | 
						|
      if (dayCount[i] < julianDay)
 | 
						|
        break;
 | 
						|
      else
 | 
						|
        i--;
 | 
						|
    julianDay -= dayCount[i];
 | 
						|
    month = i;
 | 
						|
    return new int[] { month, julianDay, 0 };
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Parses a time field hh[:mm[:ss]], returning the result
 | 
						|
   * in milliseconds. No leading sign.
 | 
						|
   */
 | 
						|
  private static int parseTime(String time)
 | 
						|
  {
 | 
						|
    int millis = 0;
 | 
						|
    int i = 0;
 | 
						|
 | 
						|
    while (i < time.length())
 | 
						|
      if (time.charAt(i) == ':')
 | 
						|
        break;
 | 
						|
      else
 | 
						|
        i++;
 | 
						|
    millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
 | 
						|
    if (i >= time.length())
 | 
						|
      return millis;
 | 
						|
 | 
						|
    int iprev = ++i;
 | 
						|
    while (i < time.length())
 | 
						|
      if (time.charAt(i) == ':')
 | 
						|
        break;
 | 
						|
      else
 | 
						|
        i++;
 | 
						|
    millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
 | 
						|
    if (i >= time.length())
 | 
						|
      return millis;
 | 
						|
 | 
						|
    millis += 1000 * Integer.parseInt(time.substring(++i));
 | 
						|
    return millis;
 | 
						|
  }
 | 
						|
}
 |