OSDN Git Service

2007-02-09 Jakub Jelinek <jakub@redhat.com>
authoraph <aph@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 12 Feb 2007 14:39:44 +0000 (14:39 +0000)
committeraph <aph@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 12 Feb 2007 14:39:44 +0000 (14:39 +0000)
* java/util/VMTimeZone.java: Rewrite to handle both the old
'TZif\0' format and the new one.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@121845 138bc75d-0d04-0410-961f-82ee72b054a4

libjava/ChangeLog
libjava/classpath/lib/java/util/VMTimeZone.class
libjava/java/util/VMTimeZone.java

index 1c39b14..39a5902 100644 (file)
@@ -1,3 +1,8 @@
+2007-02-09  Jakub Jelinek  <jakub@redhat.com>
+
+       * java/util/VMTimeZone.java: Rewrite to handle both the old
+       'TZif\0' format and the new one.
+       
 2007-02-10  Andrew Haley  <aph@redhat.com>
 
        PR java/30742
 2007-02-10  Andrew Haley  <aph@redhat.com>
 
        PR java/30742
index 351461d..a175a44 100644 (file)
Binary files a/libjava/classpath/lib/java/util/VMTimeZone.class and b/libjava/classpath/lib/java/util/VMTimeZone.class differ
index 86b6258..27bab93 100644 (file)
@@ -1,5 +1,5 @@
 /* java.util.VMTimeZone
 /* java.util.VMTimeZone
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007
    Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
    Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
@@ -40,6 +40,9 @@ exception statement from your version. */
 package java.util;
 
 import gnu.classpath.Configuration;
 package java.util;
 
 import gnu.classpath.Configuration;
+import java.util.TimeZone;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
 
 import java.io.*;
 
 
 import java.io.*;
 
@@ -206,71 +209,152 @@ final class VMTimeZone
     File f = new File(file);
     if (!f.exists())
       return null;
     File f = new File(file);
     if (!f.exists())
       return null;
-    
+
     DataInputStream dis = null;
     try
       {
     DataInputStream dis = null;
     try
       {
-        FileInputStream fis = new FileInputStream(f);
-        BufferedInputStream bis = new BufferedInputStream(fis);
-        dis = new DataInputStream(bis);
-       
-        // Make sure we are reading a tzfile.
-        byte[] tzif = new byte[4];
-        dis.readFully(tzif);
-        if (tzif[0] == 'T' && tzif[1] == 'Z'
-            && tzif[2] == 'i' && tzif[3] == 'f')
-         // Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt
-         skipFully(dis, 16 + 3 * 4);
+       FileInputStream fis = new FileInputStream(f);
+       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
        else
          // Darwin has tzdata files that don't start with the TZif marker
-         skipFully(dis, 16 + 3 * 4 - 4);
-       
+         skipFully(dis, 16 - 5);
+
+       String id = null;
+       int ttisgmtcnt = dis.readInt();
+       int ttisstdcnt = dis.readInt();
+       int leapcnt = dis.readInt();
        int timecnt = dis.readInt();
        int typecnt = 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();
+         }
        if (typecnt > 0)
          {
        if (typecnt > 0)
          {
-           int charcnt = dis.readInt();
-           // Transition times plus indexed transition times.
-           skipFully(dis, timecnt * (4 + 1));
-           
-           // Get last gmt_offset and dst/non-dst time zone names.
-           int abbrind = -1;
+           int seltimecnt = timecnt;
+           if (seltimecnt > 16)
+             seltimecnt = 16;
+
+           long[] times = new long[seltimecnt];
+           int[] types = new int[seltimecnt];
+
+           // Transition times
+           skipFully(dis, (timecnt - seltimecnt) * tzif2);
+
+           for (int i = 0; i < seltimecnt; i++)
+             if (tzif2 == 8)
+               times[i] = dis.readLong();
+             else
+               times[i] = (long) dis.readInt();
+
+           // Transition types
+           skipFully(dis, timecnt - seltimecnt);
+           for (int i = 0; i < seltimecnt; i++)
+             {
+               types[i] = dis.readByte();
+               if (types[i] < 0)
+                 types[i] += 256;
+             }
+
+           // Get std/dst_offset and dst/non-dst time zone names.
+           int std_abbrind = -1;
            int dst_abbrind = -1;
            int dst_abbrind = -1;
-           int gmt_offset = 0;
-           while (typecnt-- > 0)
+           int std_offset = 0;
+           int dst_offset = 0;
+           int std_ind = -1;
+           int dst_ind = -1;
+
+           int alternation = 0;
+           if (seltimecnt >= 4 && types[0] != types[1]
+               && types[0] < typecnt && types[1] < typecnt)
+             {
+               // Verify only two types are involved
+               // in the transitions and they alternate.
+               alternation = 1;
+               for (int i = 2; i < seltimecnt; i++)
+                 if (types[i] != types[i % 2])
+                   alternation = 0;
+             }
+
+           // If a timezone previously used DST, but no longer does
+           // (or no longer will in the near future, say 5 years),
+           // then always pick only the std zone type corresponding
+           // to latest applicable transition.
+           if (seltimecnt > 0
+               && times[seltimecnt - 1]
+                  < System.currentTimeMillis() / 1000 + 5 * 365 * 86400)
+             alternation = -1;
+
+           for (int i = 0; i < typecnt; i++)
              {
                // gmtoff
                int offset = dis.readInt();
                int dst = dis.readByte();
              {
                // gmtoff
                int offset = dis.readInt();
                int dst = dis.readByte();
+               int abbrind = dis.readByte();
                if (dst == 0)
                  {
                if (dst == 0)
                  {
-                   abbrind = dis.readByte();
-                   gmt_offset = offset;
+                   if (alternation == 0
+                       || (alternation == 1
+                           && (i == types[0] || i == types[1]))
+                       || (alternation == -1 && i == types[seltimecnt - 1]))
+                     {
+                       std_abbrind = abbrind;
+                       std_offset = offset * -1;
+                       std_ind = i;
+                     }
+                 }
+               else if (alternation >= 0)
+                 {
+                   if (alternation == 0 || i == types[0] || i == types[1])
+                     {
+                       dst_abbrind = abbrind;
+                       dst_offset = offset * -1;
+                       dst_ind = i;
+                     }
                  }
                  }
-               else
-                 dst_abbrind = dis.readByte();
              }
              }
-           
-           // gmt_offset is the offset you must add to UTC/GMT to
-           // get the local time, we need the offset to add to
-           // the local time to get UTC/GMT.
-           gmt_offset *= -1;
-           
-           // Turn into hours if possible.
-           if (gmt_offset % 3600 == 0)
-             gmt_offset /= 3600;
-           
-           if (abbrind >= 0)
+
+           if (std_abbrind >= 0)
              {
                byte[] names = new byte[charcnt];
                dis.readFully(names);
              {
                byte[] names = new byte[charcnt];
                dis.readFully(names);
-               int j = abbrind;
+               int j = std_abbrind;
                while (j < charcnt && names[j] != 0)
                  j++;
                while (j < charcnt && names[j] != 0)
                  j++;
-               
-               String zonename = new String(names, abbrind, j - abbrind,
-                                            "ASCII");
-               
+
+               String zonename = new String(names, std_abbrind,
+                                            j - std_abbrind, "ASCII");
+
                String dst_zonename;
                if (dst_abbrind >= 0)
                  {
                String dst_zonename;
                if (dst_abbrind >= 0)
                  {
@@ -282,26 +366,289 @@ final class VMTimeZone
                  }
                else
                  dst_zonename = "";
                  }
                else
                  dst_zonename = "";
-               
+
+               String[] change_spec = { null, null };
+               if (dst_abbrind >= 0 && alternation > 0)
+                 {
+                   // Guess rules for the std->dst and dst->std transitions
+                   // from the transition times since Epoch.
+                   // 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
+                   GregorianCalendar cal
+                     = new GregorianCalendar (TimeZone.getTimeZone("GMT"));
+
+                   int[] values = new int[2 * 11];
+                   int i;
+                   for (i = seltimecnt - 1; i >= 0; i--)
+                     {
+                       int base = (i % 2) * 11;
+                       int offset = types[i] == dst_ind ? std_offset : dst_offset;
+                       cal.setTimeInMillis((times[i] - offset) * 1000);
+                       if (i >= seltimecnt - 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 < 0)
+                     {
+                       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;
+
+                               // FIXME: Sun >= 5 is representable in
+                               // SimpleTimeZone, but not in POSIX TZ env
+                               // strings.  Should we change readtzFile
+                               // to actually return a SimpleTimeZone
+                               // rather than POSIX TZ string?
+                               if ((values[base + 8] % 7) != 1)
+                                 continue;
+
+                               int d;
+                               d = values[base + 1] - Calendar.JANUARY + 1;
+                               change_spec[i] = ",M" + Integer.toString(d);
+                               d = (values[base + 8] + 6) / 7;
+                               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[0] == std_ind)
+                         {
+                           String tmp = change_spec[0];
+                           change_spec[0] = change_spec[1];
+                           change_spec[1] = tmp;
+                         }
+                     }
+                 }
+
                // Only use gmt offset when necessary.
                // Also special case GMT+/- timezones.
                // Only use gmt offset when necessary.
                // Also special case GMT+/- timezones.
-               String offset_string;
-               if ("".equals(dst_zonename)
-                   && (gmt_offset == 0
+               String offset_string, dst_offset_string = "";
+               if (dst_abbrind < 0
+                   && (std_offset == 0
                        || zonename.startsWith("GMT+")
                        || zonename.startsWith("GMT-")))
                  offset_string = "";
                else
                        || zonename.startsWith("GMT+")
                        || zonename.startsWith("GMT-")))
                  offset_string = "";
                else
-                 offset_string = Integer.toString(gmt_offset);
-               
-               String id = zonename + offset_string + dst_zonename;
-               
-               return id;
+                 {
+                   offset_string = Integer.toString(std_offset / 3600);
+                   int seconds = std_offset % 3600;
+                   if (seconds != 0)
+                     {
+                       if (seconds < 0)
+                         seconds *= -1;
+                       if (seconds < 600)
+                         offset_string
+                           += ":0" + Integer.toString(seconds / 60);
+                       else
+                         offset_string
+                           += ":" + Integer.toString(seconds / 60);
+                       seconds = seconds % 60;
+                       if (seconds >= 10)
+                         offset_string
+                           += ":" + Integer.toString(seconds);
+                       else if (seconds > 0)
+                         offset_string
+                           += ":0" + Integer.toString(seconds);
+                     }
+                   if (dst_abbrind >= 0
+                       && dst_offset != std_offset - 3600)
+                     {
+                       dst_offset_string
+                         = Integer.toString(dst_offset / 3600);
+                       seconds = dst_offset % 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 (dst_abbrind < 0)
+                 id = zonename + offset_string;
+               else if (change_spec[0] != null && change_spec[1] != null)
+                 id = zonename + offset_string + dst_zonename
+                      + dst_offset_string + change_spec[0] + change_spec[1];
              }
              }
+           else if (tzif2 == 8)
+             skipFully(dis, charcnt);
          }
          }
-       
-       // Something didn't match while reading the file.
-       return null;
+       else if (tzif2 == 8)
+         skipFully(dis, timecnt * (8 + 1) + typecnt * (4 + 1 + 1) + charcnt);
+
+       if (tzif2 == 8)
+         {
+           // Skip over the rest of 64-bit data
+           skipFully(dis, leapcnt * (8 + 4) + ttisgmtcnt + ttisstdcnt);
+           if (dis.readByte() == '\n')
+             {
+               String posixtz = dis.readLine();
+               if (posixtz.length() > 0)
+                 id = posixtz;
+             }
+         }
+
+       return id;
       }
     catch (IOException ioe)
       {
       }
     catch (IOException ioe)
       {