| /* |
| * Copyright 2006 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.tapestry.util; |
| |
| import java.text.ParseException; |
| 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. |
| * |
| * <p> |
| * Added to tapestry in order to help with dojo/javascript date/time conversions. |
| * </p> |
| * |
| * @see "http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html" |
| * @author Bip Thelin |
| * @author Dan Sandberg |
| */ |
| public class Strftime |
| { |
| |
| protected static Properties translate; |
| protected static Properties pTranslate; |
| 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 ecpoch"); |
| 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("%", "%"); |
| |
| pTranslate = new Properties(); |
| pTranslate.put("EEE", "%a"); |
| pTranslate.put("EEEE", "%A"); |
| pTranslate.put("MMM", "%b"); |
| pTranslate.put("MMMM", "%B"); |
| pTranslate.put("EEE MMM d HH:mm:ss yyyy", "%c"); |
| |
| // 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"); |
| pTranslate.put("dd", "%d"); |
| pTranslate.put("MM/dd/yy", "%D"); |
| pTranslate.put("yyyy-MM-dd", "%F"); |
| pTranslate.put("yy", "%g"); |
| pTranslate.put("yyyy", "%G"); |
| pTranslate.put("HH", "%H"); |
| pTranslate.put("MMM", "%h"); |
| pTranslate.put("hh", "%I"); |
| pTranslate.put("DDD", "%j"); |
| pTranslate.put("MM", "%m"); |
| pTranslate.put("mm", "%M"); |
| pTranslate.put("\n", "%n"); |
| pTranslate.put("a", "%p"); // will show as pm instead of PM |
| pTranslate.put("hh:mm:ss a", "%r"); |
| pTranslate.put("HH:mm", "%R"); |
| // There's no way to specify this with SimpleDateFormat |
| // translate.put("s","seconds since ecpoch"); |
| pTranslate.put("ss", "%S"); |
| pTranslate.put("\t", "%t"); |
| pTranslate.put("HH:mm:ss", "%T"); |
| // 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..."); |
| |
| pTranslate.put("ww", "%V"); // 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"); |
| pTranslate.put("HH:mm:ss", "%X"); |
| pTranslate.put("MM/dd/yy", "%x"); |
| pTranslate.put("yy", "%y"); |
| pTranslate.put("yyyy", "%Y"); |
| pTranslate.put("z", "%Z"); |
| pTranslate.put("Z", "%z"); |
| pTranslate.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 |
| * 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); |
| } |
| |
| /** |
| * Parses the input. |
| * |
| * @see java.text.SimpleDateFormat#parse(String) |
| * @param input The string to parse. |
| * @return A parsed {@link Date}. |
| * @throws ParseException On input error. |
| */ |
| public Date parse(String input) |
| throws ParseException |
| { |
| return simpleDateFormat.parse(input); |
| } |
| |
| /** |
| * 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(TimeZone) |
| */ |
| public void setTimeZone(TimeZone timeZone) |
| { |
| simpleDateFormat.setTimeZone(timeZone); |
| } |
| |
| /** |
| * Does the exact opposite of {{@link #convertDateFormat(String)} by converting |
| * the incoming java date format string into a POSIX compliant format string. |
| * @param pattern The java date format style format |
| * @return The converted format into something usable by POSIX strftime style parser/formatters. |
| */ |
| public static String convertToPosixFormat(String pattern) |
| { |
| if (pattern == null) return null; |
| |
| StringBuffer buf = new StringBuffer(); |
| int start=-1; |
| |
| for(int i = 0; i < pattern.length(); i++) { |
| char c = pattern.charAt(i); |
| |
| // if in a definition |
| if (Character.isLetter(c)) { |
| if (start <= -1) start = i; |
| continue; |
| } else if (start >= 0) { |
| // we've hit the end of a definition |
| String conv = pattern.substring(start, i); |
| String match = pTranslate.getProperty(conv); |
| |
| if (match == null) |
| buf.append(conv); // just append it, this shouldn't happen we hope |
| else |
| buf.append(match); |
| |
| // reset |
| start=-1; |
| } |
| |
| buf.append(c); |
| } |
| |
| // grab last one, if any |
| if (start > -1) { |
| String conv = pattern.substring(start, pattern.length()); |
| String match = pTranslate.getProperty(conv); |
| if (match == null) buf.append(conv); |
| else buf.append(match); |
| } |
| |
| return buf.toString(); |
| } |
| |
| /** |
| * 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 |
| */ |
| public static String convertDateFormat(String pattern) |
| { |
| boolean inside = false; |
| boolean mark = false; |
| boolean modifiedCommand = false; |
| |
| StringBuffer buf = new StringBuffer(); |
| |
| 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(); |
| } |
| |
| private static 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 c |
| * The C equivalent to translate |
| * @return The Java formatting rule to use |
| */ |
| private static boolean translateCommand(StringBuffer 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; |
| } |
| } |