001 package hirondelle.web4j.config; 002 003 import java.util.*; 004 import java.text.*; 005 import java.util.regex.*; 006 007 import hirondelle.web4j.model.DateTime; 008 import hirondelle.web4j.request.DateConverter; 009 010 /** 011 Implementation of {@link DateConverter}, required by WEB4J. 012 013 <P>Dates are formatted in the style, using Jan 31, 2006 at 01:59:59 as an example: 014 <ul> 015 <li>eye-friendly format: '01/31/2006 01:59:59'; any trailing '00:00:00' or ':' is removed. 016 <li>hand-friendly format: '01312006 01:59:59'; if time portion is absent, 00:00:00 is assumed. 017 </ul> 018 */ 019 public final class DateConverterImpl implements DateConverter { 020 021 public String formatEyeFriendlyDateTime(DateTime aDateTime, Locale aLocale){ 022 return chopOffColon(chopOffMidnight(aDateTime.format("MM/DD/YYYY hh:mm:ss"))); 023 } 024 025 public DateTime parseEyeFriendlyDateTime(String aInputValue, Locale aLocale){ 026 return parseDateTime(aInputValue, EYE_FRIENDLY_REGEX); 027 } 028 029 public DateTime parseHandFriendlyDateTime(String aInputValue, Locale aLocale){ 030 return parseDateTime(aInputValue, HAND_FRIENDLY_REGEX); 031 } 032 033 public String formatEyeFriendly(Date aDate, Locale aLocale, TimeZone aTimeZone) { 034 SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); //HH 00-23 035 format.setTimeZone(aTimeZone); 036 String result = format.format(aDate); 037 return chopOffMidnight(result); 038 } 039 040 public Date parseHandFriendly(String aInputValue, Locale aLocale, TimeZone aTimeZone) { 041 //if no time portion, then assume 00:00:00 042 return parse(aInputValue, HAND_FRIENDLY_REGEX, aTimeZone); 043 } 044 045 public Date parseEyeFriendly(String aInputValue, Locale aLocale, TimeZone aTimeZone) { 046 return parse(aInputValue, EYE_FRIENDLY_REGEX, aTimeZone); 047 } 048 049 // PRIVATE 050 051 /* 052 * Patterns are thread-safe. Matchers and SimpleDateFormats are NOT thread-safe. 053 * Items that are not thread-safe can be used only as local variables, not as fields. 054 */ 055 056 /** Month in the Gregorian calendar: <tt>01..12</tt>. */ 057 private static final String MONTH = 058 "(01|02|03|04|05|06|07|08|09|10|11|12)" 059 ; 060 061 /** Day of the month in the Gregorian calendar: <tt>01..31</tt>. */ 062 private static final String DAY_OF_MONTH = 063 "(01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)" 064 ; 065 066 /** Hours in the day <tt>00..23</tt>. */ 067 private static final String HOURS = 068 "(00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23)" 069 ; 070 071 /** Minutes in an hour <tt>00..59</tt>. */ 072 private static final String MINUTES = 073 "((?:0|1|2|3|4|5)\\d)" 074 ; 075 076 /** Seconds in a minute <tt>00..59</tt>. */ 077 private static final String SECONDS = 078 "((?:0|1|2|3|4|5)\\d)" 079 ; 080 081 /** Format : 01312006 01:59:00 */ 082 private static final Pattern HAND_FRIENDLY_REGEX = 083 Pattern.compile(MONTH + DAY_OF_MONTH + "(\\d\\d\\d\\d)" + "(?: " + HOURS + ":" + MINUTES + ":" + SECONDS + ")?") 084 ; 085 086 /** Format : 01/31/2006 01:59:00 */ 087 private static final Pattern EYE_FRIENDLY_REGEX = 088 Pattern.compile(MONTH + "/" + DAY_OF_MONTH + "/" + "(\\d\\d\\d\\d)" + "(?: " + HOURS + ":" + MINUTES + ":" + SECONDS + ")?") 089 ; 090 091 /** 092 * Requires the month, day, year to be the first, second, and third groups, respectively. 093 * Optionally, hours, minutes, and seconds can appear as 4th, 5th, and 6th groups, respectively. 094 */ 095 private Date parse(String aInputValue, Pattern aRegex, TimeZone aTimeZone){ 096 Date result = null; 097 Matcher matcher = aRegex.matcher(aInputValue); 098 if( matcher.matches() ) { 099 Integer month = new Integer(matcher.group(1)); 100 Integer day = new Integer(matcher.group(2)); 101 Integer year = new Integer( matcher.group(3) ); 102 String hour = matcher.group(4); 103 String minute = matcher.group(5); 104 String seconds = matcher.group(6); 105 Calendar cal = null; 106 if( hour == null ){ 107 cal = new GregorianCalendar(year.intValue(), month.intValue() - 1, day.intValue(), 0,0,0); 108 } 109 else { 110 Integer hourVal = new Integer(hour); 111 Integer minuteVal = new Integer(minute); 112 Integer secondsVal = new Integer(seconds); 113 cal = new GregorianCalendar(year.intValue(), month.intValue() - 1, day.intValue(), hourVal.intValue(), minuteVal.intValue(), secondsVal.intValue()); 114 } 115 cal.setTimeZone(aTimeZone); 116 result = cal.getTime(); 117 } 118 return result; 119 } 120 121 /** Simple 'struct' to hold related items. */ 122 private static final class DateTimeParts { 123 String year; 124 String month; 125 String day; 126 String hour; 127 String minute; 128 } 129 130 private DateTime parseDateTime(String aInputValue, Pattern aRegex){ 131 DateTime result = null; 132 Matcher matcher = aRegex.matcher(aInputValue); 133 if( matcher.matches() ) { 134 DateTimeParts parts = getParts(matcher); 135 Integer year = new Integer(parts.year); 136 Integer month = new Integer(parts.month); 137 Integer day = new Integer(parts.day); 138 String hour = parts.hour; 139 String minute = parts.minute; 140 if( hour == null ){ 141 result = DateTime.forDateOnly(year, month, day); 142 } 143 else { 144 Integer hourVal = new Integer(hour); 145 Integer minuteVal = new Integer(minute); 146 result = new DateTime(year, month, day, hourVal, minuteVal, null, null); 147 } 148 } 149 return result; 150 } 151 152 private DateTimeParts getParts(Matcher aMatcher){ 153 DateTimeParts result = new DateTimeParts(); 154 result.month = aMatcher.group(1); 155 result.day = aMatcher.group(2); 156 result.year = aMatcher.group(3); 157 result.hour = aMatcher.group(4); 158 result.minute = aMatcher.group(5); 159 return result; 160 } 161 162 private String chopOffMidnight(String aString){ 163 return chopOffUnwanted(aString, "00:00:00"); 164 } 165 166 private String chopOffColon(String aString){ 167 return chopOffUnwanted(aString, ":"); 168 } 169 170 private String chopOffUnwanted(String aString, String aUnwanted){ 171 String result = aString; 172 if ( aString.endsWith(aUnwanted) ) { 173 int end = aString.length() - aUnwanted.length() - 1; 174 result = result.substring(0,end); 175 } 176 return result; 177 } 178 179 }