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