jdk/test/sun/util/calendar/zi/Zoneinfo.java
changeset 15658 55b829ca2334
child 23010 6dadb192ad81
equal deleted inserted replaced
15657:c588664d547e 15658:55b829ca2334
       
     1 /*
       
     2  * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 import java.io.BufferedReader;
       
    27 import java.io.FileReader;
       
    28 import java.io.FileNotFoundException;
       
    29 import java.io.IOException;
       
    30 import java.util.HashMap;
       
    31 import java.util.List;
       
    32 import java.util.Map;
       
    33 import java.util.StringTokenizer;
       
    34 
       
    35 /**
       
    36  * Zoneinfo provides javazic compiler front-end functionality.
       
    37  * @since 1.4
       
    38  */
       
    39 class Zoneinfo {
       
    40 
       
    41     private static final int minYear = 1900;
       
    42     private static final int maxYear = 2037;
       
    43     private static final long minTime = Time.getLocalTime(minYear, Month.JANUARY, 1, 0);
       
    44     private static int startYear = minYear;
       
    45     private static int endYear = maxYear;
       
    46 
       
    47     /**
       
    48      * True if javazic should generate a list of SimpleTimeZone
       
    49      * instances for the SimpleTimeZone-based time zone support.
       
    50      */
       
    51     static boolean isYearForTimeZoneDataSpecified = false;
       
    52 
       
    53     /**
       
    54      * Zone name to Zone mappings
       
    55      */
       
    56     private Map<String,Zone> zones;
       
    57 
       
    58     /**
       
    59      * Rule name to Rule mappings
       
    60      */
       
    61     private Map<String,Rule> rules;
       
    62 
       
    63     /**
       
    64      * Alias name to real name mappings
       
    65      */
       
    66     private Map<String,String> aliases;
       
    67 
       
    68     /**
       
    69      * Constracts a Zoneinfo.
       
    70      */
       
    71     Zoneinfo() {
       
    72         zones = new HashMap<String,Zone>();
       
    73         rules = new HashMap<String,Rule>();
       
    74         aliases = new HashMap<String,String>();
       
    75     }
       
    76 
       
    77     /**
       
    78      * Adds the given zone to the list of Zones.
       
    79      * @param zone Zone to be added to the list.
       
    80      */
       
    81     void add(Zone zone) {
       
    82         String name = zone.getName();
       
    83         zones.put(name, zone);
       
    84     }
       
    85 
       
    86     /**
       
    87      * Adds the given rule to the list of Rules.
       
    88      * @param rule Rule to be added to the list.
       
    89      */
       
    90     void add(Rule rule) {
       
    91         String name = rule.getName();
       
    92         rules.put(name, rule);
       
    93     }
       
    94 
       
    95     /**
       
    96      * Puts the specifid name pair to the alias table.
       
    97      * @param name1 an alias time zone name
       
    98      * @param name2 the real time zone of the alias name
       
    99      */
       
   100     void putAlias(String name1, String name2) {
       
   101         aliases.put(name1, name2);
       
   102     }
       
   103 
       
   104     /**
       
   105      * Sets the given year for SimpleTimeZone list output.
       
   106      * This method is called when the -S option is specified.
       
   107      * @param year the year for which SimpleTimeZone list should be generated
       
   108      */
       
   109     static void setYear(int year) {
       
   110         setStartYear(year);
       
   111         setEndYear(year);
       
   112         isYearForTimeZoneDataSpecified = true;
       
   113     }
       
   114 
       
   115     /**
       
   116      * Sets the start year.
       
   117      * @param year the start year value
       
   118      * @throws IllegalArgumentException if the specified year value is
       
   119      * smaller than the minimum year or greater than the end year.
       
   120      */
       
   121     static void setStartYear(int year) {
       
   122         if (year < minYear || year > endYear) {
       
   123             throw new IllegalArgumentException("invalid start year specified: " + year);
       
   124         }
       
   125         startYear = year;
       
   126     }
       
   127 
       
   128     /**
       
   129      * @return the start year value
       
   130      */
       
   131     static int getStartYear() {
       
   132         return startYear;
       
   133     }
       
   134 
       
   135     /**
       
   136      * Sets the end year.
       
   137      * @param year the end year value
       
   138      * @throws IllegalArgumentException if the specified year value is
       
   139      * smaller than the start year or greater than the maximum year.
       
   140      */
       
   141     static void setEndYear(int year) {
       
   142         if (year < startYear || year > maxYear) {
       
   143             throw new IllegalArgumentException();
       
   144         }
       
   145         endYear = year;
       
   146     }
       
   147 
       
   148     /**
       
   149      * @return the end year value
       
   150      */
       
   151     static int getEndYear() {
       
   152         return endYear;
       
   153     }
       
   154 
       
   155     /**
       
   156      * @return the minimum year value
       
   157      */
       
   158     static int getMinYear() {
       
   159         return minYear;
       
   160     }
       
   161 
       
   162     /**
       
   163      * @return the maximum year value
       
   164      */
       
   165     static int getMaxYear() {
       
   166         return maxYear;
       
   167     }
       
   168 
       
   169     /**
       
   170      * @return the alias table
       
   171      */
       
   172     Map<String,String> getAliases() {
       
   173         return aliases;
       
   174     }
       
   175 
       
   176     /**
       
   177      * @return the Zone list
       
   178      */
       
   179     Map<String,Zone> getZones() {
       
   180         return zones;
       
   181     }
       
   182 
       
   183     /**
       
   184      * @return a Zone specified by name.
       
   185      * @param name a zone name
       
   186      */
       
   187     Zone getZone(String name) {
       
   188         return zones.get(name);
       
   189     }
       
   190 
       
   191     /**
       
   192      * @return a Rule specified by name.
       
   193      * @param name a rule name
       
   194      */
       
   195     Rule getRule(String name) {
       
   196         return rules.get(name);
       
   197     }
       
   198 
       
   199     private static String line;
       
   200 
       
   201     private static int lineNum;
       
   202 
       
   203     /**
       
   204      * Parses the specified time zone data file and creates a Zoneinfo
       
   205      * that has all Rules, Zones and Links (aliases) information.
       
   206      * @param fname the time zone data file name
       
   207      * @return a Zoneinfo object
       
   208      */
       
   209     static Zoneinfo parse(String fname) {
       
   210         BufferedReader in = null;
       
   211         try {
       
   212             FileReader fr = new FileReader(fname);
       
   213             in = new BufferedReader(fr);
       
   214         } catch (FileNotFoundException e) {
       
   215             panic("can't open file: "+fname);
       
   216         }
       
   217         Zoneinfo zi = new Zoneinfo();
       
   218         boolean continued = false;
       
   219         Zone zone = null;
       
   220         String l;
       
   221         lineNum = 0;
       
   222 
       
   223         try {
       
   224             while ((line = in.readLine()) != null) {
       
   225                 lineNum++;
       
   226                 // skip blank and comment lines
       
   227                 if (line.length() == 0 || line.charAt(0) == '#') {
       
   228                     continue;
       
   229                 }
       
   230 
       
   231                 // trim trailing comments
       
   232                 int rindex = line.lastIndexOf('#');
       
   233                 if (rindex != -1) {
       
   234                     // take the data part of the line
       
   235                     l = line.substring(0, rindex);
       
   236                 } else {
       
   237                     l = line;
       
   238                 }
       
   239 
       
   240                 StringTokenizer tokens = new StringTokenizer(l);
       
   241                 if (!tokens.hasMoreTokens()) {
       
   242                     continue;
       
   243                 }
       
   244                 String token = tokens.nextToken();
       
   245 
       
   246                 if (continued || "Zone".equals(token)) {
       
   247                     if (zone == null) {
       
   248                         if (!tokens.hasMoreTokens()) {
       
   249                             panic("syntax error: zone no more token");
       
   250                         }
       
   251                         token = tokens.nextToken();
       
   252                         // if the zone name is in "GMT+hh" or "GMT-hh"
       
   253                         // format, ignore it due to spec conflict.
       
   254                         if (token.startsWith("GMT+") || token.startsWith("GMT-")) {
       
   255                             continue;
       
   256                         }
       
   257                         zone = new Zone(token);
       
   258                     } else {
       
   259                         // no way to push the current token back...
       
   260                         tokens = new StringTokenizer(l);
       
   261                     }
       
   262 
       
   263                     ZoneRec zrec = ZoneRec.parse(tokens);
       
   264                     zrec.setLine(line);
       
   265                     zone.add(zrec);
       
   266                     if ((continued = zrec.hasUntil()) == false) {
       
   267                         if (Zone.isTargetZone(zone.getName())) {
       
   268                             // zone.resolve(zi);
       
   269                             zi.add(zone);
       
   270                         }
       
   271                         zone = null;
       
   272                     }
       
   273                 } else if ("Rule".equals(token)) {
       
   274                     if (!tokens.hasMoreTokens()) {
       
   275                         panic("syntax error: rule no more token");
       
   276                     }
       
   277                     token = tokens.nextToken();
       
   278                     Rule rule = zi.getRule(token);
       
   279                     if (rule == null) {
       
   280                         rule = new Rule(token);
       
   281                         zi.add(rule);
       
   282                     }
       
   283                     RuleRec rrec = RuleRec.parse(tokens);
       
   284                     rrec.setLine(line);
       
   285                     rule.add(rrec);
       
   286                 } else if ("Link".equals(token)) {
       
   287                     // Link <newname> <oldname>
       
   288                     try {
       
   289                         String name1 = tokens.nextToken();
       
   290                         String name2 = tokens.nextToken();
       
   291 
       
   292                         // if the zone name is in "GMT+hh" or "GMT-hh"
       
   293                         // format, ignore it due to spec conflict with
       
   294                         // custom time zones. Also, ignore "ROC" for
       
   295                         // PC-ness.
       
   296                         if (name2.startsWith("GMT+") || name2.startsWith("GMT-")
       
   297                             || "ROC".equals(name2)) {
       
   298                             continue;
       
   299                         }
       
   300                         zi.putAlias(name2, name1);
       
   301                     } catch (Exception e) {
       
   302                         panic("syntax error: no more token for Link");
       
   303                     }
       
   304                 }
       
   305             }
       
   306             in.close();
       
   307         } catch (IOException ex) {
       
   308             panic("IO error: " + ex.getMessage());
       
   309         }
       
   310 
       
   311         return zi;
       
   312     }
       
   313 
       
   314     /**
       
   315      * Interprets a zone and constructs a Timezone object that
       
   316      * contains enough information on GMT offsets and DST schedules to
       
   317      * generate a zone info database.
       
   318      *
       
   319      * @param zoneName the zone name for which a Timezone object is
       
   320      * constructed.
       
   321      *
       
   322      * @return a Timezone object that contains all GMT offsets and DST
       
   323      * rules information.
       
   324      */
       
   325     Timezone phase2(String zoneName) {
       
   326         Timezone tz = new Timezone(zoneName);
       
   327         Zone zone = getZone(zoneName);
       
   328         zone.resolve(this);
       
   329 
       
   330         // TODO: merge phase2's for the regular and SimpleTimeZone ones.
       
   331         if (isYearForTimeZoneDataSpecified) {
       
   332             ZoneRec zrec = zone.get(zone.size()-1);
       
   333             tz.setLastZoneRec(zrec);
       
   334             tz.setRawOffset(zrec.getGmtOffset());
       
   335             if (zrec.hasRuleReference()) {
       
   336                 /*
       
   337                  * This part assumes that the specified year is covered by
       
   338                  * the rules referred to by the last zone record.
       
   339                  */
       
   340                 List<RuleRec> rrecs = zrec.getRuleRef().getRules(startYear);
       
   341 
       
   342                 if (rrecs.size() == 2) {
       
   343                     // make sure that one is a start rule and the other is
       
   344                     // an end rule.
       
   345                     RuleRec r0 = rrecs.get(0);
       
   346                     RuleRec r1 = rrecs.get(1);
       
   347                     if (r0.getSave() == 0 && r1.getSave() > 0) {
       
   348                         rrecs.set(0, r1);
       
   349                         rrecs.set(1, r0);
       
   350                     } else if (!(r0.getSave() > 0 && r1.getSave() == 0)) {
       
   351                         rrecs = null;
       
   352                         Main.error(zoneName + ": rules for " +  startYear + " not found.");
       
   353                     }
       
   354                 } else {
       
   355                     rrecs = null;
       
   356                 }
       
   357                 if (rrecs != null) {
       
   358                     tz.setLastRules(rrecs);
       
   359                 }
       
   360             }
       
   361             return tz;
       
   362         }
       
   363 
       
   364         int gmtOffset;
       
   365         int year = minYear;
       
   366         int fromYear = year;
       
   367         long fromTime = Time.getLocalTime(startYear,
       
   368                                           Month.JANUARY,
       
   369                                           1, 0);
       
   370 
       
   371         // take the index 0 for the GMT offset of the last zone record
       
   372         ZoneRec zrec = zone.get(zone.size()-1);
       
   373         tz.getOffsetIndex(zrec.getGmtOffset());
       
   374 
       
   375         int currentSave = 0;
       
   376         boolean usedZone;
       
   377         for (int zindex = 0; zindex < zone.size(); zindex++) {
       
   378             zrec = zone.get(zindex);
       
   379             usedZone = false;
       
   380             gmtOffset = zrec.getGmtOffset();
       
   381             int stdOffset = zrec.getDirectSave();
       
   382 
       
   383             // If this is the last zone record, take the last rule info.
       
   384             if (!zrec.hasUntil()) {
       
   385                 tz.setRawOffset(gmtOffset, fromTime);
       
   386                 if (zrec.hasRuleReference()) {
       
   387                     tz.setLastRules(zrec.getRuleRef().getLastRules());
       
   388                 } else if (stdOffset != 0) {
       
   389                     // in case the last rule is all year round DST-only
       
   390                     // (Asia/Amman once announced this rule.)
       
   391                     tz.setLastDSTSaving(stdOffset);
       
   392                 }
       
   393             }
       
   394             if (!zrec.hasRuleReference()) {
       
   395                 if (!zrec.hasUntil() || zrec.getUntilTime(stdOffset) >= fromTime) {
       
   396                     tz.addTransition(fromTime,
       
   397                                      tz.getOffsetIndex(gmtOffset+stdOffset),
       
   398                                      tz.getDstOffsetIndex(stdOffset));
       
   399                     usedZone = true;
       
   400                 }
       
   401                 currentSave = stdOffset;
       
   402                 // optimization in case the last rule is fixed.
       
   403                 if (!zrec.hasUntil()) {
       
   404                     if (tz.getNTransitions() > 0) {
       
   405                         if (stdOffset == 0) {
       
   406                             tz.setDSTType(Timezone.X_DST);
       
   407                         } else {
       
   408                             tz.setDSTType(Timezone.LAST_DST);
       
   409                         }
       
   410                         long time = Time.getLocalTime(maxYear,
       
   411                                                       Month.JANUARY, 1, 0);
       
   412                         time -= zrec.getGmtOffset();
       
   413                         tz.addTransition(time,
       
   414                                          tz.getOffsetIndex(gmtOffset+stdOffset),
       
   415                                          tz.getDstOffsetIndex(stdOffset));
       
   416                         tz.addUsedRec(zrec);
       
   417                     } else {
       
   418                         tz.setDSTType(Timezone.NO_DST);
       
   419                     }
       
   420                     break;
       
   421                 }
       
   422             } else {
       
   423                 Rule rule = zrec.getRuleRef();
       
   424                 boolean fromTimeUsed = false;
       
   425                 currentSave = 0;
       
   426             year_loop:
       
   427                 for (year = getMinYear(); year <= endYear; year++) {
       
   428                     if (zrec.hasUntil() && year > zrec.getUntilYear()) {
       
   429                         break;
       
   430                     }
       
   431                     List<RuleRec> rules = rule.getRules(year);
       
   432                     if (rules.size() > 0) {
       
   433                         for (int i = 0; i < rules.size(); i++) {
       
   434                             RuleRec rrec = rules.get(i);
       
   435                             long transition = rrec.getTransitionTime(year,
       
   436                                                                      gmtOffset,
       
   437                                                                      currentSave);
       
   438                             if (zrec.hasUntil()) {
       
   439                                 if (transition >= zrec.getUntilTime(currentSave)) {
       
   440                                     break year_loop;
       
   441                                 }
       
   442                             }
       
   443 
       
   444                             if (fromTimeUsed == false) {
       
   445                                 if (fromTime <= transition) {
       
   446                                     fromTimeUsed = true;
       
   447 
       
   448                                     if (fromTime != minTime) {
       
   449                                         int prevsave;
       
   450 
       
   451                                         ZoneRec prevzrec = zone.get(zindex - 1);
       
   452 
       
   453                                         // See if until time in the previous
       
   454                                         // ZoneRec is the same thing as the
       
   455                                         // local time in the next rule.
       
   456                                         // (examples are Asia/Ashkhabad in 1991,
       
   457                                         // Europe/Riga in 1989)
       
   458 
       
   459                                         if (i > 0) {
       
   460                                             prevsave = rules.get(i-1).getSave();
       
   461                                         } else {
       
   462                                             List<RuleRec> prevrules = rule.getRules(year-1);
       
   463 
       
   464                                             if (prevrules.size() > 0) {
       
   465                                                 prevsave = prevrules.get(prevrules.size()-1).getSave();
       
   466                                             } else {
       
   467                                                 prevsave = 0;
       
   468                                             }
       
   469                                         }
       
   470 
       
   471                                         if (rrec.isSameTransition(prevzrec, prevsave, gmtOffset)) {
       
   472                                             currentSave = rrec.getSave();
       
   473                                             tz.addTransition(fromTime,
       
   474                                                          tz.getOffsetIndex(gmtOffset+currentSave),
       
   475                                                          tz.getDstOffsetIndex(currentSave));
       
   476                                             tz.addUsedRec(rrec);
       
   477                                             usedZone = true;
       
   478                                             continue;
       
   479                                         }
       
   480                                         if (!prevzrec.hasRuleReference()
       
   481                                             || rule != prevzrec.getRuleRef()
       
   482                                             || (rule == prevzrec.getRuleRef()
       
   483                                                 && gmtOffset != prevzrec.getGmtOffset())) {
       
   484                                             int save = (fromTime == transition) ? rrec.getSave() : currentSave;
       
   485                                             tz.addTransition(fromTime,
       
   486                                                          tz.getOffsetIndex(gmtOffset+save),
       
   487                                                          tz.getDstOffsetIndex(save));
       
   488                                             tz.addUsedRec(rrec);
       
   489                                             usedZone = true;
       
   490                                         }
       
   491                                     } else {  // fromTime == minTime
       
   492                                         int save = rrec.getSave();
       
   493                                         tz.addTransition(minTime,
       
   494                                                          tz.getOffsetIndex(gmtOffset),
       
   495                                                          tz.getDstOffsetIndex(0));
       
   496 
       
   497                                         tz.addTransition(transition,
       
   498                                                          tz.getOffsetIndex(gmtOffset+save),
       
   499                                                          tz.getDstOffsetIndex(save));
       
   500 
       
   501                                         tz.addUsedRec(rrec);
       
   502                                         usedZone = true;
       
   503                                     }
       
   504                                 } else if (year == fromYear && i == rules.size()-1) {
       
   505                                     int save = rrec.getSave();
       
   506                                     tz.addTransition(fromTime,
       
   507                                                      tz.getOffsetIndex(gmtOffset+save),
       
   508                                                      tz.getDstOffsetIndex(save));
       
   509                                 }
       
   510                             }
       
   511 
       
   512                             currentSave = rrec.getSave();
       
   513                             if (fromTime < transition) {
       
   514                                 tz.addTransition(transition,
       
   515                                                  tz.getOffsetIndex(gmtOffset+currentSave),
       
   516                                                  tz.getDstOffsetIndex(currentSave));
       
   517                                 tz.addUsedRec(rrec);
       
   518                                 usedZone = true;
       
   519                             }
       
   520                         }
       
   521                     } else {
       
   522                         if (year == fromYear) {
       
   523                             tz.addTransition(fromTime,
       
   524                                              tz.getOffsetIndex(gmtOffset+currentSave),
       
   525                                              tz.getDstOffsetIndex(currentSave));
       
   526                             fromTimeUsed = true;
       
   527                         }
       
   528                         if (year == endYear && !zrec.hasUntil()) {
       
   529                             if (tz.getNTransitions() > 0) {
       
   530                                 // Assume that this Zone stopped DST
       
   531                                 tz.setDSTType(Timezone.X_DST);
       
   532                                 long time = Time.getLocalTime(maxYear, Month.JANUARY,
       
   533                                                               1, 0);
       
   534                                 time -= zrec.getGmtOffset();
       
   535                                 tz.addTransition(time,
       
   536                                                  tz.getOffsetIndex(gmtOffset),
       
   537                                                  tz.getDstOffsetIndex(0));
       
   538                                 usedZone = true;
       
   539                             } else {
       
   540                                 tz.setDSTType(Timezone.NO_DST);
       
   541                             }
       
   542                         }
       
   543                     }
       
   544                 }
       
   545             }
       
   546             if (usedZone) {
       
   547                 tz.addUsedRec(zrec);
       
   548             }
       
   549             if (zrec.hasUntil() && zrec.getUntilTime(currentSave) > fromTime) {
       
   550                 fromTime = zrec.getUntilTime(currentSave);
       
   551                 fromYear = zrec.getUntilYear();
       
   552                 year = zrec.getUntilYear();
       
   553             }
       
   554         }
       
   555 
       
   556         if (tz.getDSTType() == Timezone.UNDEF_DST) {
       
   557             tz.setDSTType(Timezone.DST);
       
   558         }
       
   559         tz.optimize();
       
   560         tz.checksum();
       
   561         return tz;
       
   562     }
       
   563 
       
   564     private static void panic(String msg) {
       
   565         Main.panic(msg);
       
   566     }
       
   567 }