// (c) 2003 Allen I Holub. All rights reserved.
package com.holub.tools;
import java.text.*;
import java.util.*;
import java.util.regex.*;
//import com.holub.io.P; // for debugging only
/* The DateUtil utility provides several workhorse methods that
* use Java's formatters to parse and format dates, times, etc.
*
* <!-- ====================== distribution terms ===================== -->
* <p><blockquote
* style="border-style: solid; border-width:thin; padding: 1em 1em 1em 1em;">
* <center>
* Copyright © 2003, Allen I. Holub. All rights reserved.
* </center>
* <br>
* <br>
* This code is distributed under the terms of the
* <a href="http://www.gnu.org/licenses/gpl.html"
* >GNU Public License</a> (GPL)
* with the following ammendment to section 2.c:
* <p>
* As a requirement for distributing this code, your splash screen,
* about box, or equivalent must include an my name, copyright,
* <em>and URL</em>. An acceptable message would be:
* <center>
* This program contains Allen Holub's <em>XXX</em> utility.<br>
* (c) 2003 Allen I. Holub. All Rights Reserved.<br>
* http://www.holub.com<br>
* </center>
* If your progam does not run interactively, then the foregoing
* notice must appear in your documentation.
* </blockquote>
* <!-- =============================================================== -->
* @author Allen I. Holub
*/
public class DateUtil
{
/** Return a string showing all supported time formats, useful
* for help messages.
* @param asHtml if true, then a <br> is used between formats
* rather than a newline. If false, the lines are terminated
* by a <code>" \n"</code>. (The space in front of the newline makes the
* string look reasonable, even if newlines are ignored.)
*/
static public String supportedDateFormats( boolean asHtml )
{
Date now = new Date();
return
DateFormat.getDateInstance(DateFormat.FULL).format(now)
+ (asHtml ? "<br>" : " \n")
+ DateFormat.getDateInstance(DateFormat.LONG).format(now)
+ (asHtml ? "<br>" : " \n")
+ DateFormat.getDateInstance(DateFormat.MEDIUM).format(now)
+ (asHtml ? "<br>" : " \n")
+ DateFormat.getDateInstance(DateFormat.SHORT).format(now)
+ (asHtml ? "<br>" : " \n")
+ "If you leave out the year, the current year is used."
+ (asHtml ? "<br>" : " \n")
+ "You can use dashes instead of slashes."
+ (asHtml ? "<br>" : " \n")
;
}
/** Return a string showing all supported time formats, useful
* for help messages.
* @param asHtml if true, then a <br> is used between formats
* rather than a newline.
*/
static public String supportedTimeFormats( boolean asHtml )
{
Date now = new Date();
return
DateFormat.getTimeInstance(DateFormat.FULL).format(now)
+ (asHtml ? "<br>" : " \n")
+ DateFormat.getTimeInstance(DateFormat.LONG).format(now)
+ (asHtml ? "<br>" : " \n")
+ DateFormat.getTimeInstance(DateFormat.MEDIUM).format(now)
+ (asHtml ? "<br>" : " \n")
+ DateFormat.getTimeInstance(DateFormat.SHORT).format(now)
;
}
/** Parse the date from a string and return it. This method tries
* all the formats supported by {@link DateFormat} (FULL, LONG,
* MEDIUM, and SHORT in that order). It also recognises a few
* formats not handled by the <code>DateFormat</code> class.
* <p> In an attempt to make date entry a bit more user friendly,
* In particular, the following are recognized correctly:
* <table border=1 cellspacing=0 cellpadding=1>
* <tr><td> Input </td><td> Recognized as if they were: </td></tr>
* </table>
*
* @return an initialized {@link java.util.Date} object or
* <code>null</code> if it can't parse the date.
* (It does not throw <code>ParseException</code>)
*/
static public Date parseDate( String str )
{ // assert str != null;
if( str.length() == 0 )
return null;
str = str.replace('-', '/'); // just in case.
Matcher m = dayFirst.matcher( str ); // 1 Jan instead of Jan 1
if( m.find() )
{ str = m.replaceFirst(m.group(2) + " " + m.group(1));
}
m = noYear.matcher( str ); // add a year to 10/15 format.
if( m.find() )
str += ( "/" + thisYear );
Date parsed = tryToParse(str);
if( parsed == null )
parsed = tryToParse( str + ", " + thisYear ); // try adding a year
return parsed;
}
static private Date tryToParse( String str )
{
Date date = null;
try
{ DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL);
formatter.setLenient( true );
date = formatter.parse(str);
}
catch (ParseException pel) { try
{ DateFormat formatter = DateFormat.getDateInstance(DateFormat.LONG);
formatter.setLenient( true );
date = formatter.parse(str);
}
catch (ParseException pe2) { try
{ DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM);
formatter.setLenient( true );
date = formatter.parse(str);
}
catch (ParseException pe3) { try
{ DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT);
formatter.setLenient( true );
date = formatter.parse(str);
}
catch( ParseException pe4 )
{ return null;
}
}}}
return date;
}
private static final Pattern noYear = Pattern.compile(
"\\s*[0-9]+\\/[0-9]+$");
private static final Pattern dayFirst = Pattern.compile(
"([0-9]+)\\s+([A-Za-z]+)");
private static final String thisYear = String.valueOf(
Calendar.getInstance().get(Calendar.YEAR));
/** Parse the time from a string and return it. This method tries
* all the formats supported by {@link DateFormat} (FULL, LONG,
* MEDIUM, and SHORT in that order).
* @return an initialized {@link java.util.Date} object or
* <code>null</code> if it can't parse the time.
* (It does not throw <code>ParseException</code>)
*/
static public Date parseTime(String str)
{
// assert str != null;
Date date = null;
try
{ date = DateFormat.getTimeInstance(DateFormat.FULL).
parse(str);
}
catch (ParseException pel)
{ try
{ date = DateFormat.getTimeInstance(DateFormat.LONG).
parse(str);
}
catch (ParseException pe2)
{ try
{ date = DateFormat.getTimeInstance(DateFormat.MEDIUM).
parse(str);
}
catch (ParseException pe3)
{ try
{ date = DateFormat.
getTimeInstance(DateFormat.SHORT).
parse(str);
}
catch( ParseException pe4 ){}
}
}
}
return date;
}
/** Return a string holding just the date. The following format is used:
* <pre>
* Thursday, May 29, 2003
* </pre>
*/
static public String asString( Date date )
{ return DateFormat.getDateInstance(DateFormat.FULL).format(date);
}
/** Return a string holding just the time. The following format is used:
* <pre>
* 11:42:44 AM
* </pre>
*/
static public String timeAsString( Date time )
{ return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(time);
}
/** Return a string holding the time and date in the form:
* <pre>
* Thursday, May 29, 2003 11:42:44 AM
* </pre>
*/
static public String timestamp( Date dateAndTime )
{ return DateFormat.getDateTimeInstance( DateFormat.FULL,
DateFormat.MEDIUM).
format(dateAndTime);
}
/*
* Return a string holding a fomatted date or time.
* The <code>format</code> argument specifies what
* the returned string looks like. Most characters in the
* format string are placed in the output, however the
* following character sequences are replaced as follows:
* <table border=1 cellspacing=0 cellpadding=1>
* <tr><td> %m </td><td>Month (numeral) </td></tr>
* <tr><td> %M </td><td>Month (word) </td></tr>
* <tr><td> %d </td><td>day of the month (numeral) </td></tr>
* <tr><td> %y </td><td>year (two digits) </td></tr>
* <tr><td> %Y </td><td>year (four digits) </td></tr>
* <tr><td> %w </td><td>Day of week as 3-character abbreviation</td></tr>
* <tr><td> %W </td><td>Day of week spelled out </td></tr>
* </table>
*
* @format Format-specifier string.
static public String format( String specifier, Date d )
{ StringBuffer b = new StringBuffer();
int end = specifier.length;
Calendar calendar = new Calendar(d);
for( int i = 0; i < end; ++i )
{ char c = specifier.charAt(i);
if( c != '%' )
b.append(c);
else if( ++i < end )
{ c = specifier.charAt(i);
switch(c)
{
case 'm':
case 'M':
case 'd':
case 'y':
case 'Y':
case 'w':
case 'W':
}
}
}
}
*/
private static class Test
{ public static void main( String[] args )
{/*
P.rintln("Supported date formats are: ");
P.rintln( supportedDateFormats(false) );
P.rintln("\nSupported time formats are: ");
P.rintln( supportedTimeFormats(false) );
P.rintln("");
Date now = new Date();
P.rintln( asString(now) );
P.rintln( timeAsString(now) );
P.rintln( timestamp(now) );
String[] tests = new String[]
{ "1/2/34",
"January 2, 1934",
"1/2",
"January 2",
"2 January, 1934",
"2 January",
"Oct 8",
};
for( int i = 0; i < tests.length; ++i )
{ Date then = parseDate( tests[i] );
if( then == null )
P.rintln( "FAILURE: parseDate(" + tests[i] + ")" );
else
P.rintln( "SUCCESS: parseDate(" + tests[i] + "): " + asString(then) );
}
// assert parseDate( "" ) == null : "FAILURE: Parsing empty string";
// assert parseDate( null ) == null : "FAILURE: Parsing null string";
*/}
}
}