/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.runtime.functions.displayFormatting; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Locale; import java.util.TimeZone; import lucee.commons.lang.StringUtil; import lucee.runtime.PageContext; import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.ExpressionException; import lucee.runtime.exp.PageException; import lucee.runtime.ext.function.BIF; import lucee.runtime.op.Caster; import lucee.runtime.type.dt.DateTime; /** * Implements the CFML Function dateformat */ public final class DateTimeFormat extends BIF { private static final long serialVersionUID = 134840879454373440L; public static final String DEFAULT_MASK = "dd-MMM-yyyy HH:mm:ss";// this is already a SimpleDateFormat mask! private static final String[] AP = new String[]{"A","P"}; private static final char ZERO = (char)0; private static final char ONE = (char)1; private static final String ZEROZERO = new StringBuilder().append(ZERO).append(ZERO).toString(); /** * @param pc * @param object * @return Formated Time Object as String * @throws ExpressionException */ public static String call(PageContext pc , Object object) throws ExpressionException { return invoke(pc,object, null,Locale.US,ThreadLocalPageContext.getTimeZone(pc)); } /** * @param pc * @param object * @param mask Characters that show how CFML displays a date: * @return Formated Time Object as String * @throws ExpressionException */ public static String call(PageContext pc , Object object, String mask) throws ExpressionException { return invoke(pc,object,mask,Locale.US,ThreadLocalPageContext.getTimeZone(pc)); } public static String call(PageContext pc , Object object, String mask,TimeZone tz) throws ExpressionException { return invoke(pc,object,mask, Locale.US,tz==null?ThreadLocalPageContext.getTimeZone(pc):tz); } public static String invoke(PageContext pc , Object object, String mask,Locale locale,TimeZone tz) throws ExpressionException { if(locale==null) locale=Locale.US; DateTime datetime = Caster.toDate(object,true,tz,null); if(datetime==null) { if(object.toString().trim().length()==0) return ""; throw new ExpressionException("can't convert value "+object+" to a datetime value"); } java.text.DateFormat format=null; if("short".equalsIgnoreCase(mask)) format=java.text.DateFormat.getDateTimeInstance(java.text.DateFormat.SHORT, java.text.DateFormat.SHORT, locale); else if("medium".equalsIgnoreCase(mask)) format=java.text.DateFormat.getDateTimeInstance(java.text.DateFormat.MEDIUM, java.text.DateFormat.MEDIUM, locale); else if("long".equalsIgnoreCase(mask)) format=java.text.DateFormat.getDateTimeInstance(java.text.DateFormat.LONG, java.text.DateFormat.LONG, locale); else if("full".equalsIgnoreCase(mask)) format=java.text.DateFormat.getDateTimeInstance(java.text.DateFormat.FULL, java.text.DateFormat.FULL, locale); else if ("iso8601".equalsIgnoreCase(mask)) format = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ" ); else { SimpleDateFormat sdf; format = sdf= new SimpleDateFormat(convertMask(mask), locale); if(mask!=null && StringUtil.indexOfIgnoreCase(mask, "tt")==-1 && StringUtil.indexOfIgnoreCase(mask, "t")!=-1) { DateFormatSymbols dfs = new DateFormatSymbols(locale); dfs.setAmPmStrings(AP); sdf.setDateFormatSymbols(dfs); } } format.setTimeZone(tz); return format.format(datetime); } @Override public Object invoke(PageContext pc, Object[] args) throws PageException { if(args.length==1)return call(pc,args[0]); if(args.length==2)return call(pc,args[0],Caster.toString(args[1])); return call(pc,args[0],Caster.toString(args[1]),Caster.toTimeZone(args[2])); } private static String convertMask(String mask) { if(mask==null) return DEFAULT_MASK; mask=StringUtil.replace(mask, "''", ZEROZERO, false); boolean inside=false; char[] carr = mask.toCharArray(); StringBuilder sb=new StringBuilder(); for(int i=0;i<carr.length;i++){ switch(carr[i]){ case 'm': if(!inside){sb.append('M');}else{sb.append(carr[i]);} break; case 'S': if(!inside){sb.append('s');}else{sb.append(carr[i]);} break; case 't': if(!inside){sb.append('a');}else{sb.append(carr[i]);} break; case 'T': if(!inside){sb.append('a');}else{sb.append(carr[i]);} break; case 'n': if(!inside){sb.append('m');}else{sb.append(carr[i]);} break; case 'N': if(!inside){sb.append('m');}else{sb.append(carr[i]);} break; case 'l': if(!inside){sb.append('S');}else{sb.append(carr[i]);} break; case 'L': if(!inside){sb.append('S');}else{sb.append(carr[i]);} break; case 'Y': if(!inside){sb.append('y');}else{sb.append(carr[i]);} break; case 'g': if(!inside){sb.append('G');}else{sb.append(carr[i]);} break; case 'f': if(!inside){sb.append("'f'");}else{sb.append(carr[i]);} break; case 'e': if(!inside){sb.append("'e'");}else{sb.append(carr[i]);} break; case 'G': case 'y': case 'M': case 'W': case 'w': case 'F': case 'E': case 'a': case 'H': case 'h': case 'K': case 'k': case 'Z': case 'z': case 's': //case '.': sb.append(carr[i]); break; case 'D': case 'd': int len=sb.length(); // 2 before are D or d if(len>1 && (sb.charAt(len-1)=='d'||sb.charAt(len-1)=='D') && (sb.charAt(len-2)=='d'||sb.charAt(len-2)=='D')) { sb.deleteCharAt(len-1); sb.deleteCharAt(len-2); sb.append(ONE).append(ONE).append(ONE); break; } // 2 before are D or d else if(len>0 && sb.charAt(len-1)==ONE) { sb.append(ONE); break; } sb.append(carr[i]); break; case '\'': if(carr.length-1>i) { if(carr[i+1]=='\'') { i++; sb.append("''"); break; } } inside=!inside; sb.append(carr[i]); break; /*case '\'': if(carr.length-1>i) { if(carr[i+1]=='\'') { i++; sb.append("''"); break; } } sb.append("''"); break;*/ default: char c=carr[i]; if(!inside && ((c>='a' && c<='z') || (c>='A' && c<='Z'))) sb.append('\'').append(c).append('\''); else sb.append(c); } } String str=StringUtil.replace(sb.toString(), "''", "", false); str=StringUtil.replace(str, ZEROZERO,"''", false); str=str.replace(ONE, 'E'); str=y2yyyy(str); return str; } public static String y2yyyy(String str) { char[] carr = str.toCharArray(); StringBuilder sb=new StringBuilder(); boolean inside=false; char c; for(int i=0;i<carr.length;i++) { c=carr[i]; if(c=='\'') inside=!inside; else if(!inside && c=='y') { if((i==0 || carr[i-1]!='y') && (i==(carr.length-1) || carr[i+1]!='y')) { sb.append("yyyy"); continue; } } sb.append(c); } return sb.toString(); } }