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.i18n; 020 021import java.nio.charset.Charset; 022import java.text.DateFormat; 023import java.text.SimpleDateFormat; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.Locale; 027import java.util.Map; 028import java.util.TimeZone; 029 030import lucee.commons.date.TimeZoneConstants; 031import lucee.commons.io.IOUtil; 032import lucee.commons.io.res.Resource; 033import lucee.commons.lang.ExceptionUtil; 034import lucee.commons.lang.StringUtil; 035import lucee.runtime.config.Config; 036import lucee.runtime.engine.ThreadLocalPageContext; 037 038import org.apache.commons.collections.map.ReferenceMap; 039 040public class FormatUtil { 041 042 public static final short FORMAT_TYPE_DATE=1; 043 public static final short FORMAT_TYPE_TIME=2; 044 public static final short FORMAT_TYPE_DATE_TIME=3; 045 public static final short FORMAT_TYPE_DATE_ALL=4; 046 047 private final static Map<String,DateFormat[]> formats=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT); 048 049 public static DateFormat[] getDateTimeFormats(Locale locale,TimeZone tz,boolean lenient) { 050 051 String id="dt-"+locale.hashCode()+"-"+tz.getID()+"-"+lenient; 052 DateFormat[] df=formats.get(id); 053 if(df==null) { 054 List<DateFormat> list=new ArrayList<DateFormat>(); 055 list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,locale)); 056 list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.LONG,locale)); 057 list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.MEDIUM,locale)); 058 list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.SHORT,locale)); 059 060 list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.FULL,locale)); 061 list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG,locale)); 062 list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.MEDIUM,locale)); 063 list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.SHORT,locale)); 064 065 list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.FULL,locale)); 066 list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.LONG,locale)); 067 list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM,locale)); 068 list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.SHORT,locale)); 069 070 list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.FULL,locale)); 071 list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.LONG,locale)); 072 list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM,locale)); 073 list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT,locale)); 074 add24(list, locale); 075 addCustom(list, locale, FORMAT_TYPE_DATE_TIME); 076 df=list.toArray(new DateFormat[list.size()]); 077 078 for(int i=0;i<df.length;i++){ 079 df[i].setLenient(lenient); 080 df[i].setTimeZone(tz); 081 } 082 083 formats.put(id, df); 084 } 085 086 return df; 087 } 088 089 090 public static DateFormat[] getDateFormats(Locale locale,TimeZone tz,boolean lenient) { 091 String id="d-"+locale.hashCode()+"-"+tz.getID()+"-"+lenient; 092 DateFormat[] df= formats.get(id); 093 if(df==null) { 094 List<DateFormat> list=new ArrayList<DateFormat>(); 095 list.add(DateFormat.getDateInstance(DateFormat.FULL,locale)); 096 list.add(DateFormat.getDateInstance(DateFormat.LONG,locale)); 097 list.add(DateFormat.getDateInstance(DateFormat.MEDIUM,locale)); 098 list.add(DateFormat.getDateInstance(DateFormat.SHORT,locale)); 099 addCustom(list, locale, FORMAT_TYPE_DATE); 100 df=list.toArray(new DateFormat[list.size()]); 101 102 for(int i=0;i<df.length;i++){ 103 df[i].setLenient(lenient); 104 df[i].setTimeZone(tz); 105 } 106 formats.put(id, df); 107 } 108 return df; 109 } 110 111 public static DateFormat[] getTimeFormats(Locale locale,TimeZone tz,boolean lenient) { 112 String id="t-"+locale.hashCode()+"-"+tz.getID()+"-"+lenient; 113 DateFormat[] df= formats.get(id); 114 if(df==null) { 115 List<DateFormat> list=new ArrayList<DateFormat>(); 116 list.add(DateFormat.getTimeInstance(DateFormat.FULL,locale)); 117 list.add(DateFormat.getTimeInstance(DateFormat.LONG,locale)); 118 list.add(DateFormat.getTimeInstance(DateFormat.MEDIUM,locale)); 119 list.add(DateFormat.getTimeInstance(DateFormat.SHORT,locale)); 120 add24(list, locale); 121 addCustom(list, locale, FORMAT_TYPE_TIME); 122 df=list.toArray(new DateFormat[list.size()]); 123 124 for(int i=0;i<df.length;i++){ 125 df[i].setLenient(lenient); 126 df[i].setTimeZone(tz); 127 } 128 formats.put(id, df); 129 } 130 return df; 131 } 132 133 134 private static void add24(List<DateFormat> list,Locale locale) { 135 136 // if found h:mm:ss a add H:mm:ss ... 137 String p; 138 int index; 139 SimpleDateFormat sdf; 140 DateFormat[] df=list.toArray(new DateFormat[list.size()]); 141 for(int i=0;i<df.length;i++){ 142 if(df[i] instanceof SimpleDateFormat) { 143 p=((SimpleDateFormat) df[i]).toPattern()+""; 144 145 if(check(list,p,locale,"hh:mm:ss a","HH:mm:ss")) continue; 146 if(check(list,p,locale,"h:mm:ss a","H:mm:ss")) continue; 147 if(check(list,p,locale,"hh:mm a","HH:mm")) continue; 148 if(check(list,p,locale,"h:mm a","H:mm")) continue; 149 150 if(check(list,p,locale,"hh:mm:ssa","HH:mm:ss")) continue; 151 if(check(list,p,locale,"h:mm:ssa","H:mm:ss")) continue; 152 if(check(list,p,locale,"hh:mma","HH:mm")) continue; 153 if(check(list,p,locale,"h:mma","H:mm")) continue; 154 155 //if(check(list,p,locale,"HH:mm:ss","hh:mm:ss a")) continue; 156 //if(check(list,p,locale,"H:mm:ss","h:mm:ss a")) continue; 157 //if(check(list,p,locale,"HH:mm","hh:mm a")) continue; 158 //if(check(list,p,locale,"H:mm","h:mm a")) continue; 159 } 160 } 161 } 162 163 private static boolean check(List<DateFormat> list, String p,Locale locale, String from, String to) { 164 int index = p.indexOf(from); 165 if(index!=-1) { 166 p=StringUtil.replace(p, from, to, true); 167 SimpleDateFormat sdf = new SimpleDateFormat(p,locale); 168 if(!list.contains(sdf))list.add(sdf); 169 return true; 170 } 171 return false; 172 } 173 174 175 private static void addCustom(List<DateFormat> list,Locale locale,short formatType) { 176 // get custom formats from file 177 Config config = ThreadLocalPageContext.getConfig(); 178 Resource dir=config.getConfigDir().getRealResource("locales"); 179 if(dir.isDirectory()) { 180 String appendix="-datetime"; 181 if(formatType==FORMAT_TYPE_DATE)appendix="-date"; 182 if(formatType==FORMAT_TYPE_TIME)appendix="-time"; 183 184 Resource file = dir.getRealResource(locale.getLanguage()+"-"+locale.getCountry()+appendix+".df"); 185 if(file.isFile()) { 186 try { 187 String content=IOUtil.toString(file, (Charset)null); 188 String[] arr = lucee.runtime.type.util.ListUtil.listToStringArray(content, '\n'); 189 String line; 190 SimpleDateFormat sdf; 191 for(int i=0;i<arr.length;i++){ 192 line=arr[i].trim(); 193 if(StringUtil.isEmpty(line)) continue; 194 sdf = new SimpleDateFormat(line,locale); 195 if(!list.contains(sdf))list.add(sdf); 196 } 197 198 } 199 catch (Throwable t) { 200 ExceptionUtil.rethrowIfNecessary(t); 201 } 202 } 203 } 204 } 205 206 /** 207 * CFML Supported LS Formats 208 * @param locale 209 * @param tz 210 * @param lenient 211 * @return 212 */ 213 public static DateFormat[] getCFMLFormats(TimeZone tz,boolean lenient) { 214 String id="cfml-"+Locale.ENGLISH.hashCode()+"-"+tz.getID()+"-"+lenient; 215 DateFormat[] df= formats.get(id); 216 if(df==null) { 217 df= new SimpleDateFormat[]{ 218 new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy",Locale.ENGLISH) 219 ,new SimpleDateFormat("MMMM dd, yyyy HH:mm:ss a zzz",Locale.ENGLISH) 220 ,new SimpleDateFormat("MMM dd, yyyy HH:mm:ss a",Locale.ENGLISH) 221 ,new SimpleDateFormat("MMM dd, yyyy HH:mm:ss",Locale.ENGLISH) 222 ,new SimpleDateFormat("MMMM d yyyy HH:mm:ssZ",Locale.ENGLISH) 223 ,new SimpleDateFormat("MMMM d yyyy HH:mm:ss",Locale.ENGLISH) 224 ,new SimpleDateFormat("MMMM d yyyy HH:mm",Locale.ENGLISH) 225 ,new SimpleDateFormat("EEE, MMM dd, yyyy HH:mm:ssZ",Locale.ENGLISH) 226 ,new SimpleDateFormat("EEE, MMM dd, yyyy HH:mm:ss",Locale.ENGLISH) 227 ,new SimpleDateFormat("EEEE, MMMM dd, yyyy H:mm:ss a zzz",Locale.ENGLISH) 228 ,new SimpleDateFormat("dd-MMM-yy HH:mm a",Locale.ENGLISH) 229 ,new SimpleDateFormat("dd-MMMM-yy HH:mm a",Locale.ENGLISH) 230 ,new SimpleDateFormat("EE, dd-MMM-yyyy HH:mm:ss zz",Locale.ENGLISH) 231 ,new SimpleDateFormat("EE, dd MMM yyyy HH:mm:ss zz",Locale.ENGLISH) 232 ,new SimpleDateFormat("EEE d, MMM yyyy HH:mm:ss zz",Locale.ENGLISH) 233 ,new SimpleDateFormat("dd-MMM-yyyy",Locale.ENGLISH) 234 ,new SimpleDateFormat("MMMM, dd yyyy HH:mm:ssZ",Locale.ENGLISH) 235 ,new SimpleDateFormat("MMMM, dd yyyy HH:mm:ss",Locale.ENGLISH) 236 ,new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zz",Locale.ENGLISH) 237 ,new SimpleDateFormat("dd MMM yyyy HH:mm:ss zz",Locale.ENGLISH) 238 ,new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZ (z)",Locale.ENGLISH) 239 ,new SimpleDateFormat("dd MMM, yyyy HH:mm:ss",Locale.ENGLISH) 240 //,new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss",Locale.ENGLISH) 241 }; 242 243 for(int i=0;i<df.length;i++){ 244 df[i].setLenient(lenient); 245 df[i].setTimeZone(tz); 246 } 247 formats.put(id, df); 248 } 249 return df; 250 } 251 252 public static DateFormat[] getFormats(Locale locale,TimeZone tz,boolean lenient, short formatType) { 253 if(FORMAT_TYPE_DATE_TIME==formatType)return getDateTimeFormats(locale,TimeZoneConstants.GMT,true); 254 if(FORMAT_TYPE_DATE==formatType)return getDateFormats(locale,TimeZoneConstants.GMT,true); 255 if(FORMAT_TYPE_TIME==formatType)return getTimeFormats(locale,TimeZoneConstants.GMT,true); 256 257 DateFormat[] dt = getDateTimeFormats(locale,TimeZoneConstants.GMT,true); 258 DateFormat[] d = getDateFormats(locale,TimeZoneConstants.GMT,true); 259 DateFormat[] t = getTimeFormats(locale,TimeZoneConstants.GMT,true); 260 261 DateFormat[] all=new DateFormat[dt.length+d.length+t.length]; 262 for(int i=0;i<dt.length;i++){ 263 all[i]=dt[i]; 264 } 265 for(int i=0;i<d.length;i++){ 266 all[i+dt.length]=d[i]; 267 } 268 for(int i=0;i<t.length;i++){ 269 all[i+dt.length+d.length]=t[i]; 270 } 271 return getDateTimeFormats(locale,TimeZoneConstants.GMT,true); 272 } 273 274 public static String[] getSupportedPatterns(Locale locale, short formatType) { 275 DateFormat[] _formats = getFormats(locale,TimeZoneConstants.GMT,true,formatType); 276 String[] patterns=new String[_formats.length]; 277 for(int i=0;i<_formats.length;i++){ 278 if(!(_formats[i] instanceof SimpleDateFormat))return null; // all or nothing 279 patterns[i]=((SimpleDateFormat)_formats[i]).toPattern(); 280 } 281 282 return patterns; 283 } 284 285 286 287}