Java/GWT/Date Utilities
A simple regular expression based parser for date notations
<source lang="java">
package org.gwtwidgets.client.util; import java.util.Date; import org.gwtwidgets.client.util.regex.Pattern; /**
* This is a simple regular expression based parser for date notations. * While our aim is to fully support in the future the JDK date parser, currently * only numeric notations and literals are supported such asdd/MM/yyyy HH:mm:ss.SSSS
. * Each entity is parsed with the same number of digits, i.e. fordd
two digits will be * parsed while ford
only one will be parsed. * @author * */
public class SimpleDateParser {
private final static String DAY_IN_MONTH = "d"; private final static String MONTH = "M"; private final static String YEAR = "y"; private final static String LITERAL = "\\"; private final static int DATE_PATTERN = 0; private final static int REGEX_PATTERN = 1; private final static int COMPONENT = 2; private final static int REGEX = 0; private final static int INSTRUCTION = 1; private final static String[] TOKENS[] = { { "SSSS", "(\\d\\d\\d\\d)",DateLocale.TOKEN_MILLISECOND }, { "SSS", "(\\d\\d\\d)", DateLocale.TOKEN_MILLISECOND }, { "SS", "(\\d\\d)", DateLocale.TOKEN_MILLISECOND }, { "S", "(\\d)", DateLocale.TOKEN_MILLISECOND }, { "ss", "(\\d\\d)", DateLocale.TOKEN_SECOND }, { "s", "(\\d)", DateLocale.TOKEN_SECOND }, { "mm", "(\\d\\d)", DateLocale.TOKEN_MINUTE }, { "m", "(\\d)", DateLocale.TOKEN_MINUTE}, { "HH", "(\\d\\d)", DateLocale.TOKEN_HOUR_24}, { "H", "(\\d)", DateLocale.TOKEN_HOUR_24 }, { "dd", "(\\d\\d)", DateLocale.TOKEN_DAY_OF_MONTH }, { "d", "(\\d)", DateLocale.TOKEN_DAY_OF_MONTH }, { "MM", "(\\d\\d)", DateLocale.TOKEN_MONTH }, { "M", "(\\d)", DateLocale.TOKEN_MONTH }, { "yyyy", "(\\d\\d\\d\\d)", DateLocale.TOKEN_YEAR }, { "yyy", "(\\d\\d\\d)", DateLocale.TOKEN_YEAR }, { "yy", "(\\d\\d)", DateLocale.TOKEN_YEAR }, { "y", "(\\d)", DateLocale.TOKEN_YEAR } }; private Pattern regularExpression;; private String instructions = ""; private static void _parse(String format, String[] args) { if (format.length() == 0) return; if (format.startsWith(""")){ format = format.substring(1); int end = format.indexOf("""); if (end == -1) throw new IllegalArgumentException("Unmatched single quotes."); args[REGEX]+=Pattern.quote(format.substring(0,end)); format = format.substring(end+1); } for (int i = 0; i < TOKENS.length; i++) { String[] row = TOKENS[i]; String datePattern = row[DATE_PATTERN]; if (!format.startsWith(datePattern)) continue; format = format.substring(datePattern.length()); args[REGEX] += row[REGEX_PATTERN]; args[INSTRUCTION] += row[COMPONENT]; _parse(format, args); return; } args[REGEX] += Pattern.quote(""+format.charAt(0)); format = format.substring(1); _parse(format, args); } private static void load(Date date, String text, String component) { if (component.equals(DateLocale.TOKEN_MILLISECOND)) { //TODO: implement } if (component.equals(DateLocale.TOKEN_SECOND)) { date.setSeconds(Integer.parseInt(text)); } if (component.equals(DateLocale.TOKEN_MINUTE)) { date.setMinutes(Integer.parseInt(text)); } if (component.equals(DateLocale.TOKEN_HOUR_24)) { date.setHours(Integer.parseInt(text)); } if (component.equals(DateLocale.TOKEN_DAY_OF_MONTH)) { date.setDate(Integer.parseInt(text)); } if (component.equals(DateLocale.TOKEN_MONTH)) { date.setMonth(Integer.parseInt(text)-1); } if (component.equals(DateLocale.TOKEN_YEAR)) { //TODO: fix for short patterns date.setYear(Integer.parseInt(text)-1900); } } public SimpleDateParser(String format) { String[] args = new String[] { "", "" }; _parse(format, args); regularExpression = new Pattern(args[REGEX]); instructions = args[INSTRUCTION]; } public Date parse(String input) { Date date = new Date(0, 0, 0, 0, 0, 0); String matches[] = regularExpression.match(input); if (matches == null) throw new IllegalArgumentException(input+" does not match "+regularExpression.pattern()); if (matches.length-1!=instructions.length()) throw new IllegalArgumentException("Different group count - "+input+" does not match "+regularExpression.pattern()); for (int group = 0; group < instructions.length(); group++) { String match = matches[group + 1]; load(date, match, ""+instructions.charAt(group)); } return date; } public static Date parse(String input, String pattern){ return new SimpleDateParser(pattern).parse(input); }
}
</source>
Date locale support for the SimpleDateParser
<source lang="java">
package org.gwtwidgets.client.util; import java.util.Arrays; import java.util.List; /**
* Date locale support for the {@link SimpleDateParser}. You are encouraged to * extend this class and provide implementations for other locales. * @author * */
public class DateLocale {
public final static String TOKEN_DAY_OF_WEEK = "E"; public final static String TOKEN_DAY_OF_MONTH = "d"; public final static String TOKEN_MONTH = "M"; public final static String TOKEN_YEAR = "y"; public final static String TOKEN_HOUR_12 = "h"; public final static String TOKEN_HOUR_24 = "H"; public final static String TOKEN_MINUTE = "m"; public final static String TOKEN_SECOND = "s"; public final static String TOKEN_MILLISECOND = "S"; public final static String TOKEN_AM_PM = "a"; public final static String AM = "AM"; public final static String PM = "PM"; public final static List SUPPORTED_DF_TOKENS = Arrays.asList(new String[] { TOKEN_DAY_OF_WEEK, TOKEN_DAY_OF_MONTH, TOKEN_MONTH, TOKEN_YEAR, TOKEN_HOUR_12, TOKEN_HOUR_24, TOKEN_MINUTE, TOKEN_SECOND, TOKEN_AM_PM }); public String[] MONTH_LONG = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; public String[] MONTH_SHORT = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" }; public String[] WEEKDAY_LONG = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; public String[] WEEKDAY_SHORT = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; public static String getAM() { return AM; } public static String getPM() { return PM; } public String[] getWEEKDAY_LONG() { return WEEKDAY_LONG; } public String[] getWEEKDAY_SHORT() { return WEEKDAY_SHORT; }
}
</source>
Implement SimpleDateFormat for GWT
<source lang="java">
/*
* Copyright 2006 Robert Hanson <iamroberthanson AT gmail.ru> * * 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.gwtwidgets.client.util; import java.util.Date; /**
*-
*
- Title: *
- SimpleDateFormat *
- Description: *
- GWT does not implement any of the java.text package, so this class tries
* to fill the void of the missing java.text.SimpleDateFormat class. This
* version however only supports a subset of the date and time patterns
* supported by its java.text counterpart. The pattern symbols supported by this
* class are:
*
-
*
- E *
- Day in a week *
- d *
- Day of the month *
- y *
- Year *
- M *
- Month January, Jan, 01, 1 *
- H *
- Hour in 24 hour format (0-23) *
- h *
- Hour in 12 hour format (1-12) *
- m *
- Minute of the hour *
- s *
- Seconds of the minute *
- a *
- am/pm *
* All characters that are not recognised as a date format character are * translated literally into the output string.
* <p>
* </dl>
*
*
* A simple date parsing facility has also been implemented resembling the java * prototype. You can currently parse most numeric patterns but no temporal * literals (such as day or month names). *
* * @author * @version $Revision: 0.0 $ */
public class SimpleDateFormat {
private String format; private DateLocale locale = new DateLocale(); /** * Gets the support locale for formatting and parsing dates * @return */ public DateLocale getLocale() { return locale; } public void setLocale(DateLocale locale) { this.locale = locale; } public SimpleDateFormat(String pattern) { format = pattern; } public String format(Date date) { String f = ""; if (format != null && format.length() > 0) { String lastTokenType = null; String currentToken = ""; for (int i = 0; i < format.length(); i++) { String thisChar = format.substring(i, i + 1); String currentTokenType = DateLocale.SUPPORTED_DF_TOKENS .contains(thisChar) ? thisChar : ""; if (currentTokenType.equals(lastTokenType) || i == 0) { currentToken += thisChar; lastTokenType = currentTokenType; } else { if ("".equals(lastTokenType)) f += currentToken; else f += handleToken(currentToken, date); currentToken = thisChar; lastTokenType = currentTokenType; } } if ("".equals(lastTokenType)) f += currentToken; else f += handleToken(currentToken, date); } return f; } /** * takes a date format string and returns the formatted portion of the date. * For instance if the token is MMMM then the full month name is returned. * * @param token * date format token * @param date * date to format * @return formatted portion of the date */ private String handleToken(String token, Date date) { String response = token; String tc = token.substring(0, 1); if (DateLocale.TOKEN_DAY_OF_WEEK.equals(tc)) { if (token.length() > 3) response = locale.getWEEKDAY_LONG()[date.getDay()]; else response = locale.getWEEKDAY_SHORT()[date.getDay()]; } else if (DateLocale.TOKEN_DAY_OF_MONTH.equals(tc)) { if (token.length() == 1) response = Integer.toString(date.getDate()); else response = twoCharDateField(date.getDate()); } else if (DateLocale.TOKEN_MONTH.equals(tc)) { switch (token.length()) { case 1: response = Integer.toString(date.getMonth() + 1); break; case 2: response = twoCharDateField(date.getMonth() + 1); break; case 3: response = locale.MONTH_SHORT[date.getMonth()]; break; default: response = locale.MONTH_LONG[date.getMonth()]; break; } } else if (DateLocale.TOKEN_YEAR.equals(tc)) { if (token.length() > 2) response = Integer.toString(date.getYear() + 1900); else response = twoCharDateField(date.getYear()); } else if (DateLocale.TOKEN_HOUR_12.equals(tc)) { int h = date.getHours(); if (h == 0) h = 12; else if (h > 12) h -= 12; if (token.length() > 1) response = twoCharDateField(h); else response = Integer.toString(h); } else if (DateLocale.TOKEN_HOUR_24.equals(tc)) { if (token.length() > 1) response = twoCharDateField(date.getHours()); else response = Integer.toString(date.getHours()); } else if (DateLocale.TOKEN_MINUTE.equals(tc)) { if (token.length() > 1) response = twoCharDateField(date.getMinutes()); else response = Integer.toString(date.getMinutes()); } else if (DateLocale.TOKEN_SECOND.equals(tc)) { if (token.length() > 1) response = twoCharDateField(date.getSeconds()); else response = Integer.toString(date.getSeconds()); } else if (DateLocale.TOKEN_AM_PM.equals(tc)) { int hour = date.getHours(); if (hour > 11) response = DateLocale.getPM(); else response = DateLocale.getAM(); } return response; } /** * This is basically just a sneaky way to guarantee that our 1 or 2 digit * numbers come out as a 2 character string. we add an arbitrary number * larger than 100, convert this new number to a string, then take the right * most 2 characters. * * @param num * @return */ private String twoCharDateField(int num) { String res = Integer.toString(num + 1900); res = res.substring(res.length() - 2); return res; } private static Date newDate(long time) { return new Date(time); } /** * Parses text and returns the corresponding date object. * * @param source * @return java.util.Date */ public Date parse(String source){ return SimpleDateParser.parse(source, format); };
}
</source>