Path: blob/master/test/jdk/sun/util/calendar/zi/Zoneinfo.java
41153 views
/*1* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.BufferedReader;24import java.io.FileReader;25import java.io.FileNotFoundException;26import java.io.IOException;27import java.util.HashMap;28import java.util.List;29import java.util.Map;30import java.util.StringTokenizer;3132/**33* Zoneinfo provides javazic compiler front-end functionality.34* @since 1.435*/36class Zoneinfo {3738private static final int minYear = 1900;39private static final int maxYear = 2037;40private static final long minTime = Time.getLocalTime(minYear, Month.JANUARY, 1, 0);41private static int startYear = minYear;42private static int endYear = maxYear;4344/**45* True if javazic should generate a list of SimpleTimeZone46* instances for the SimpleTimeZone-based time zone support.47*/48static boolean isYearForTimeZoneDataSpecified = false;4950/**51* Zone name to Zone mappings52*/53private Map<String,Zone> zones;5455/**56* Rule name to Rule mappings57*/58private Map<String,Rule> rules;5960/**61* Alias name to real name mappings62*/63private Map<String,String> aliases;6465/**66* Constracts a Zoneinfo.67*/68Zoneinfo() {69zones = new HashMap<String,Zone>();70rules = new HashMap<String,Rule>();71aliases = new HashMap<String,String>();72}7374/**75* Adds the given zone to the list of Zones.76* @param zone Zone to be added to the list.77*/78void add(Zone zone) {79String name = zone.getName();80zones.put(name, zone);81}8283/**84* Adds the given rule to the list of Rules.85* @param rule Rule to be added to the list.86*/87void add(Rule rule) {88String name = rule.getName();89rules.put(name, rule);90}9192/**93* Puts the specifid name pair to the alias table.94* @param name1 an alias time zone name95* @param name2 the real time zone of the alias name96*/97void putAlias(String name1, String name2) {98aliases.put(name1, name2);99}100101/**102* Sets the given year for SimpleTimeZone list output.103* This method is called when the -S option is specified.104* @param year the year for which SimpleTimeZone list should be generated105*/106static void setYear(int year) {107setStartYear(year);108setEndYear(year);109isYearForTimeZoneDataSpecified = true;110}111112/**113* Sets the start year.114* @param year the start year value115* @throws IllegalArgumentException if the specified year value is116* smaller than the minimum year or greater than the end year.117*/118static void setStartYear(int year) {119if (year < minYear || year > endYear) {120throw new IllegalArgumentException("invalid start year specified: " + year);121}122startYear = year;123}124125/**126* @return the start year value127*/128static int getStartYear() {129return startYear;130}131132/**133* Sets the end year.134* @param year the end year value135* @throws IllegalArgumentException if the specified year value is136* smaller than the start year or greater than the maximum year.137*/138static void setEndYear(int year) {139if (year < startYear || year > maxYear) {140throw new IllegalArgumentException();141}142endYear = year;143}144145/**146* @return the end year value147*/148static int getEndYear() {149return endYear;150}151152/**153* @return the minimum year value154*/155static int getMinYear() {156return minYear;157}158159/**160* @return the maximum year value161*/162static int getMaxYear() {163return maxYear;164}165166/**167* @return the alias table168*/169Map<String,String> getAliases() {170return aliases;171}172173/**174* @return the Zone list175*/176Map<String,Zone> getZones() {177return zones;178}179180/**181* @return a Zone specified by name.182* @param name a zone name183*/184Zone getZone(String name) {185return zones.get(name);186}187188/**189* @return a Rule specified by name.190* @param name a rule name191*/192Rule getRule(String name) {193return rules.get(name);194}195196private static String line;197198private static int lineNum;199200/**201* Parses the specified time zone data file and creates a Zoneinfo202* that has all Rules, Zones and Links (aliases) information.203* @param fname the time zone data file name204* @return a Zoneinfo object205*/206static Zoneinfo parse(String fname) {207BufferedReader in = null;208try {209FileReader fr = new FileReader(fname);210in = new BufferedReader(fr);211} catch (FileNotFoundException e) {212panic("can't open file: "+fname);213}214Zoneinfo zi = new Zoneinfo();215boolean continued = false;216Zone zone = null;217String l;218lineNum = 0;219220try {221while ((line = in.readLine()) != null) {222lineNum++;223// skip blank and comment lines224if (line.length() == 0 || line.charAt(0) == '#') {225continue;226}227228// trim trailing comments229int rindex = line.lastIndexOf('#');230if (rindex != -1) {231// take the data part of the line232l = line.substring(0, rindex);233} else {234l = line;235}236237StringTokenizer tokens = new StringTokenizer(l);238if (!tokens.hasMoreTokens()) {239continue;240}241String token = tokens.nextToken();242243if (continued || "Zone".equals(token)) {244if (zone == null) {245if (!tokens.hasMoreTokens()) {246panic("syntax error: zone no more token");247}248token = tokens.nextToken();249// if the zone name is in "GMT+hh" or "GMT-hh"250// format, ignore it due to spec conflict.251if (token.startsWith("GMT+") || token.startsWith("GMT-")) {252continue;253}254zone = new Zone(token);255} else {256// no way to push the current token back...257tokens = new StringTokenizer(l);258}259260ZoneRec zrec = ZoneRec.parse(tokens);261zrec.setLine(line);262zone.add(zrec);263if ((continued = zrec.hasUntil()) == false) {264if (Zone.isTargetZone(zone.getName())) {265// zone.resolve(zi);266zi.add(zone);267}268zone = null;269}270} else if ("Rule".equals(token)) {271if (!tokens.hasMoreTokens()) {272panic("syntax error: rule no more token");273}274token = tokens.nextToken();275Rule rule = zi.getRule(token);276if (rule == null) {277rule = new Rule(token);278zi.add(rule);279}280RuleRec rrec = RuleRec.parse(tokens);281rrec.setLine(line);282rule.add(rrec);283} else if ("Link".equals(token)) {284// Link <newname> <oldname>285try {286String name1 = tokens.nextToken();287String name2 = tokens.nextToken();288289// if the zone name is in "GMT+hh" or "GMT-hh"290// format, ignore it due to spec conflict with291// custom time zones. Also, ignore "ROC" for292// PC-ness.293if (name2.startsWith("GMT+") || name2.startsWith("GMT-")294|| "ROC".equals(name2)) {295continue;296}297zi.putAlias(name2, name1);298} catch (Exception e) {299panic("syntax error: no more token for Link");300}301}302}303in.close();304} catch (IOException ex) {305panic("IO error: " + ex.getMessage());306}307308return zi;309}310311/**312* Interprets a zone and constructs a Timezone object that313* contains enough information on GMT offsets and DST schedules to314* generate a zone info database.315*316* @param zoneName the zone name for which a Timezone object is317* constructed.318*319* @return a Timezone object that contains all GMT offsets and DST320* rules information.321*/322Timezone phase2(String zoneName) {323Timezone tz = new Timezone(zoneName);324Zone zone = getZone(zoneName);325zone.resolve(this);326327// TODO: merge phase2's for the regular and SimpleTimeZone ones.328if (isYearForTimeZoneDataSpecified) {329ZoneRec zrec = zone.get(zone.size()-1);330tz.setLastZoneRec(zrec);331tz.setRawOffset(zrec.getGmtOffset());332if (zrec.hasRuleReference()) {333/*334* This part assumes that the specified year is covered by335* the rules referred to by the last zone record.336*/337List<RuleRec> rrecs = zrec.getRuleRef().getRules(startYear);338339if (rrecs.size() == 2) {340// make sure that one is a start rule and the other is341// an end rule.342RuleRec r0 = rrecs.get(0);343RuleRec r1 = rrecs.get(1);344if (r0.getSave() == 0 && r1.getSave() > 0) {345rrecs.set(0, r1);346rrecs.set(1, r0);347} else if (!(r0.getSave() > 0 && r1.getSave() == 0)) {348rrecs = null;349Main.error(zoneName + ": rules for " + startYear + " not found.");350}351} else {352rrecs = null;353}354if (rrecs != null) {355tz.setLastRules(rrecs);356}357}358return tz;359}360361int gmtOffset;362int year = minYear;363int fromYear = year;364long fromTime = Time.getLocalTime(startYear,365Month.JANUARY,3661, 0);367368// take the index 0 for the GMT offset of the last zone record369ZoneRec zrec = zone.get(zone.size()-1);370tz.getOffsetIndex(zrec.getGmtOffset());371372int lastGmtOffsetValue = -1;373ZoneRec prevzrec = null;374int currentSave = 0;375boolean usedZone;376for (int zindex = 0; zindex < zone.size(); zindex++) {377zrec = zone.get(zindex);378usedZone = false;379gmtOffset = zrec.getGmtOffset();380int stdOffset = zrec.getDirectSave();381382if (gmtOffset != lastGmtOffsetValue) {383tz.setRawOffset(gmtOffset, fromTime);384lastGmtOffsetValue = gmtOffset;385}386// If this is the last zone record, take the last rule info.387if (!zrec.hasUntil()) {388if (zrec.hasRuleReference()) {389tz.setLastRules(zrec.getRuleRef().getLastRules());390} else if (stdOffset != 0) {391// in case the last rule is all year round DST-only392// (Asia/Amman once announced this rule.)393tz.setLastDSTSaving(stdOffset);394}395}396if (!zrec.hasRuleReference()) {397if (!zrec.hasUntil() || zrec.getUntilTime(stdOffset) >= fromTime) {398tz.addTransition(fromTime,399tz.getOffsetIndex(gmtOffset+stdOffset),400tz.getDstOffsetIndex(stdOffset));401usedZone = true;402}403currentSave = stdOffset;404// optimization in case the last rule is fixed.405if (!zrec.hasUntil()) {406if (tz.getNTransitions() > 0) {407if (stdOffset == 0) {408tz.setDSTType(Timezone.X_DST);409} else {410tz.setDSTType(Timezone.LAST_DST);411}412long time = Time.getLocalTime(maxYear,413Month.JANUARY, 1, 0);414time -= zrec.getGmtOffset();415tz.addTransition(time,416tz.getOffsetIndex(gmtOffset+stdOffset),417tz.getDstOffsetIndex(stdOffset));418tz.addUsedRec(zrec);419} else {420tz.setDSTType(Timezone.NO_DST);421}422break;423}424} else {425Rule rule = zrec.getRuleRef();426boolean fromTimeUsed = false;427currentSave = 0;428year_loop:429for (year = getMinYear(); year <= endYear; year++) {430if (zrec.hasUntil() && year > zrec.getUntilYear()) {431break;432}433List<RuleRec> rules = rule.getRules(year);434if (rules.size() > 0) {435for (int i = 0; i < rules.size(); i++) {436RuleRec rrec = rules.get(i);437long transition = rrec.getTransitionTime(year,438gmtOffset,439currentSave);440if (zrec.hasUntil()) {441if (transition >= zrec.getUntilTime(currentSave)) {442// If the GMT offset changed from the previous one,443// record fromTime as a transition.444if (!fromTimeUsed && prevzrec != null445&& gmtOffset != prevzrec.getGmtOffset()) {446tz.addTransition(fromTime,447tz.getOffsetIndex(gmtOffset+currentSave),448tz.getDstOffsetIndex(currentSave));449fromTimeUsed = true; // for consistency450}451break year_loop;452}453}454455if (fromTimeUsed == false) {456if (fromTime <= transition) {457fromTimeUsed = true;458459if (fromTime != minTime) {460int prevsave;461462// See if until time in the previous463// ZoneRec is the same thing as the464// local time in the next rule.465// (examples are Asia/Ashkhabad in 1991,466// Europe/Riga in 1989)467468if (i > 0) {469prevsave = rules.get(i-1).getSave();470} else {471List<RuleRec> prevrules = rule.getRules(year-1);472473if (prevrules.size() > 0) {474prevsave = prevrules.get(prevrules.size()-1).getSave();475} else {476prevsave = 0;477}478}479480if (rrec.isSameTransition(prevzrec, prevsave, gmtOffset)) {481currentSave = rrec.getSave();482tz.addTransition(fromTime,483tz.getOffsetIndex(gmtOffset+currentSave),484tz.getDstOffsetIndex(currentSave));485tz.addUsedRec(rrec);486usedZone = true;487continue;488}489if (!prevzrec.hasRuleReference()490|| rule != prevzrec.getRuleRef()491|| (rule == prevzrec.getRuleRef()492&& gmtOffset != prevzrec.getGmtOffset())) {493int save = (fromTime == transition) ? rrec.getSave() : currentSave;494tz.addTransition(fromTime,495tz.getOffsetIndex(gmtOffset+save),496tz.getDstOffsetIndex(save));497tz.addUsedRec(rrec);498usedZone = true;499}500} else { // fromTime == minTime501int save = rrec.getSave();502tz.addTransition(minTime,503tz.getOffsetIndex(gmtOffset),504tz.getDstOffsetIndex(0));505506tz.addTransition(transition,507tz.getOffsetIndex(gmtOffset+save),508tz.getDstOffsetIndex(save));509510tz.addUsedRec(rrec);511usedZone = true;512}513} else if (year == fromYear && i == rules.size()-1) {514int save = rrec.getSave();515tz.addTransition(fromTime,516tz.getOffsetIndex(gmtOffset+save),517tz.getDstOffsetIndex(save));518}519}520521currentSave = rrec.getSave();522if (fromTime < transition) {523tz.addTransition(transition,524tz.getOffsetIndex(gmtOffset+currentSave),525tz.getDstOffsetIndex(currentSave));526tz.addUsedRec(rrec);527usedZone = true;528}529}530} else {531if (year == fromYear) {532tz.addTransition(fromTime,533tz.getOffsetIndex(gmtOffset+currentSave),534tz.getDstOffsetIndex(currentSave));535fromTimeUsed = true;536}537if (year == endYear && !zrec.hasUntil()) {538if (tz.getNTransitions() > 0) {539// Assume that this Zone stopped DST540tz.setDSTType(Timezone.X_DST);541long time = Time.getLocalTime(maxYear, Month.JANUARY,5421, 0);543time -= zrec.getGmtOffset();544tz.addTransition(time,545tz.getOffsetIndex(gmtOffset),546tz.getDstOffsetIndex(0));547usedZone = true;548} else {549tz.setDSTType(Timezone.NO_DST);550}551}552}553}554}555if (usedZone) {556tz.addUsedRec(zrec);557}558if (zrec.hasUntil() && zrec.getUntilTime(currentSave) > fromTime) {559fromTime = zrec.getUntilTime(currentSave);560fromYear = zrec.getUntilYear();561year = zrec.getUntilYear();562}563prevzrec = zrec;564}565566if (tz.getDSTType() == Timezone.UNDEF_DST) {567tz.setDSTType(Timezone.DST);568}569tz.optimize();570tz.checksum();571return tz;572}573574private static void panic(String msg) {575Main.panic(msg);576}577}578579580