Java/GWT/Date Utilities
A simple regular expression based parser for date notations
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 as <code>dd/MM/yyyy HH:mm:ss.SSSS</code>.
* Each entity is parsed with the same number of digits, i.e. for <code>dd</code> two digits will be
* parsed while for <code>d</code> 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);
}
}
Date locale support for the SimpleDateParser
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;
}
}
Implement SimpleDateFormat for GWT
/*
* 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;
/**
* <dl>
* <dt><b>Title: </b>
* <dd>SimpleDateFormat</dd>
* <p>
* <dt><b>Description: </b>
* <dd>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:
* <dl>
* <dt><b>E</b></dt>
* <dd>Day in a week</dd>
* <dt><b>d</b></dt>
* <dd>Day of the month</dd>
* <dt><b>y</b></dt>
* <dd>Year</dd>
* <dt><b>M</b></dt>
* <dd>Month January, Jan, 01, 1</dd>
* <dt><b>H</b></dt>
* <dd>Hour in 24 hour format (0-23)</dd>
* <dt><b>h</b></dt>
* <dd>Hour in 12 hour format (1-12)</dd>
* <dt><b>m</b></dt>
* <dd>Minute of the hour </dd>
* <dt><b>s</b></dt>
* <dd>Seconds of the minute</dd>
* <dt><b>a</b></dt>
* <dd>am/pm</dd>
* </dl>
* All characters that are not recognised as a date format character are
* translated literally into the output string. <br/> </dd>
* <p>
* </dl>
* <p>
* 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).
* </p>
*
* @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);
};
}