/** * * 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.format; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import lucee.commons.date.JREDateTimeUtil; import lucee.commons.date.TimeZoneConstants; import lucee.commons.lang.StringUtil; import lucee.runtime.engine.ThreadLocalPageContext; public final class DateFormat extends BaseFormat implements Format { /** * constructor of the class * @param locale */ public DateFormat(Locale locale) { super(locale); } /** * formats a date to a cfml date format (short) * @param date * @return formated date */ @Override public String format(Date date) { return format(date,"medium"); } /** * formats a date to a cfml date format * @param date * @param mask * @return formated date as string */ @Override public String format(Date date,String mask) { return format(date,mask,null); } public String format(Date date,String mask, TimeZone tz) { return format(date.getTime(), mask, tz); } public String format(long time,String mask, TimeZone tz) { tz=ThreadLocalPageContext.getTimeZone(tz); Calendar calendar = JREDateTimeUtil.getThreadCalendar(getLocale(),tz); calendar.setTimeInMillis(time); String lcMask=StringUtil.toLowerCase(mask); if(lcMask.equals("short")) return getAsString(calendar,java.text.DateFormat.SHORT,tz); else if(lcMask.equals("medium")) return getAsString(calendar,java.text.DateFormat.MEDIUM,tz); else if(lcMask.equals("long")) return getAsString(calendar,java.text.DateFormat.LONG,tz); else if(lcMask.equals("full")) return getAsString(calendar,java.text.DateFormat.FULL,tz); int len=mask.length(); int pos=0; if(len==0) return ""; StringBuilder formated=new StringBuilder(); for(;pos<len;pos++) { char c=mask.charAt(pos); char next=(len>pos+1)?mask.charAt(pos+1):(char)0; switch(c) { case 'z':{ int count=1; while(mask.length()>pos+1 && mask.charAt(pos+1)=='z') { pos++; count++; } formated.append(z(time,tz,count)); } break; // RFC 822 TimeZone case 'Z':{ while(mask.length()>pos+1 && mask.charAt(pos+1)=='Z') { pos++; } formated.append(Z(time,tz)); } break; // ISO 8601 TimeZone case 'X':{ int count=1; while(mask.length()>pos+1 && mask.charAt(pos+1)=='X') { pos++; count++; } TimeZone.setDefault(TimeZone.getTimeZone("CET")); formated.append(X(time,tz,count)); } break; // g: Era designator // gg: Era designator case 'g': case 'G': String era=toEra(calendar.get(Calendar.ERA),""); while(mask.length()>pos+1 && Character.toLowerCase(mask.charAt(pos+1))=='g') { pos++; } formated.append(era); break; // d: Day of month. Digits; no leading zero for single-digit days // dd: Day of month. Digits; leading zero for single-digit days // ddd: Day of week, abbreviation // dddd: Day of week. Full name case 'd': case 'D': char next2=(len>pos+2)?mask.charAt(pos+2):(char)0; char next3=(len>pos+3)?mask.charAt(pos+3):(char)0; int day=calendar.get(Calendar.DATE); if(next=='d' || next=='D') { if(next2=='d' || next2=='D') { if(next3=='d' || next3=='D') { formated.append(getDayOfWeekAsString(calendar.get(Calendar.DAY_OF_WEEK))); pos+=3; } else { formated.append(getDayOfWeekShortAsString(calendar.get(Calendar.DAY_OF_WEEK))); pos+=2; } } else { formated.append(day<10?"0"+day:""+day); pos++; } } else { formated.append(day); } break; // m: Month. Digits; no leading zero for single-digit months // mm: Month. Digits; leading zero for single-digit months // mmm: Month. abbreviation (if appropriate) // mmmm: Month. Full name case 'm': case 'M': char next_2=(len>pos+2)?mask.charAt(pos+2):(char)0; char next_3=(len>pos+3)?mask.charAt(pos+3):(char)0; int month=calendar.get(Calendar.MONTH)+1; if(next=='m' || next=='M') { if(next_2=='m' || next_2=='M') { if(next_3=='m' || next_3=='M') { formated.append(getMonthAsString(month)); pos+=3; } else { formated.append(getMonthShortAsString(month)); pos+=2; } } else { formated.append(month<10?"0"+month:""+month); pos++; } } else { formated.append(month); } break; // y: Year. Last two digits; no leading zero for years less than 10 // yy: Year. Last two digits; leading zero for years less than 10 // yyyy: Year. Four digits case 'y': case 'Y': char next__2=(len>pos+2)?mask.charAt(pos+2):(char)0; char next__3=(len>pos+3)?mask.charAt(pos+3):(char)0; int year4=calendar.get(Calendar.YEAR); int year2=year4%100; if(next=='y' || next=='Y') { if((next__2=='y' || next__2=='Y') && (next__3=='y' || next__3=='Y')) { formated.append(year4); pos+=3; } else if((next__2=='y' || next__2=='Y')) { formated.append(year4); pos+=2; } else { formated.append(year2<10?"0"+year2:""+year2); pos++; } } else { formated.append(year4); } break; // Otherwise default: formated.append(c); } } return formated.toString(); } private String toEra(int era, String defaultValue) { if(GregorianCalendar.AD==era) return "AD"; if(GregorianCalendar.BC==era) return "BC"; return defaultValue; } public static Object X(long time, TimeZone tz, int count) { if(tz.equals(TimeZoneConstants.UTC)) return "Z"; String res = Z(time,tz); if(count==1) return res.substring(0, 3); if(count==2) return res; //String h=(res.charAt(1)=='0')? h=res.substring(2, 3):res.substring(1, 3); return res.substring(0, 1)+res.substring(1, 3)+":"+res.substring(3); } public static String z(long time, TimeZone tz, int count) { Calendar c = Calendar.getInstance(tz,Locale.US); c.setTimeInMillis(time); boolean daylight = c.get(Calendar.DST_OFFSET) != 0; int style = (count < 4 ? TimeZone.SHORT : TimeZone.LONG); return tz.getDisplayName(daylight, style, Locale.US); } public static String Z(long time, TimeZone tz) { StringBuilder sb=new StringBuilder(); Calendar c = Calendar.getInstance(tz,Locale.US); c.setTimeInMillis(time); int value = (c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET)) / 60000; int width = 4; if (value >= 0) sb.append('+'); else width++; int num = (value / 60) * 100 + (value % 60); sprintf0d(sb, num, width); return sb.toString(); } /** * Mimics sprintf(buf, "%0*d", decaimal, width). */ private static final StringBuilder sprintf0d(StringBuilder sb, int value, int width) { long d = value; if (d < 0) { sb.append('-'); d = -d; --width; } int n = 10; for (int i = 2; i < width; i++) { n *= 10; } for (int i = 1; i < width && d < n; i++) { sb.append('0'); n /= 10; } sb.append(d); return sb; } private String getAsString(Calendar c,int style, TimeZone tz) { java.text.DateFormat df = java.text.DateFormat.getDateInstance(style,getLocale()); df.setTimeZone(tz); return df.format(c.getTime()); } }