Coverage Report - org.apache.tapestry.util.Strftime
 
Classes in this File Line Coverage Branch Coverage Complexity
Strftime
0%
0/146
0%
0/52
3.7
 
 1  
 /*
 2  
  * Copyright 2006 The Apache Software Foundation. Licensed under the Apache License, Version 2.0
 3  
  * (the "License"); you may not use this file except in compliance with the License. You may obtain
 4  
  * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
 5  
  * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 6  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 7  
  * for the specific language governing permissions and limitations under the License.
 8  
  */
 9  
 
 10  
 package org.apache.tapestry.util;
 11  
 
 12  
 import java.text.ParseException;
 13  
 import java.text.SimpleDateFormat;
 14  
 import java.util.Date;
 15  
 import java.util.Locale;
 16  
 import java.util.Properties;
 17  
 import java.util.TimeZone;
 18  
 
 19  
 /**
 20  
  * Converts dates to strings using the same format specifiers as strftime Note: This does not mimic
 21  
  * strftime perfectly. Certain strftime commands, are not supported, and will convert as if they
 22  
  * were literals. Certain complicated commands, like those dealing with the week of the year
 23  
  * probably don't have exactly the same behavior as strftime. These limitations are due to use
 24  
  * SimpleDateTime. If the conversion was done manually, all these limitations could be eliminated.
 25  
  * The interface looks like a subset of DateFormat. Maybe someday someone will make this class
 26  
  * extend DateFormat.
 27  
  * 
 28  
  * <p>
 29  
  * Added to tapestry in order to help with dojo/javascript date/time conversions.
 30  
  * </p>
 31  
  * 
 32  
  * @see "http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html"
 33  
  * @author Bip Thelin
 34  
  * @author Dan Sandberg
 35  
  */
 36  
 public class Strftime
 37  
 {
 38  
 
 39  
     protected static Properties translate;
 40  
     protected static Properties pTranslate;
 41  
     protected SimpleDateFormat simpleDateFormat;
 42  
 
 43  
     /**
 44  
      * Initialize our pattern translation
 45  
      */
 46  
     static {
 47  0
         translate = new Properties();
 48  0
         translate.put("a", "EEE");
 49  0
         translate.put("A", "EEEE");
 50  0
         translate.put("b", "MMM");
 51  0
         translate.put("B", "MMMM");
 52  0
         translate.put("c", "EEE MMM d HH:mm:ss yyyy");
 53  
 
 54  
         // There's no way to specify the century in SimpleDateFormat. We don't want to hard-code
 55  
         // 20 since this could be wrong for the pre-2000 files.
 56  
         // translate.put("C", "20");
 57  0
         translate.put("d", "dd");
 58  0
         translate.put("D", "MM/dd/yy");
 59  0
         translate.put("e", "dd"); // will show as '03' instead of ' 3'
 60  0
         translate.put("F", "yyyy-MM-dd");
 61  0
         translate.put("g", "yy");
 62  0
         translate.put("G", "yyyy");
 63  0
         translate.put("H", "HH");
 64  0
         translate.put("h", "MMM");
 65  0
         translate.put("I", "hh");
 66  0
         translate.put("j", "DDD");
 67  0
         translate.put("k", "HH"); // will show as '07' instead of ' 7'
 68  0
         translate.put("l", "hh"); // will show as '07' instead of ' 7'
 69  0
         translate.put("m", "MM");
 70  0
         translate.put("M", "mm");
 71  0
         translate.put("n", "\n");
 72  0
         translate.put("p", "a");
 73  0
         translate.put("P", "a"); // will show as pm instead of PM
 74  0
         translate.put("r", "hh:mm:ss a");
 75  0
         translate.put("R", "HH:mm");
 76  
         // There's no way to specify this with SimpleDateFormat
 77  
         // translate.put("s","seconds since ecpoch");
 78  0
         translate.put("S", "ss");
 79  0
         translate.put("t", "\t");
 80  0
         translate.put("T", "HH:mm:ss");
 81  
         // There's no way to specify this with SimpleDateFormat
 82  
         // translate.put("u","day of week ( 1-7 )");
 83  
 
 84  
         // There's no way to specify this with SimpleDateFormat
 85  
         // translate.put("U","week in year with first sunday as first day...");
 86  
 
 87  0
         translate.put("V", "ww"); // I'm not sure this is always exactly the same
 88  
 
 89  
         // There's no way to specify this with SimpleDateFormat
 90  
         // translate.put("W","week in year with first monday as first day...");
 91  
 
 92  
         // There's no way to specify this with SimpleDateFormat
 93  
         // translate.put("w","E");
 94  0
         translate.put("X", "HH:mm:ss");
 95  0
         translate.put("x", "MM/dd/yy");
 96  0
         translate.put("y", "yy");
 97  0
         translate.put("Y", "yyyy");
 98  0
         translate.put("Z", "z");
 99  0
         translate.put("z", "Z");
 100  0
         translate.put("%", "%");
 101  
         
 102  0
         pTranslate = new Properties();
 103  0
         pTranslate.put("EEE", "%a");
 104  0
         pTranslate.put("EEEE", "%A");
 105  0
         pTranslate.put("MMM", "%b");
 106  0
         pTranslate.put("MMMM", "%B");
 107  0
         pTranslate.put("EEE MMM d HH:mm:ss yyyy", "%c");
 108  
 
 109  
         // There's no way to specify the century in SimpleDateFormat. We don't want to hard-code
 110  
         // 20 since this could be wrong for the pre-2000 files.
 111  
         // translate.put("C", "20");
 112  0
         pTranslate.put("dd", "%d");
 113  0
         pTranslate.put("MM/dd/yy", "%D");
 114  0
         pTranslate.put("yyyy-MM-dd", "%F");
 115  0
         pTranslate.put("yy", "%g");
 116  0
         pTranslate.put("yyyy", "%G");
 117  0
         pTranslate.put("HH", "%H");
 118  0
         pTranslate.put("MMM", "%h");
 119  0
         pTranslate.put("hh", "%I");
 120  0
         pTranslate.put("DDD", "%j");
 121  0
         pTranslate.put("MM", "%m");
 122  0
         pTranslate.put("mm", "%M");
 123  0
         pTranslate.put("\n", "%n");
 124  0
         pTranslate.put("a", "%p"); // will show as pm instead of PM
 125  0
         pTranslate.put("hh:mm:ss a", "%r");
 126  0
         pTranslate.put("HH:mm", "%R");
 127  
         // There's no way to specify this with SimpleDateFormat
 128  
         // translate.put("s","seconds since ecpoch");
 129  0
         pTranslate.put("ss", "%S");
 130  0
         pTranslate.put("\t", "%t");
 131  0
         pTranslate.put("HH:mm:ss", "%T");
 132  
         // There's no way to specify this with SimpleDateFormat
 133  
         // translate.put("u","day of week ( 1-7 )");
 134  
 
 135  
         // There's no way to specify this with SimpleDateFormat
 136  
         // translate.put("U","week in year with first sunday as first day...");
 137  
 
 138  0
         pTranslate.put("ww", "%V"); // I'm not sure this is always exactly the same
 139  
 
 140  
         // There's no way to specify this with SimpleDateFormat
 141  
         // translate.put("W","week in year with first monday as first day...");
 142  
 
 143  
         // There's no way to specify this with SimpleDateFormat
 144  
         // translate.put("w","E");
 145  0
         pTranslate.put("HH:mm:ss", "%X");
 146  0
         pTranslate.put("MM/dd/yy", "%x");
 147  0
         pTranslate.put("yy", "%y");
 148  0
         pTranslate.put("yyyy", "%Y");
 149  0
         pTranslate.put("z", "%Z");
 150  0
         pTranslate.put("Z", "%z");
 151  0
         pTranslate.put("%", "%");
 152  0
     }
 153  
 
 154  
     /**
 155  
      * Create an instance of this date formatting class.
 156  
      * 
 157  
      * @see #Strftime( String, Locale )
 158  
      */
 159  
     public Strftime(String origFormat)
 160  0
     {
 161  0
         String convertedFormat = convertDateFormat(origFormat);
 162  0
         simpleDateFormat = new SimpleDateFormat(convertedFormat);
 163  0
     }
 164  
 
 165  
     /**
 166  
      * Create an instance of this date formatting class.
 167  
      * 
 168  
      * @param origFormat
 169  
      *            the strftime-style formatting string
 170  
      * @param locale
 171  
      *            the locale to use for locale-specific conversions
 172  
      */
 173  
     public Strftime(String origFormat, Locale locale)
 174  0
     {
 175  0
         String convertedFormat = convertDateFormat(origFormat);
 176  0
         simpleDateFormat = new SimpleDateFormat(convertedFormat, locale);
 177  0
     }
 178  
 
 179  
     /**
 180  
      * Format the date according to the strftime-style string given in the constructor.
 181  
      * 
 182  
      * @param date
 183  
      *            the date to format
 184  
      * @return the formatted date
 185  
      */
 186  
     public String format(Date date)
 187  
     {
 188  0
         return simpleDateFormat.format(date);
 189  
     }
 190  
 
 191  
     /**
 192  
      * Parses the input. 
 193  
      * 
 194  
      * @see java.text.SimpleDateFormat#parse(String)
 195  
      * @param input The string to parse.
 196  
      * @return A parsed {@link Date}.
 197  
      * @throws ParseException On input error.
 198  
      */
 199  
     public Date parse(String input)
 200  
     throws ParseException
 201  
     {
 202  0
         return simpleDateFormat.parse(input);
 203  
     }
 204  
     
 205  
     /**
 206  
      * Get the timezone used for formatting conversions.
 207  
      * 
 208  
      * @return the timezone
 209  
      */
 210  
     public TimeZone getTimeZone()
 211  
     {
 212  0
         return simpleDateFormat.getTimeZone();
 213  
     }
 214  
 
 215  
     /**
 216  
      * Change the timezone used to format dates.
 217  
      * 
 218  
      * @see SimpleDateFormat#setTimeZone(TimeZone)
 219  
      */
 220  
     public void setTimeZone(TimeZone timeZone)
 221  
     {
 222  0
         simpleDateFormat.setTimeZone(timeZone);
 223  0
     }
 224  
     
 225  
     /**
 226  
      * Does the exact opposite of {{@link #convertDateFormat(String)} by converting
 227  
      * the incoming java date format string into a POSIX compliant format string.
 228  
      * @param pattern The java date format style format
 229  
      * @return The converted format into something usable by POSIX strftime style parser/formatters.
 230  
      */
 231  
     public static String convertToPosixFormat(String pattern)
 232  
     {
 233  0
         if (pattern == null) return null;
 234  
         
 235  0
         StringBuffer buf = new StringBuffer();
 236  0
         int start=-1;
 237  
         
 238  0
         for(int i = 0; i < pattern.length(); i++) {
 239  0
             char c = pattern.charAt(i);
 240  
             
 241  
             // if in a definition
 242  0
             if (Character.isLetter(c)) {
 243  0
                 if (start <= -1) start = i;
 244  
                 continue;
 245  0
             } else if (start >= 0) {
 246  
                 // we've hit the end of a definition
 247  0
                 String conv = pattern.substring(start, i);
 248  0
                 String match = pTranslate.getProperty(conv);
 249  
                 
 250  0
                 if (match == null)
 251  0
                     buf.append(conv); // just append it, this shouldn't happen we hope
 252  
                 else
 253  0
                     buf.append(match);
 254  
                 
 255  
                 // reset
 256  0
                 start=-1;
 257  
             }
 258  
             
 259  0
             buf.append(c);
 260  
         }
 261  
         
 262  
         // grab last one, if any
 263  0
         if (start > -1) {
 264  0
             String conv = pattern.substring(start, pattern.length());
 265  0
             String match = pTranslate.getProperty(conv);
 266  0
             if (match == null) buf.append(conv);
 267  0
             else buf.append(match);
 268  
         }
 269  
         
 270  0
         return buf.toString();
 271  
     }
 272  
     
 273  
     /**
 274  
      * Search the provided pattern and get the C standard Date/Time formatting rules and convert
 275  
      * them to the Java equivalent.
 276  
      * 
 277  
      * @param pattern
 278  
      *            The pattern to search
 279  
      * @return The modified pattern
 280  
      */
 281  
     public static String convertDateFormat(String pattern)
 282  
     {
 283  0
         boolean inside = false;
 284  0
         boolean mark = false;
 285  0
         boolean modifiedCommand = false;
 286  
         
 287  0
         StringBuffer buf = new StringBuffer();
 288  
         
 289  0
         for(int i = 0; i < pattern.length(); i++) {
 290  0
             char c = pattern.charAt(i);
 291  
 
 292  0
             if (c == '%' && !mark) {
 293  0
                 mark = true;
 294  
             } else {
 295  0
                 if (mark) {
 296  0
                     if (modifiedCommand) {
 297  
                         // don't do anything--we just wanted to skip a char
 298  0
                         modifiedCommand = false;
 299  0
                         mark = false;
 300  
                     } else {
 301  0
                         inside = translateCommand(buf, pattern, i, inside);
 302  
                         // It's a modifier code
 303  0
                         if (c == 'O' || c == 'E') {
 304  0
                             modifiedCommand = true;
 305  
                         } else {
 306  0
                             mark = false;
 307  
                         }
 308  
                     }
 309  
                 } else {
 310  0
                     if (!inside && c != ' ') {
 311  
                         // We start a literal, which we need to quote
 312  0
                         buf.append("'");
 313  0
                         inside = true;
 314  
                     }
 315  
 
 316  0
                     buf.append(c);
 317  
                 }
 318  
             }
 319  
         }
 320  
 
 321  0
         if (buf.length() > 0) {
 322  0
             char lastChar = buf.charAt(buf.length() - 1);
 323  
 
 324  0
             if (lastChar != '\'' && inside) {
 325  0
                 buf.append('\'');
 326  
             }
 327  
         }
 328  0
         return buf.toString();
 329  
     }
 330  
 
 331  
     private static String quote(String str, boolean insideQuotes)
 332  
     {
 333  0
         String retVal = str;
 334  0
         if (!insideQuotes) {
 335  0
             retVal = '\'' + retVal + '\'';
 336  
         }
 337  0
         return retVal;
 338  
     }
 339  
 
 340  
     /**
 341  
      * try to get the Java Date/Time formating associated with the C standard provided.
 342  
      * 
 343  
      * @param c
 344  
      *            The C equivalent to translate
 345  
      * @return The Java formatting rule to use
 346  
      */
 347  
     private static boolean translateCommand(StringBuffer buf, String pattern, int index,
 348  
             boolean oldInside)
 349  
     {
 350  0
         char firstChar = pattern.charAt(index);
 351  0
         boolean newInside = oldInside;
 352  
 
 353  
         // O and E are modifiers, they mean to present an alternative representation of the next
 354  
         // char
 355  
         // we just handle the next char as if the O or E wasn't there
 356  0
         if (firstChar == 'O' || firstChar == 'E') {
 357  0
             if (index + 1 < pattern.length()) {
 358  0
                 newInside = translateCommand(buf, pattern, index + 1, oldInside);
 359  
             } else {
 360  0
                 buf.append(quote("%" + firstChar, oldInside));
 361  
             }
 362  
         } else {
 363  0
             String command = translate.getProperty(String.valueOf(firstChar));
 364  
 
 365  
             // If we don't find a format, treat it as a literal--That's what apache does
 366  0
             if (command == null) {
 367  0
                 buf.append(quote("%" + firstChar, oldInside));
 368  
             } else {
 369  
                 // If we were inside quotes, close the quotes
 370  0
                 if (oldInside) {
 371  0
                     buf.append('\'');
 372  
                 }
 373  0
                 buf.append(command);
 374  0
                 newInside = false;
 375  
             }
 376  
         }
 377  0
         return newInside;
 378  
     }
 379  
 }