| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.catalina.util; |
| |
| import java.text.SimpleDateFormat; |
| import java.util.Date; |
| import java.util.Locale; |
| import java.util.Properties; |
| import java.util.TimeZone; |
| |
| /** |
| * Converts dates to strings using the same format specifiers as strftime |
| * |
| * Note: This does not mimic strftime perfectly. Certain strftime commands, |
| * are not supported, and will convert as if they were literals. |
| * |
| * Certain complicated commands, like those dealing with the week of the year |
| * probably don't have exactly the same behavior as strftime. |
| * |
| * These limitations are due to use SimpleDateTime. If the conversion was done |
| * manually, all these limitations could be eliminated. |
| * |
| * The interface looks like a subset of DateFormat. Maybe someday someone will make this class |
| * extend DateFormat. |
| * |
| * @author Bip Thelin |
| * @author Dan Sandberg |
| */ |
| public class Strftime { |
| protected static final Properties translate; |
| protected final SimpleDateFormat simpleDateFormat; |
| |
| /** |
| * Initialize our pattern translation |
| */ |
| static { |
| translate = new Properties(); |
| translate.put("a","EEE"); |
| translate.put("A","EEEE"); |
| translate.put("b","MMM"); |
| translate.put("B","MMMM"); |
| translate.put("c","EEE MMM d HH:mm:ss yyyy"); |
| |
| //There's no way to specify the century in SimpleDateFormat. We don't want to hard-code |
| //20 since this could be wrong for the pre-2000 files. |
| //translate.put("C", "20"); |
| translate.put("d","dd"); |
| translate.put("D","MM/dd/yy"); |
| translate.put("e","dd"); //will show as '03' instead of ' 3' |
| translate.put("F","yyyy-MM-dd"); |
| translate.put("g","yy"); |
| translate.put("G","yyyy"); |
| translate.put("H","HH"); |
| translate.put("h","MMM"); |
| translate.put("I","hh"); |
| translate.put("j","DDD"); |
| translate.put("k","HH"); //will show as '07' instead of ' 7' |
| translate.put("l","hh"); //will show as '07' instead of ' 7' |
| translate.put("m","MM"); |
| translate.put("M","mm"); |
| translate.put("n","\n"); |
| translate.put("p","a"); |
| translate.put("P","a"); //will show as pm instead of PM |
| translate.put("r","hh:mm:ss a"); |
| translate.put("R","HH:mm"); |
| //There's no way to specify this with SimpleDateFormat |
| //translate.put("s","seconds since epoch"); |
| translate.put("S","ss"); |
| translate.put("t","\t"); |
| translate.put("T","HH:mm:ss"); |
| //There's no way to specify this with SimpleDateFormat |
| //translate.put("u","day of week ( 1-7 )"); |
| |
| //There's no way to specify this with SimpleDateFormat |
| //translate.put("U","week in year with first Sunday as first day..."); |
| |
| translate.put("V","ww"); //I'm not sure this is always exactly the same |
| |
| //There's no way to specify this with SimpleDateFormat |
| //translate.put("W","week in year with first Monday as first day..."); |
| |
| //There's no way to specify this with SimpleDateFormat |
| //translate.put("w","E"); |
| translate.put("X","HH:mm:ss"); |
| translate.put("x","MM/dd/yy"); |
| translate.put("y","yy"); |
| translate.put("Y","yyyy"); |
| translate.put("Z","z"); |
| translate.put("z","Z"); |
| translate.put("%","%"); |
| } |
| |
| |
| /** |
| * Create an instance of this date formatting class |
| * |
| * @param origFormat the strftime-style formatting string |
| * @param locale the locale to use for locale-specific conversions |
| */ |
| public Strftime( String origFormat, Locale locale ) { |
| String convertedFormat = convertDateFormat( origFormat ); |
| simpleDateFormat = new SimpleDateFormat( convertedFormat, locale ); |
| } |
| |
| /** |
| * Format the date according to the strftime-style string given in the constructor. |
| * |
| * @param date the date to format |
| * @return the formatted date |
| */ |
| public String format( Date date ) { |
| return simpleDateFormat.format( date ); |
| } |
| |
| /** |
| * Get the timezone used for formatting conversions |
| * |
| * @return the timezone |
| */ |
| public TimeZone getTimeZone() { |
| return simpleDateFormat.getTimeZone(); |
| } |
| |
| /** |
| * Change the timezone used to format dates |
| * |
| * @see SimpleDateFormat#setTimeZone |
| */ |
| public void setTimeZone( TimeZone timeZone ) { |
| simpleDateFormat.setTimeZone( timeZone ); |
| } |
| |
| /** |
| * Search the provided pattern and get the C standard |
| * Date/Time formatting rules and convert them to the |
| * Java equivalent. |
| * |
| * @param pattern The pattern to search |
| * @return The modified pattern |
| */ |
| protected String convertDateFormat( String pattern ) { |
| boolean inside = false; |
| boolean mark = false; |
| boolean modifiedCommand = false; |
| |
| StringBuilder buf = new StringBuilder(); |
| |
| for(int i = 0; i < pattern.length(); i++) { |
| char c = pattern.charAt(i); |
| |
| if ( c=='%' && !mark ) { |
| mark=true; |
| } else { |
| if ( mark ) { |
| if ( modifiedCommand ) { |
| //don't do anything--we just wanted to skip a char |
| modifiedCommand = false; |
| mark = false; |
| } else { |
| inside = translateCommand( buf, pattern, i, inside ); |
| //It's a modifier code |
| if ( c=='O' || c=='E' ) { |
| modifiedCommand = true; |
| } else { |
| mark=false; |
| } |
| } |
| } else { |
| if ( !inside && c != ' ' ) { |
| //We start a literal, which we need to quote |
| buf.append("'"); |
| inside = true; |
| } |
| |
| buf.append(c); |
| } |
| } |
| } |
| |
| if ( buf.length() > 0 ) { |
| char lastChar = buf.charAt( buf.length() - 1 ); |
| |
| if( lastChar!='\'' && inside ) { |
| buf.append('\''); |
| } |
| } |
| return buf.toString(); |
| } |
| |
| protected String quote( String str, boolean insideQuotes ) { |
| String retVal = str; |
| if ( !insideQuotes ) { |
| retVal = '\'' + retVal + '\''; |
| } |
| return retVal; |
| } |
| |
| /** |
| * Try to get the Java Date/Time formatting associated with |
| * the C standard provided. |
| * |
| * @param buf The buffer |
| * @param pattern The date/time pattern |
| * @param index The char index |
| * @param oldInside Flag value |
| * @return True if new is inside buffer |
| */ |
| protected boolean translateCommand( StringBuilder buf, String pattern, int index, boolean oldInside ) { |
| char firstChar = pattern.charAt( index ); |
| boolean newInside = oldInside; |
| |
| //O and E are modifiers, they mean to present an alternative representation of the next char |
| //we just handle the next char as if the O or E wasn't there |
| if ( firstChar == 'O' || firstChar == 'E' ) { |
| if ( index + 1 < pattern.length() ) { |
| newInside = translateCommand( buf, pattern, index + 1, oldInside ); |
| } else { |
| buf.append( quote("%" + firstChar, oldInside ) ); |
| } |
| } else { |
| String command = translate.getProperty( String.valueOf( firstChar ) ); |
| |
| //If we don't find a format, treat it as a literal--That's what apache does |
| if ( command == null ) { |
| buf.append( quote( "%" + firstChar, oldInside ) ); |
| } else { |
| //If we were inside quotes, close the quotes |
| if ( oldInside ) { |
| buf.append( '\'' ); |
| } |
| buf.append( command ); |
| newInside = false; |
| } |
| } |
| return newInside; |
| } |
| } |