/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. * * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2004 The Apache Software Foundation * * Licensed 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 * @version $Revision: 1.3 $, $Date: 2005/12/08 01:28:20 $ */ public class Strftime { protected static final Properties translate; protected 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 * * @see #Strftime( String, Locale ) */ public Strftime( String origFormat ) { String convertedFormat = convertDateFormat( origFormat ); simpleDateFormat = new SimpleDateFormat( convertedFormat ); } /** * Create an instance of this date formatting class * * @param origFormat the strftime-style formatting string * @param 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 java.text.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 formating associated with * the C standard provided * * @param buf translated StringBuilder * @param pattern command to translate * @param index first character index * @param oldInside whether to close the quotes if inside quote * @return The Java formatting rule to use */ 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; } }