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;
 | |
|   }
 | |
| }
 |