001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.date;
020
021import java.util.HashMap;
022import java.util.Map;
023import java.util.TimeZone;
024
025import lucee.commons.lang.StringUtil;
026import lucee.runtime.exp.ExpressionException;
027import lucee.runtime.op.Caster;
028import lucee.runtime.type.util.ListUtil;
029
030public class TimeZoneUtil {
031
032        private static final Map<String,TimeZone> IDS=new HashMap<String,TimeZone>();
033        
034        static {
035                String[] ids=TimeZone.getAvailableIDs();
036                for(int i=0;i<ids.length;i++){
037                        IDS.put(ids[i].toLowerCase(), TimeZone.getTimeZone(ids[i]));
038                }
039                TimeZone def = TimeZone.getDefault();
040                if(def!=null)IDS.put(def.getID(),def);
041                IDS.put("jvm", TimeZone.getDefault());
042                IDS.put("default", TimeZone.getDefault());
043                IDS.put("", TimeZone.getDefault());
044
045                // MS specific Timezone definions
046                set("Dateline Standard Time",TimeZoneConstants.ETC_GMT_PLUS_12); // (GMT-12:00) International Date Line West
047                set("Samoa Standard Time",TimeZoneConstants.PACIFIC_MIDWAY); //         (GMT-11:00) Midway Island, Samoa
048                set("Hawaiian Standard Time",TimeZoneConstants.HST); // (GMT-10:00) Hawaii
049                set("Alaskan Standard Time",TimeZoneConstants.AST); // (GMT-09:00) Alaska
050                set("Pacific Standard Time",TimeZoneConstants.PST); // (GMT-08:00) Pacific Time (US and Canada); Tijuana
051                set("Mountain Standard Time",TimeZoneConstants.MST); // (GMT-07:00) Mountain Time (US and Canada)
052                set("Mexico Standard Time",TimeZoneConstants.MEXICO_GENERAL); // (GMT-06:00) Guadalajara, Mexico City, Monterrey
053                set("Mexico Standard Time 2",TimeZoneConstants.AMERICA_CHIHUAHUA); // (GMT-07:00) Chihuahua, La Paz, Mazatlan
054                set("U.S. Mountain Standard Time",TimeZoneConstants.MST); // (GMT-07:00) Arizona
055                set("Central Standard Time",TimeZoneConstants.CST); // (GMT-06:00) Central Time (US and Canada
056                set("Canada Central Standard Time",TimeZoneConstants.CANADA_CENTRAL); // (GMT-06:00) Saskatchewan
057                set("Central America Standard Time",TimeZoneConstants.CST); // (GMT-06:00) Central America
058                set("Eastern Standard Time",TimeZoneConstants.EST); // (GMT-05:00) Eastern Time (US and Canada)
059                set("U.S. Eastern Standard Time",TimeZoneConstants.EST); // (GMT-05:00) Indiana (East)
060                set("S.A. Pacific Standard Time",TimeZoneConstants.AMERICA_BOGOTA); // (GMT-05:00) Bogota, Lima, Quito
061                set("Atlantic Standard Time",TimeZoneConstants.CANADA_ATLANTIC); // (GMT-04:00) Atlantic Time (Canada)
062                set("S.A. Western Standard Time",TimeZoneConstants.AMERICA_ANTIGUA); // (GMT-04:00) Caracas, La Paz
063                set("Pacific S.A. Standard Time",TimeZoneConstants.AMERICA_SANTIAGO); // (GMT-04:00) Santiago
064                set("Newfoundland and Labrador Standard Time",TimeZoneConstants.CNT); // (GMT-03:30) Newfoundland and Labrador
065                set("E. South America Standard Time",TimeZoneConstants.BET); // (GMT-03:00) Brasilia
066                set("S.A. Eastern Standard Time",TimeZoneConstants.AMERICA_ARGENTINA_BUENOS_AIRES); // (GMT-03:00) Buenos Aires, Georgetown
067                set("Greenland Standard Time",TimeZoneConstants.AMERICA_GODTHAB); // (GMT-03:00) Greenland
068                set("Mid-Atlantic Standard Time",TimeZoneConstants.AMERICA_NORONHA); // (GMT-02:00) Mid-Atlantic
069                set("Azores Standard Time",TimeZoneConstants.ATLANTIC_AZORES); // (GMT-01:00) Azores
070                set("Cape Verde Standard Time",TimeZoneConstants.ATLANTIC_CAPE_VERDE); // (GMT-01:00) Cape Verde Islands
071                set("Central Europe Standard Time",TimeZoneConstants.CET); // (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
072                set("Central European Standard Time",TimeZoneConstants.CET); // (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
073                set("Romance Standard Time",TimeZoneConstants.EUROPE_BRUSSELS); // (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
074                set("W. Europe Standard Time",TimeZoneConstants.CET); // (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
075                set("W. Central Africa Standard Time",null); // (GMT+01:00) West Central Africa
076                set("E. Europe Standard Time",TimeZoneConstants.ART); // (GMT+02:00) Bucharest
077                set("Egypt Standard Time",TimeZoneConstants.EGYPT); // (GMT+02:00) Cairo
078                set("FLE Standard Time",TimeZoneConstants.EET); // (GMT+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius
079                set("GTB Standard Time",TimeZoneConstants.EUROPE_ATHENS); // (GMT+02:00) Athens, Istanbul, Minsk
080                set("Israel Standard Time",TimeZoneConstants.ASIA_JERUSALEM); // (GMT+02:00) Jerusalem
081                set("South Africa Standard Time",TimeZoneConstants.AFRICA_JOHANNESBURG); // (GMT+02:00) Harare, Pretoria
082                set("Russian Standard Time",TimeZoneConstants.EUROPE_MOSCOW); // (GMT+03:00) Moscow, St. Petersburg, Volgograd
083                set("Arab Standard Time",TimeZoneConstants.ASIA_KUWAIT); // (GMT+03:00) Kuwait, Riyadh
084                set("E. Africa Standard Time",TimeZoneConstants.AFRICA_NAIROBI); // (GMT+03:00) Nairobi
085                set("Arabic Standard Time",TimeZoneConstants.ASIA_BAGHDAD); // (GMT+03:00) Baghdad
086                set("Iran Standard Time",TimeZoneConstants.ASIA_TEHRAN); // (GMT+03:30) Tehran
087                set("Arabian Standard Time",TimeZoneConstants.ASIA_MUSCAT); // (GMT+04:00) Abu Dhabi, Muscat
088                set("Caucasus Standard Time",TimeZoneConstants.ASIA_YEREVAN); // (GMT+04:00) Baku, Tbilisi, Yerevan
089                set("Transitional Islamic State of Afghanistan Standard Time",TimeZoneConstants.ASIA_KABUL); // (GMT+04:30) Kabul
090                set("Ekaterinburg Standard Time",TimeZoneConstants.ASIA_YEKATERINBURG); // (GMT+05:00) Ekaterinburg
091                set("West Asia Standard Time",TimeZoneConstants.ASIA_KARACHI); // (GMT+05:00) Islamabad, Karachi, Tashkent
092                set("India Standard Time",TimeZoneConstants.IST); // (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi
093                set("Nepal Standard Time",TimeZoneConstants.ASIA_KATMANDU); // (GMT+05:45) Kathmandu
094                set("Central Asia Standard Time",TimeZoneConstants.ASIA_DHAKA); //(GMT+06:00) Astana, Dhaka 
095                set("Sri Lanka Standard Time",TimeZoneConstants.ASIA_COLOMBO); // (GMT+06:00) Sri Jayawardenepura
096                set("N. Central Asia Standard Time",TimeZoneConstants.ASIA_ALMATY); // (GMT+06:00) Almaty, Novosibirsk
097                set("Myanmar Standard Time",TimeZoneConstants.ASIA_RANGOON); // (GMT+06:30) Yangon Rangoon
098                set("S.E. Asia Standard Time",TimeZoneConstants.ASIA_BANGKOK); // (GMT+07:00) Bangkok, Hanoi, Jakarta
099                set("North Asia Standard Time",TimeZoneConstants.ASIA_KRASNOYARSK); // (GMT+07:00) Krasnoyarsk
100                set("China Standard Time",TimeZoneConstants.CTT); // (GMT+08:00) Beijing, Chongqing, Hong Kong SAR, Urumqi
101                set("Singapore Standard Time",TimeZoneConstants.ASIA_SINGAPORE); // (GMT+08:00) Kuala Lumpur, Singapore
102                set("Taipei Standard Time",TimeZoneConstants.ASIA_TAIPEI); // (GMT+08:00) Taipei
103                set("W. Australia Standard Time",TimeZoneConstants.AUSTRALIA_PERTH); // (GMT+08:00) Perth
104                set("North Asia East Standard Time",TimeZoneConstants.ASIA_IRKUTSK); // (GMT+08:00) Irkutsk, Ulaanbaatar
105                set("Korea Standard Time",TimeZoneConstants.ASIA_SEOUL); // (GMT+09:00) Seoul
106                set("Tokyo Standard Time",TimeZoneConstants.ASIA_TOKYO); // (GMT+09:00) Osaka, Sapporo, Tokyo
107                set("Yakutsk Standard Time",TimeZoneConstants.ASIA_YAKUTSK); // (GMT+09:00) Yakutsk
108                set("A.U.S. Central Standard Time",TimeZoneConstants.ACT); // (GMT+09:30) Darwin
109                set("Cen. Australia Standard Time",TimeZoneConstants.ACT); // (GMT+09:30) Adelaide
110                set("A.U.S. Eastern Standard Time",TimeZoneConstants.AET); // (GMT+10:00) Canberra, Melbourne, Sydney
111                set("E. Australia Standard Time",TimeZoneConstants.AET); // (GMT+10:00) Brisbane
112                set("Tasmania Standard Time",TimeZoneConstants.AUSTRALIA_TASMANIA); // (GMT+10:00) Hobart
113                set("Vladivostok Standard Time",TimeZoneConstants.ASIA_VLADIVOSTOK); // (GMT+10:00) Vladivostok
114                set("West Pacific Standard Time",TimeZoneConstants.PACIFIC_GUAM); // (GMT+10:00) Guam, Port Moresby
115                set("Central Pacific Standard Time",TimeZoneConstants.ASIA_MAGADAN); // (GMT+11:00) Magadan, Solomon Islands, New Caledonia
116                set("Fiji Islands Standard Time",TimeZoneConstants.PACIFIC_FIJI); // (GMT+12:00) Fiji Islands, Kamchatka, Marshall Islands
117                set("New Zealand Standard Time",TimeZoneConstants.NZ); // (GMT+12:00) Auckland, Wellington
118                set("Tonga Standard Time",TimeZoneConstants.PACIFIC_TONGATAPU); // (GMT+13:00) Nuku'alofa
119                
120                
121        }
122        
123        private static void set(String name, TimeZone tz) {
124                if(tz==null) return;
125                name=StringUtil.replace(name.trim().toLowerCase(), " ", "", false);
126                IDS.put(name.toLowerCase(), tz);
127        }
128
129        /**
130         * return the string format of the Timezone
131         * @param timezone
132         * @return
133         */
134        public static String toString(TimeZone timezone){
135                return timezone.getID();
136        }
137
138        private static String getSupportedTimeZonesAsString() {
139                return ListUtil.arrayToList(TimeZone.getAvailableIDs(),", ");
140        }
141        
142        /**
143         * translate timezone string format to a timezone
144         * @param strTimezone
145         * @return
146         */
147        public static TimeZone toTimeZone(String strTimezone,TimeZone defaultValue){
148                if(strTimezone==null) return defaultValue;
149                strTimezone=StringUtil.replace(strTimezone.trim().toLowerCase(), " ", "", false);
150                TimeZone tz = IDS.get(strTimezone);
151                if(tz!=null) return tz;
152                
153                //parse GMT followd by a number
154                float gmtOffset=Float.NaN;
155                if(strTimezone.startsWith("gmt")) gmtOffset=getGMTOffset(strTimezone.substring(3).trim(),Float.NaN);
156                else if(strTimezone.startsWith("etc/gmt")) gmtOffset=getGMTOffset(strTimezone.substring(7).trim(),Float.NaN);
157                else if(strTimezone.startsWith("utc")) gmtOffset=getGMTOffset(strTimezone.substring(3).trim(),Float.NaN);
158                else if(strTimezone.startsWith("etc/utc")) gmtOffset=getGMTOffset(strTimezone.substring(7).trim(),Float.NaN);
159                
160
161                
162                if(!Float.isNaN(gmtOffset)) {
163                        strTimezone="etc/gmt"+(gmtOffset>=0?"+":"")+Caster.toString(gmtOffset);
164                        tz =  IDS.get(strTimezone);
165                        if(tz!=null) return tz;
166                        
167                }
168                
169                
170                
171                
172                return defaultValue;
173        }
174        
175        private static float getGMTOffset(String str, float defaultValue) {
176                int index;
177                String left=null,right=null;
178                if((index=str.indexOf(':'))!=-1) {
179                        left = str.substring(0,index);
180                        right=str.substring(index+1);
181                }
182                else if(str.startsWith("-")) {
183                        if(str.length()>=4 && str.indexOf('.')==-1){
184                                left = str.substring(0,str.length()-2);
185                                right=str.substring(str.length()-2);
186                        }
187                }
188                else if(str.length()>=3 && str.indexOf('.')==-1) {
189                        left = str.substring(0,str.length()-2);
190                        right=str.substring(str.length()-2);
191                }
192                if(left!=null) {
193                        int l = Caster.toIntValue(left,Integer.MIN_VALUE);
194                        int r = Caster.toIntValue(right,Integer.MIN_VALUE);
195                        if(l==Integer.MIN_VALUE || r==Integer.MIN_VALUE || r>59) return defaultValue;
196                        return l+(r/60f);
197                }
198                
199                
200                float f=Caster.toFloatValue(str,Float.NaN);
201                if(Float.isNaN(f)) return defaultValue;
202                return f;
203        }
204
205        public static TimeZone toTimeZone(String strTimezone) throws ExpressionException{
206                TimeZone tz = toTimeZone(strTimezone, null);
207                if(tz!=null) return tz;
208                throw new ExpressionException("can't cast value ("+strTimezone+") to a TimeZone","supported TimeZones are:"+getSupportedTimeZonesAsString());
209        }
210}