001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.util;
018:
019: import java.text.SimpleDateFormat;
020: import java.util.Properties;
021: import java.util.Date;
022: import java.util.Locale;
023: import java.util.TimeZone;
024:
025: /**
026: * Converts dates to strings using the same format specifiers as strftime
027: *
028: * Note: This does not mimic strftime perfectly. Certain strftime commands,
029: * are not supported, and will convert as if they were literals.
030: *
031: * Certain complicated commands, like those dealing with the week of the year
032: * probably don't have exactly the same behavior as strftime.
033: *
034: * These limitations are due to use SimpleDateTime. If the conversion was done
035: * manually, all these limitations could be eliminated.
036: *
037: * The interface looks like a subset of DateFormat. Maybe someday someone will make this class
038: * extend DateFormat.
039: *
040: * @author Bip Thelin
041: * @author Dan Sandberg
042: * @version $Revision: 1.3 $, $Date: 2004/05/26 16:26:22 $
043: */
044: public class Strftime {
045: protected static Properties translate;
046: protected SimpleDateFormat simpleDateFormat;
047:
048: /**
049: * Initialize our pattern translation
050: */
051: static {
052: translate = new Properties();
053: translate.put("a", "EEE");
054: translate.put("A", "EEEE");
055: translate.put("b", "MMM");
056: translate.put("B", "MMMM");
057: translate.put("c", "EEE MMM d HH:mm:ss yyyy");
058:
059: //There's no way to specify the century in SimpleDateFormat. We don't want to hard-code
060: //20 since this could be wrong for the pre-2000 files.
061: //translate.put("C", "20");
062: translate.put("d", "dd");
063: translate.put("D", "MM/dd/yy");
064: translate.put("e", "dd"); //will show as '03' instead of ' 3'
065: translate.put("F", "yyyy-MM-dd");
066: translate.put("g", "yy");
067: translate.put("G", "yyyy");
068: translate.put("H", "HH");
069: translate.put("h", "MMM");
070: translate.put("I", "hh");
071: translate.put("j", "DDD");
072: translate.put("k", "HH"); //will show as '07' instead of ' 7'
073: translate.put("l", "hh"); //will show as '07' instead of ' 7'
074: translate.put("m", "MM");
075: translate.put("M", "mm");
076: translate.put("n", "\n");
077: translate.put("p", "a");
078: translate.put("P", "a"); //will show as pm instead of PM
079: translate.put("r", "hh:mm:ss a");
080: translate.put("R", "HH:mm");
081: //There's no way to specify this with SimpleDateFormat
082: //translate.put("s","seconds since ecpoch");
083: translate.put("S", "ss");
084: translate.put("t", "\t");
085: translate.put("T", "HH:mm:ss");
086: //There's no way to specify this with SimpleDateFormat
087: //translate.put("u","day of week ( 1-7 )");
088:
089: //There's no way to specify this with SimpleDateFormat
090: //translate.put("U","week in year with first sunday as first day...");
091:
092: translate.put("V", "ww"); //I'm not sure this is always exactly the same
093:
094: //There's no way to specify this with SimpleDateFormat
095: //translate.put("W","week in year with first monday as first day...");
096:
097: //There's no way to specify this with SimpleDateFormat
098: //translate.put("w","E");
099: translate.put("X", "HH:mm:ss");
100: translate.put("x", "MM/dd/yy");
101: translate.put("y", "yy");
102: translate.put("Y", "yyyy");
103: translate.put("Z", "z");
104: translate.put("z", "Z");
105: translate.put("%", "%");
106: }
107:
108: /**
109: * Create an instance of this date formatting class
110: *
111: * @see #Strftime( String, Locale )
112: */
113: public Strftime(String origFormat) {
114: String convertedFormat = convertDateFormat(origFormat);
115: simpleDateFormat = new SimpleDateFormat(convertedFormat);
116: }
117:
118: /**
119: * Create an instance of this date formatting class
120: *
121: * @param origFormat the strftime-style formatting string
122: * @param locale the locale to use for locale-specific conversions
123: */
124: public Strftime(String origFormat, Locale locale) {
125: String convertedFormat = convertDateFormat(origFormat);
126: simpleDateFormat = new SimpleDateFormat(convertedFormat, locale);
127: }
128:
129: /**
130: * Format the date according to the strftime-style string given in the constructor.
131: *
132: * @param date the date to format
133: * @return the formatted date
134: */
135: public String format(Date date) {
136: return simpleDateFormat.format(date);
137: }
138:
139: /**
140: * Get the timezone used for formatting conversions
141: *
142: * @return the timezone
143: */
144: public TimeZone getTimeZone() {
145: return simpleDateFormat.getTimeZone();
146: }
147:
148: /**
149: * Change the timezone used to format dates
150: *
151: * @see SimpleDateFormat#setTimeZone
152: */
153: public void setTimeZone(TimeZone timeZone) {
154: simpleDateFormat.setTimeZone(timeZone);
155: }
156:
157: /**
158: * Search the provided pattern and get the C standard
159: * Date/Time formatting rules and convert them to the
160: * Java equivalent.
161: *
162: * @param pattern The pattern to search
163: * @return The modified pattern
164: */
165: protected String convertDateFormat(String pattern) {
166: boolean inside = false;
167: boolean mark = false;
168: boolean modifiedCommand = false;
169:
170: StringBuffer buf = new StringBuffer();
171:
172: for (int i = 0; i < pattern.length(); i++) {
173: char c = pattern.charAt(i);
174:
175: if (c == '%' && !mark) {
176: mark = true;
177: } else {
178: if (mark) {
179: if (modifiedCommand) {
180: //don't do anything--we just wanted to skip a char
181: modifiedCommand = false;
182: mark = false;
183: } else {
184: inside = translateCommand(buf, pattern, i,
185: inside);
186: //It's a modifier code
187: if (c == 'O' || c == 'E') {
188: modifiedCommand = true;
189: } else {
190: mark = false;
191: }
192: }
193: } else {
194: if (!inside && c != ' ') {
195: //We start a literal, which we need to quote
196: buf.append("'");
197: inside = true;
198: }
199:
200: buf.append(c);
201: }
202: }
203: }
204:
205: if (buf.length() > 0) {
206: char lastChar = buf.charAt(buf.length() - 1);
207:
208: if (lastChar != '\'' && inside) {
209: buf.append('\'');
210: }
211: }
212: return buf.toString();
213: }
214:
215: protected String quote(String str, boolean insideQuotes) {
216: String retVal = str;
217: if (!insideQuotes) {
218: retVal = '\'' + retVal + '\'';
219: }
220: return retVal;
221: }
222:
223: /**
224: * try to get the Java Date/Time formating associated with
225: * the C standard provided
226: *
227: * @param c The C equivalent to translate
228: * @return The Java formatting rule to use
229: */
230: protected boolean translateCommand(StringBuffer buf,
231: String pattern, int index, boolean oldInside) {
232: char firstChar = pattern.charAt(index);
233: boolean newInside = oldInside;
234:
235: //O and E are modifiers, they mean to present an alternative representation of the next char
236: //we just handle the next char as if the O or E wasn't there
237: if (firstChar == 'O' || firstChar == 'E') {
238: if (index + 1 < pattern.length()) {
239: newInside = translateCommand(buf, pattern, index + 1,
240: oldInside);
241: } else {
242: buf.append(quote("%" + firstChar, oldInside));
243: }
244: } else {
245: String command = translate.getProperty(String
246: .valueOf(firstChar));
247:
248: //If we don't find a format, treat it as a literal--That's what apache does
249: if (command == null) {
250: buf.append(quote("%" + firstChar, oldInside));
251: } else {
252: //If we were inside quotes, close the quotes
253: if (oldInside) {
254: buf.append('\'');
255: }
256: buf.append(command);
257: newInside = false;
258: }
259: }
260: return newInside;
261: }
262: }
|