removed the weak assumptions for number of millis in a month/year from DateUtils. Implemented a second format method that relies on a start and an end in DurationFormatUtils, though I found that TimeZone was very important in the overloaded millis version. The two methods hand off to each other depending on whether the time is > or < than 28 days

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137935 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Henri Yandell 2004-09-27 03:14:15 +00:00
parent fc447adfef
commit 177d40989f
3 changed files with 113 additions and 15 deletions

View File

@ -31,7 +31,7 @@
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a> * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @author Phil Steitz * @author Phil Steitz
* @since 2.0 * @since 2.0
* @version $Id: DateUtils.java,v 1.29 2004/09/26 05:45:33 bayard Exp $ * @version $Id: DateUtils.java,v 1.30 2004/09/27 03:14:15 bayard Exp $
*/ */
public class DateUtils { public class DateUtils {
@ -56,10 +56,6 @@ public class DateUtils {
*/ */
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
// hmm. not very accurate. used by DurationFormatUtils
static final long MILLIS_PER_YEAR = 365 * MILLIS_PER_DAY + 6 * MILLIS_PER_HOUR;
static final long MILLIS_PER_MONTH = MILLIS_PER_YEAR / 12;
/** /**
* This is half a month, so this represents whether a date is in the top * This is half a month, so this represents whether a date is in the top
* or bottom half of the month. * or bottom half of the month.

View File

@ -17,6 +17,9 @@
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import java.util.Calendar;
import java.util.TimeZone;
/** /**
* <p>Duration formatting utilities and constants.</p> * <p>Duration formatting utilities and constants.</p>
* *
@ -27,7 +30,7 @@
* @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a> * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
* @author Henri Yandell * @author Henri Yandell
* @since 2.1 * @since 2.1
* @version $Id: DurationFormatUtils.java,v 1.14 2004/09/26 05:45:33 bayard Exp $ * @version $Id: DurationFormatUtils.java,v 1.15 2004/09/27 03:14:15 bayard Exp $
*/ */
public class DurationFormatUtils { public class DurationFormatUtils {
@ -101,9 +104,18 @@ public static String format(long millis, String format) {
return format(millis, format, true); return format(millis, format, true);
} }
public static String format(long millis, String format, boolean padWithZeros) { public static String format(long millis, String format, boolean padWithZeros) {
StringBuffer buffer = new StringBuffer(); return format(millis, format, padWithZeros, TimeZone.getDefault());
}
public static String format(long millis, String format, boolean padWithZeros, TimeZone timezone) {
if(millis > 28 * DateUtils.MILLIS_PER_DAY) {
Calendar c = Calendar.getInstance(timezone);
c.set(1970, 0, 1, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
return format(c.getTime().getTime(), millis, format, padWithZeros, timezone);
}
Token[] tokens = lexx(format); Token[] tokens = lexx(format);
int sz = tokens.length;
int years = 0; int years = 0;
int months = 0; int months = 0;
@ -113,6 +125,7 @@ public static String format(long millis, String format, boolean padWithZeros) {
int seconds = 0; int seconds = 0;
int milliseconds = 0; int milliseconds = 0;
/* This will never be evaluated
if(Token.containsTokenWithValue(tokens, y) ) { if(Token.containsTokenWithValue(tokens, y) ) {
years = (int) (millis / DateUtils.MILLIS_PER_YEAR); years = (int) (millis / DateUtils.MILLIS_PER_YEAR);
millis = millis - (years * DateUtils.MILLIS_PER_YEAR); millis = millis - (years * DateUtils.MILLIS_PER_YEAR);
@ -126,6 +139,7 @@ public static String format(long millis, String format, boolean padWithZeros) {
months = 0; months = 0;
} }
} }
*/
if(Token.containsTokenWithValue(tokens, d) ) { if(Token.containsTokenWithValue(tokens, d) ) {
days = (int) (millis / DateUtils.MILLIS_PER_DAY); days = (int) (millis / DateUtils.MILLIS_PER_DAY);
millis = millis - (days * DateUtils.MILLIS_PER_DAY); millis = millis - (days * DateUtils.MILLIS_PER_DAY);
@ -146,7 +160,15 @@ public static String format(long millis, String format, boolean padWithZeros) {
milliseconds = (int) millis; milliseconds = (int) millis;
} }
return formatDuration(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
private static String formatDuration(Token[] tokens, int years, int months, int days, int hours,
int minutes, int seconds, int milliseconds, boolean padWithZeros)
{
StringBuffer buffer = new StringBuffer();
int sz = tokens.length;
for(int i=0; i<sz; i++) { for(int i=0; i<sz; i++) {
Token token = tokens[i]; Token token = tokens[i];
Object value = token.getValue(); Object value = token.getValue();
@ -181,6 +203,87 @@ public static String format(long millis, String format, boolean padWithZeros) {
return buffer.toString(); return buffer.toString();
} }
// slower than the above I presume
public static String format(long startMillis, long endMillis, String format, boolean padWithZeros) {
return format(startMillis, endMillis, format, padWithZeros, TimeZone.getDefault());
}
public static String format(long startMillis, long endMillis, String format, boolean padWithZeros, TimeZone timezone) {
long millis = endMillis - startMillis;
if(millis < 28 * DateUtils.MILLIS_PER_DAY) {
return format(millis, format, padWithZeros, timezone);
}
Token[] tokens = lexx(format);
// timezones get funky around 0, so normalizing everything to GMT
// stops the hours being off
Calendar start = Calendar.getInstance(timezone);
start.setTimeInMillis(startMillis);
Calendar end = Calendar.getInstance(timezone);
end.setTimeInMillis(endMillis);
// initial estimates
int years = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
int months = end.get(Calendar.MONTH) - start.get(Calendar.MONTH);
// each initial estimate is adjusted in case it is under 0
while(months < 0) {
months += 12;
years -= 1;
}
int days = end.get(Calendar.DAY_OF_MONTH) - start.get(Calendar.DAY_OF_MONTH);
while(days < 0) {
days += 31; // such overshooting is taken care of later on
days -= 1;
}
int hours = end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY);
while(hours < 0) {
hours += 24;
days -= 1;
}
int minutes = end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE);
while(minutes < 0) {
minutes += 60;
hours -= 1;
}
int seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND);
while(seconds < 0) {
seconds += 60;
minutes -= 1;
}
int milliseconds = end.get(Calendar.MILLISECOND) - start.get(Calendar.MILLISECOND);
while(milliseconds < 0) {
milliseconds += 1000;
seconds -= 1;
}
// take estimates off of end to see if we can equal start, when it overshoots recalculate
milliseconds -= reduceAndCorrect( start, end, Calendar.MILLISECOND, milliseconds );
seconds -= reduceAndCorrect( start, end, Calendar.SECOND, seconds );
minutes -= reduceAndCorrect( start, end, Calendar.MINUTE, minutes );
hours -= reduceAndCorrect( start, end, Calendar.HOUR_OF_DAY, hours );
days -= reduceAndCorrect( start, end, Calendar.DAY_OF_MONTH, days );
months -= reduceAndCorrect( start, end, Calendar.MONTH, months );
years -= reduceAndCorrect( start, end, Calendar.YEAR, years );
return formatDuration(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros);
}
// Reduces by difference, then if it overshot, calculates the overshot amount and
// fixes and returns the amount to change by
private static int reduceAndCorrect(Calendar start, Calendar end, int field, int difference) {
end.add( field, -1 * difference );
int endValue = end.get(field);
int startValue = start.get(field);
if(endValue < startValue) {
int newdiff = startValue - endValue;
end.add( field, newdiff );
return newdiff;
} else {
return 0;
}
}
/** /**
* <p>Format an elapsed time into a plurialization correct string.</p> * <p>Format an elapsed time into a plurialization correct string.</p>
* *

View File

@ -157,16 +157,13 @@ public void testISODurationFormat(){
text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(cal); text = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(cal);
assertEquals("2002-02-23T09:11:12-03:00", text); assertEquals("2002-02-23T09:11:12-03:00", text);
// test fixture is the same as above, but now with extended format. // test fixture is the same as above, but now with extended format.
text = DurationFormatUtils.format(cal.getTime().getTime(), DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN, false); text = DurationFormatUtils.format(cal.getTime().getTime(), DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN, false, timeZone);
// TODO: The 1H41M here should be 9H11M. Again the year/month assumption. assertEquals("P32Y1M22DT9H11M12.1S", text);
System.err.println("T: "+text);
assertEquals("P32Y1M23DT1H41M12.1S", text);
// test fixture from example in http://www.w3.org/TR/xmlschema-2/#duration // test fixture from example in http://www.w3.org/TR/xmlschema-2/#duration
cal.set(1971, 1, 3, 10, 30, 0); cal.set(1971, 1, 3, 10, 30, 0);
cal.set(Calendar.MILLISECOND, 0); cal.set(Calendar.MILLISECOND, 0);
text = DurationFormatUtils.format(cal.getTime().getTime(), DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN, false); text = DurationFormatUtils.format(cal.getTime().getTime(), DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN, false, timeZone);
// TODO: The 2D21H here is wrong and should be larger. The Year/Month assumption in DurationFormatUtils. assertEquals("P1Y1M2DT10H30M0.0S", text);
assertEquals("P1Y1M2DT21H0M0.0S", text);
// want a way to say 'don't print the seconds in format()' or other fields for that matter: // want a way to say 'don't print the seconds in format()' or other fields for that matter:
//assertEquals("P1Y2M3DT10H30M", text); //assertEquals("P1Y2M3DT10H30M", text);
} }
@ -194,6 +191,7 @@ public void testFormat() {
assertEquals( "60000", DurationFormatUtils.format(time, "S") ); assertEquals( "60000", DurationFormatUtils.format(time, "S") );
assertEquals( "01:00", DurationFormatUtils.format(time, "mm:ss") ); assertEquals( "01:00", DurationFormatUtils.format(time, "mm:ss") );
/*
time = 3 * DateUtils.MILLIS_PER_YEAR + 7 * DateUtils.MILLIS_PER_MONTH; time = 3 * DateUtils.MILLIS_PER_YEAR + 7 * DateUtils.MILLIS_PER_MONTH;
assertEquals( "37", DurationFormatUtils.format(time, "yM") ); assertEquals( "37", DurationFormatUtils.format(time, "yM") );
assertEquals( "3 years 7 months", DurationFormatUtils.format(time, "y' years 'M' months'") ); assertEquals( "3 years 7 months", DurationFormatUtils.format(time, "y' years 'M' months'") );
@ -211,6 +209,7 @@ public void testFormat() {
assertEquals( "48", DurationFormatUtils.format(time, "M") ); assertEquals( "48", DurationFormatUtils.format(time, "M") );
assertEquals( "48", DurationFormatUtils.format(time, "MM") ); assertEquals( "48", DurationFormatUtils.format(time, "MM") );
assertEquals( "048", DurationFormatUtils.format(time, "MMM") ); assertEquals( "048", DurationFormatUtils.format(time, "MMM") );
*/
} }