package com.ibm.xsp.teamroom.timezone; import java.util.Arrays; import java.util.Comparator; import java.util.Locale; import javax.faces.model.SelectItem; public class TimeZoneUtil { private static String[][] codeToNonNLSLabel_unsorted = new String[][]{ { "Z=12$DO=0$ZX=154$ZN=Dateline", "(GMT-12:00) International Date Line West"}, { "Z=11$DO=1$DL=9 -1 7 1 1 6$ZX=203$ZN=Samoa", "(GMT-11:00) Samoa"}, { "Z=11$DO=0$ZX=219$ZN=UTC-11", "(GMT-11:00) Coordinated Universal Time-11"}, { "Z=10$DO=0$ZX=169$ZN=Hawaiian", "(GMT-10:00) Hawaii"}, { "Z=9$DO=1$DL=3 2 1 11 1 1$ZX=129$ZN=Alaskan", "(GMT-09:00) Alaska"}, { "Z=8$DO=1$DL=3 2 1 11 1 1$ZX=194$ZN=Pacific", "(GMT-08:00) Pacific Time (US & Canada)"}, { "Z=8$DO=1$DL=4 1 1 10 -1 1$ZX=195$ZN=Pacific Standard Time (Mexico)", "(GMT-08:00) Baja California"}, { "Z=7$DO=1$DL=4 1 1 10 -1 1$ZX=178$ZN=Mexico Standard Time 2", "(GMT-07:00) Chihuahua, La Paz, Mazatlan - Old"}, { "Z=7$DO=1$DL=3 2 1 11 1 1$ZX=183$ZN=Mountain", "(GMT-07:00) Mountain Time (US & Canada)"}, { "Z=7$DO=1$DL=4 1 1 10 -1 1$ZX=184$ZN=Mountain Standard Time (Mexico)", "(GMT-07:00) Chihuahua, La Paz, Mazatlan - New"}, { "Z=7$DO=0$ZX=215$ZN=US Mountain", "(GMT-07:00) Arizona"}, { "Z=6$DO=0$ZX=141$ZN=Canada Central", "(GMT-06:00) Saskatchewan"}, { "Z=6$DO=0$ZX=145$ZN=Central America", "(GMT-06:00) Central America"}, { "Z=6$DO=1$DL=3 2 1 11 1 1$ZX=151$ZN=Central", "(GMT-06:00) Central Time (US & Canada)"}, { "Z=6$DO=1$DL=4 1 1 10 -1 1$ZX=152$ZN=Central Standard Time (Mexico)", "(GMT-06:00) Guadalajara, Mexico City, Monterrey - New"}, { "Z=6$DO=1$DL=4 1 1 10 -1 1$ZX=177$ZN=Mexico", "(GMT-06:00) Guadalajara, Mexico City, Monterrey - Old"}, { "Z=5$DO=1$DL=3 2 1 11 1 1$ZX=159$ZN=Eastern", "(GMT-05:00) Eastern Time (US & Canada)"}, { "Z=5$DO=0$ZX=201$ZN=SA Pacific", "(GMT-05:00) Bogota, Lima, Quito"}, { "Z=5$DO=1$DL=3 2 1 11 1 1$ZX=214$ZN=US Eastern", "(GMT-05:00) Indiana (East)"}, { "Z=3004$DO=0$ZX=220$ZN=Venezuela", "(GMT-04:30) Caracas"}, { "Z=4$DO=1$DL=3 2 1 11 1 1$ZX=135$ZN=Atlantic", "(GMT-04:00) Atlantic Time (Canada)"}, { "Z=4$DO=1$DL=10 3 7 2 3 7$ZX=147$ZN=Central Brazilian", "(GMT-04:00) Cuiaba"}, { "Z=4$DO=1$DL=10 2 7 4 1 7$ZX=193$ZN=Pacific SA", "(GMT-04:00) Santiago"}, { "Z=4$DO=1$DL=10 1 7 4 2 7$ZX=197$ZN=Paraguay", "(GMT-04:00) Asuncion"}, { "Z=4$DO=0$ZX=202$ZN=SA Western", "(GMT-04:00) Georgetown, La Paz, Manaus, San Juan"}, { "Z=3003$DO=1$DL=3 2 1 11 1 1$ZX=190$ZN=Newfoundland", "(GMT-03:30) Newfoundland"}, { "Z=3$DO=0$ZX=133$ZN=Argentina", "(GMT-03:00) Buenos Aires"}, { "Z=3$DO=1$DL=10 3 7 2 3 7$ZX=158$ZN=E. South America", "(GMT-03:00) Brasilia"}, { "Z=3$DO=1$DL=3 -1 7 10 -1 7$ZX=166$ZN=Greenland", "(GMT-03:00) Greenland"}, { "Z=3$DO=1$DL=10 1 1 3 2 1$ZX=181$ZN=Montevideo", "(GMT-03:00) Montevideo"}, { "Z=3$DO=0$ZX=200$ZN=SA Eastern", "(GMT-03:00) Cayenne, Fortaleza"}, { "Z=2$DO=1$DL=3 -1 1 9 -1 1$ZX=179$ZN=Mid-Atlantic", "(GMT-02:00) Mid-Atlantic"}, { "Z=2$DO=0$ZX=218$ZN=UTC-02", "(GMT-02:00) Coordinated Universal Time-02"}, { "Z=1$DO=1$DL=3 -1 1 10 -1 1$ZX=139$ZN=Azores", "(GMT-01:00) Azores"}, { "Z=1$DO=0$ZX=142$ZN=Cape Verde", "(GMT-01:00) Cape Verde Is."}, { "Z=0$DO=1$DL=3 -1 1 10 -1 1$ZX=165$ZN=GMT", "(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London"}, { "Z=0$DO=0$ZX=167$ZN=Greenwich", "(GMT) Monrovia, Reykjavik"}, { "Z=0$DO=1$DL=5 1 7 8 1 7$ZX=182$ZN=Morocco", "(GMT) Casablanca"}, { "Z=0$DO=0$ZX=216$ZN=UTC", "(GMT) Coordinated Universal Time"}, { "Z=-1$DO=1$DL=3 -1 1 10 -1 1$ZX=148$ZN=Central Europe", "(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague"}, { "Z=-1$DO=1$DL=3 -1 1 10 -1 1$ZX=149$ZN=Central European", "(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb"}, { "Z=-1$DO=1$DL=3 -1 1 10 -1 1$ZX=198$ZN=Romance", "(GMT+01:00) Brussels, Copenhagen, Madrid, Paris"}, { "Z=-1$DO=0$ZX=223$ZN=W. Central Africa", "(GMT+01:00) West Central Africa"}, { "Z=-1$DO=1$DL=3 -1 1 10 -1 1$ZX=224$ZN=W. Europe", "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"}, { "Z=-2$DO=1$DL=3 -1 1 10 -1 1$ZX=157$ZN=E. Europe", "(GMT+02:00) Minsk"}, { "Z=-2$DO=1$DL=4 -1 5 9 -1 5$ZX=160$ZN=Egypt", "(GMT+02:00) Cairo"}, { "Z=-2$DO=1$DL=3 -1 1 10 -1 1$ZX=163$ZN=FLE", "(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius"}, { "Z=-2$DO=1$DL=3 -1 1 10 -1 1$ZX=168$ZN=GTB", "(GMT+02:00) Athens, Bucharest, Istanbul"}, { "Z=-2$DO=1$DL=3 -1 6 9 2 1$ZX=172$ZN=Israel", "(GMT+02:00) Jerusalem"}, { "Z=-2$DO=1$DL=3 -1 5 10 -1 6$ZX=173$ZN=Jordan", "(GMT+02:00) Amman"}, { "Z=-2$DO=1$DL=3 -1 7 10 -1 7$ZX=180$ZN=Middle East", "(GMT+02:00) Beirut"}, { "Z=-2$DO=1$DL=4 1 1 9 1 1$ZX=187$ZN=Namibia", "(GMT+01:00) Windhoek"}, { "Z=-2$DO=0$ZX=206$ZN=South Africa", "(GMT+02:00) Harare, Pretoria"}, { "Z=-2$DO=1$DL=4 1 5 10 -1 5$ZX=208$ZN=Syria", "(GMT+02:00) Damascus"}, { "Z=-3$DO=0$ZX=130$ZN=Arab", "(GMT+03:00) Kuwait, Riyadh"}, { "Z=-3$DO=0$ZX=132$ZN=Arabic", "(GMT+03:00) Baghdad"}, { "Z=-3$DO=0$ZX=155$ZN=E. Africa", "(GMT+03:00) Nairobi"}, { "Z=-3$DO=1$DL=3 -1 1 10 -1 1$ZX=199$ZN=Russian", "(GMT+03:00) Moscow, St. Petersburg, Volgograd"}, { "Z=-3003$DO=1$DL=3 3 7 9 3 2$ZX=171$ZN=Iran", "(GMT+03:30) Tehran"}, { "Z=-4$DO=0$ZX=131$ZN=Arabian", "(GMT+04:00) Abu Dhabi, Muscat"}, { "Z=-4$DO=1$DL=3 -1 1 10 -1 1$ZX=134$ZN=Armenian", "(GMT+04:00) Yerevan"}, { "Z=-4$DO=1$DL=3 -1 1 10 -1 1$ZX=138$ZN=Azerbaijan", "(GMT+04:00) Baku"}, { "Z=-4$DO=0$ZX=143$ZN=Caucasus", "(GMT+04:00) Caucasus Standard Time"}, { "Z=-4$DO=0$ZX=164$ZN=Georgian", "(GMT+04:00) Tbilisi"}, { "Z=-4$DO=0$ZX=176$ZN=Mauritius", "(GMT+04:00) Port Louis"}, { "Z=-3004$DO=0$ZX=128$ZN=Afghanistan", "(GMT+04:30) Kabul"}, { "Z=-5$DO=1$DL=3 -1 1 10 -1 1$ZX=161$ZN=Ekaterinburg", "(GMT+05:00) Ekaterinburg"}, { "Z=-5$DO=0$ZX=196$ZN=Pakistan", "(GMT+05:00) Islamabad, Karachi"}, { "Z=-5$DO=0$ZX=225$ZN=West Asia", "(GMT+05:00) Tashkent"}, { "Z=-3005$DO=0$ZX=170$ZN=India", "(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi"}, { "Z=-3005$DO=0$ZX=207$ZN=Sri Lanka", "(GMT+05:30) Sri Jayawardenepura"}, { "Z=-4505$DO=0$ZX=188$ZN=Nepal", "(GMT+05:45) Kathmandu"}, { "Z=-6$DO=0$ZX=140$ZN=Bangladesh", "(GMT+06:00) Dhaka"}, { "Z=-6$DO=0$ZX=146$ZN=Central Asia", "(GMT+06:00) Astana"}, { "Z=-6$DO=1$DL=3 -1 1 10 -1 1$ZX=186$ZN=N. Central Asia", "(GMT+06:00) Novosibirsk"}, { "Z=-3006$DO=0$ZX=185$ZN=Myanmar", "(GMT+06:30) Yangon (Rangoon)"}, { "Z=-7$DO=1$DL=3 -1 1 10 -1 1$ZX=192$ZN=North Asia", "(GMT+07:00) Krasnoyarsk"}, { "Z=-7$DO=0$ZX=204$ZN=SE Asia", "(GMT+07:00) Bangkok, Hanoi, Jakarta"}, { "Z=-8$DO=0$ZX=153$ZN=China", "(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi"}, { "Z=-8$DO=1$DL=3 -1 1 10 -1 1$ZX=191$ZN=North Asia East", "(GMT+08:00) Irkutsk"}, { "Z=-8$DO=0$ZX=205$ZN=Singapore", "(GMT+08:00) Kuala Lumpur, Singapore"}, { "Z=-8$DO=0$ZX=209$ZN=Taipei", "(GMT+08:00) Taipei"}, { "Z=-8$DO=0$ZX=213$ZN=Ulaanbaatar", "(GMT+08:00) Ulaanbaatar"}, { "Z=-8$DO=0$ZX=222$ZN=W. Australia", "(GMT+08:00) Perth"}, { "Z=-9$DO=0$ZX=175$ZN=Korea", "(GMT+09:00) Seoul"}, { "Z=-9$DO=0$ZX=211$ZN=Tokyo", "(GMT+09:00) Osaka, Sapporo, Tokyo"}, { "Z=-9$DO=1$DL=3 -1 1 10 -1 1$ZX=227$ZN=Yakutsk", "(GMT+09:00) Yakutsk"}, { "Z=-3009$DO=0$ZX=136$ZN=AUS Central", "(GMT+09:30) Darwin"}, { "Z=-3009$DO=1$DL=10 1 1 4 1 1$ZX=144$ZN=Cen. Australia", "(GMT+09:30) Adelaide"}, { "Z=-10$DO=1$DL=10 1 1 4 1 1$ZX=137$ZN=AUS Eastern", "(GMT+10:00) Canberra, Melbourne, Sydney"}, { "Z=-10$DO=0$ZX=156$ZN=E. Australia", "(GMT+10:00) Brisbane"}, { "Z=-10$DO=1$DL=10 1 1 4 1 1$ZX=210$ZN=Tasmania", "(GMT+10:00) Hobart"}, { "Z=-10$DO=1$DL=3 -1 1 10 -1 1$ZX=221$ZN=Vladivostok", "(GMT+10:00) Vladivostok"}, { "Z=-10$DO=0$ZX=226$ZN=West Pacific", "(GMT+10:00) Guam, Port Moresby"}, { "Z=-11$DO=0$ZX=150$ZN=Central Pacific", "(GMT+11:00) Magadan, Solomon Is., New Caledonia"}, { "Z=-12$DO=1$DL=10 4 1 3 -1 1$ZX=162$ZN=Fiji", "(GMT+12:00) Fiji"}, { "Z=-12$DO=1$DL=3 -1 1 10 -1 1$ZX=174$ZN=Kamchatka", "(GMT+12:00) Petropavlovsk-Kamchatsky - Old"}, { "Z=-12$DO=1$DL=9 -1 1 4 1 1$ZX=189$ZN=New Zealand", "(GMT+12:00) Auckland, Wellington"}, { "Z=-12$DO=0$ZX=217$ZN=UTC+12", "(GMT+12:00) Coordinated Universal Time+12"}, { "Z=-13$DO=0$ZX=212$ZN=Tonga", "(GMT+13:00) Nuku'alofa"}, }; private static String[][] codeToNonNLSLabel_sortByCode; private static final Comparator<Object> CODE_ARRAY_COMPARATOR = new Comparator<Object>(){ public int compare(Object o1, Object o2) { String s1 = toCodeString(o1); String s2 = toCodeString(o2); return s1.compareTo(s2); } }; private static final Comparator<Object> CODE_IGNORE_ZX_COMPARATOR = new Comparator<Object>(){ public int compare(Object o1, Object o2) { String s1 = toCodeString(o1); String s2 = toCodeString(o2); s1 = removeZX(s1); s2 = removeZX(s2); return s1.compareTo(s2); } }; private static final Comparator<Object> CODE_IGNORE_OPTIONAL_COMPARATOR = new Comparator<Object>(){ public int compare(Object o1, Object o2) { String s1 = toCodeString(o1); String s2 = toCodeString(o2); s1 = removeOptional(s1); s2 = removeOptional(s2); return s1.compareTo(s2); } }; public static String[][] getCodeToLabel(Locale labelTranslateLocale){ return codeToNonNLSLabel_unsorted; } private static String[][] byCode(){ if( null == codeToNonNLSLabel_sortByCode){ String[][] copy = Arrays.copyOf(codeToNonNLSLabel_unsorted, codeToNonNLSLabel_unsorted.length); Arrays.sort(copy, CODE_ARRAY_COMPARATOR); codeToNonNLSLabel_sortByCode = copy; } return codeToNonNLSLabel_sortByCode; } public static SelectItem[] getComboOptions(Locale labelTranslateLocale){ String[][] codeToLabel = getCodeToLabel(labelTranslateLocale); SelectItem[] items = new SelectItem[codeToLabel.length]; int i = 0; for (String[] codeAndLabel : codeToLabel) { items[i] = new SelectItem(codeAndLabel[0], codeAndLabel[1]); i++; } return items; } /** * Note, the indexes depend on the locale, because the index returned is the * index in array sorted by the translated labels. * * @param code * @param labelTranslateLocale * @return */ public static boolean containsCode(String code){ int indexInCodeArr = Arrays.binarySearch(byCode(), code, CODE_ARRAY_COMPARATOR); return indexInCodeArr >= 0; } public static String label(String code, Locale labelTranslateLocale){ code = resetOrdering(code); return findLabel(code, CODE_ARRAY_COMPARATOR); } private static String findLabel(String code, Comparator<Object> comparator) { String[][] byCode = byCode(); int indexInCodeArr = Arrays.binarySearch(byCode, code, comparator); if( indexInCodeArr < 0 ){ return null; } String[] codeAndLabel = byCode[indexInCodeArr]; return codeAndLabel[1]; } public static String labelAllowImperfectMatch(String code, Locale labelTranslateLocale){ // It would be best it this is not used, // as there are multiple TimeZones with the same // optional values but which have different names. // It is present so it is possible to preserve // some of the existing behavior. code = resetOrdering(code); code = removeZX(code); return findLabel(code, CODE_IGNORE_ZX_COMPARATOR); } public static String labelIgnoreOptional(String code, Locale labelTranslateLocale){ // It would be best it this is not used, // as there are multiple TimeZones with the same // optional values but which have different names. // It is present so it is possible to preserve // some of the existing behavior. code = removeOptional(code); return findLabel(code, CODE_IGNORE_OPTIONAL_COMPARATOR); } public static String getOffsetOnly(String code){ // Z=5$DO=1$DL=3 2 1 11 1 1$ZN=US Eastern$ZX=214 String z = code.substring(2, code.indexOf('$') ); if( "0".equals(z)){ return "GMT"; } if( z.charAt(0) == '-' ){ z = z.substring(1); return "GMT+"+z; } return "GMT-"+z; } private static String removeOptional(String code) { // remove trailing ZX and ZN int index = code.lastIndexOf("$ZX="); if( -1 != index ){ code = code.substring(0, index); } index = code.lastIndexOf("$ZN"); if( -1 != index ){ code = code.substring(0, index); } return code; } private static String removeZX(String code) { // remove trailing ZX and ZN int index = code.lastIndexOf("$ZX="); if( -1 != index ){ int endZXIndex = code.indexOf('$', index+1); if( -1 == endZXIndex ){ return code.substring(0, index); } return code.substring(0, index)+code.substring(endZXIndex); } return code; } public static String containsCodeIgnoreOrdering(String code){ code = resetOrdering(code); if( containsCode(code) ){ // return the updated code return code; } return null; } private static String resetOrdering(String code) { // convert the optional parts to the ordering above, // with ZX before ZN, needed because the Web classic // TimeZone picker was using the wrong ordering. int ZNindex = code.indexOf("ZN="); int ZXindex = code.indexOf("ZX="); if( -1 != ZNindex && -1 != ZXindex && ZXindex > ZNindex ){ String ZX = code.substring(ZXindex); String ZN = code.substring(ZNindex, ZXindex - 1); code = code.substring(0, ZNindex)+ZX+"$"+ZN; } return code; } private static String toCodeString(Object o1) { if( o1 instanceof String[] ){ o1 = ((String[])o1)[0]; } String s1 = (String)o1; return s1; } }