You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2007/02/16 02:27:20 UTC

svn commit: r508269 - in /velocity/tools/trunk/src/java/org/apache/velocity/tools/generic: ComparisonDateTool.java times.properties

Author: nbubna
Date: Thu Feb 15 17:27:20 2007
New Revision: 508269

URL: http://svn.apache.org/viewvc?view=rev&rev=508269
Log:
add the long overdue third rewrite of what was once Chris Townsen's 'ExtendedDateTool', now named 'ComparisonDateTool'

Added:
    velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java   (with props)
    velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/times.properties   (with props)

Added: velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java?view=auto&rev=508269
==============================================================================
--- velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java (added)
+++ velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java Thu Feb 15 17:27:20 2007
@@ -0,0 +1,697 @@
+package org.apache.velocity.tools.generic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Tool for comparing {@link Date} and {@link Calendar} values
+ * in Velocity templates.  This is a subclass of {@link DateTool}
+ * and thus provides all the functionality of that tool and
+ * augments it with the ability to find the relationship between
+ * any date and the current date, or between any two dates.
+ * This comparison can result in either a textual representation
+ * of the relationship (e.g. "3 weeks, 2 days ago", "tomorrow", or
+ * "3 hrs away") or the value of a specific time unit may be requested.
+ * When using the textual representations, you can configure the
+ * tool to use alternate resource bundles and to skip over units
+ * you do not want to be included.
+ * <p><pre>
+ * Example of formatting the "current" date:
+ *  $date.whenIs('2005-07-04')                -> 1 year ago
+ *  $date.whenIs('2007-02-15').full           -> 1 year 32 weeks 2 days 17 hours 38 minutes 44 seconds 178 milliseconds ago
+ *  $date.whenIs('2007-02-15').days           -> -730
+ *  $date.whenIs($date.calendar)              -> now
+ *  $date.whenIs('2005-07-04', '2005-07-04')  -> same time
+ *  $date.difference('2005-07-04','2005-07-04')      -> 0 milliseconds
+ *  $date.difference('2005-07-04','2007-02-15').abbr -> 1 yr
+ *
+ * Example toolbox.xml config (if you want to use this with VelocityView):
+ * &lt;tool&gt;
+ *   &lt;key&gt;date&lt;/key&gt;
+ *   &lt;scope&gt;application&lt;/scope&gt;
+ *   &lt;class&gt;org.apache.velocity.tools.generic.ComparisonDateTool&lt;/class&gt;
+ *   &lt;parameter name="format" value="yyyy-MM-dd"/&gt;
+ *   &lt;parameter name="bundle" value="org.apache.velocity.tools.generic.times"/&gt;
+ *   &lt;parameter name="depth" value="1"/&gt;
+ *   &lt;parameter name="skip" value="month,week,millisecond"/&gt;
+ * &lt;/tool&gt;
+ * </pre></p>
+ *
+ * @author Nathan Bubna
+ * @author Chris Townsen
+ * @since VelocityTools 1.4
+ * @version $Revision$ $Date: 2006-04-04 12:35:17 -0700 (Tue, 04 Apr 2006) $
+ */
+public class ComparisonDateTool extends DateTool
+{
+    /** The number of milliseconds in a second. */
+    public static final long MILLIS_PER_SECOND = 1000l;
+    
+    /** The number of millseconds in a minute. */
+    public static final long MILLIS_PER_MINUTE = 60l * MILLIS_PER_SECOND;
+    
+    /** The number of milliseconds in an hour. */
+    public static final long MILLIS_PER_HOUR = 60l * MILLIS_PER_MINUTE;
+    
+    /** The number of milliseconds in a day. */
+    public static final long MILLIS_PER_DAY = 24l * MILLIS_PER_HOUR;
+    
+    /** The number of milliseconds in a week. */
+    public static final long MILLIS_PER_WEEK = 7l * MILLIS_PER_DAY;
+    
+    /** An approximation of the number of milliseconds in a month. */
+    public static final long MILLIS_PER_MONTH = 30l * MILLIS_PER_DAY;
+    
+    /** An approximation of the number of milliseconds in a year. */
+    public static final long MILLIS_PER_YEAR = 365l * MILLIS_PER_DAY;
+
+    /** The key used for specifying a default locale via toolbox params. */
+    public static final String BUNDLE_NAME_KEY = "bundle";
+
+    /** The key used for specifying a different default depth via toolbox params. */
+    public static final String DEPTH_KEY = "depth";
+
+    /** The key used for specifying time units to be skipped over. */
+    public static final String SKIPPED_UNITS_KEY = "skip";
+
+    /** The default path of the relative format resource bundles. */
+    public static final String DEFAULT_BUNDLE_NAME = 
+        "org.apache.velocity.tools.generic.times";
+
+
+    // time unit message keys
+    protected static final String MILLISECOND_KEY = "millisecond";
+    protected static final String SECOND_KEY = "second";
+    protected static final String MINUTE_KEY = "minute";
+    protected static final String HOUR_KEY = "hour";
+    protected static final String DAY_KEY = "day";
+    protected static final String WEEK_KEY = "week";
+    protected static final String MONTH_KEY = "month";
+    protected static final String YEAR_KEY = "year";
+
+    /** Array of all time unit message keys to their millisecond conversion factor. */
+    protected static final Map TIME_UNITS;
+    static
+    {
+        Map units = new LinkedHashMap(8);
+        units.put(MILLISECOND_KEY, Long.valueOf(1));
+        units.put(SECOND_KEY, Long.valueOf(MILLIS_PER_SECOND));
+        units.put(MINUTE_KEY, Long.valueOf(MILLIS_PER_MINUTE));
+        units.put(HOUR_KEY, Long.valueOf(MILLIS_PER_HOUR));
+        units.put(DAY_KEY, Long.valueOf(MILLIS_PER_DAY));
+        units.put(WEEK_KEY, Long.valueOf(MILLIS_PER_WEEK));
+        units.put(MONTH_KEY, Long.valueOf(MILLIS_PER_MONTH));
+        units.put(YEAR_KEY, Long.valueOf(MILLIS_PER_YEAR));
+        TIME_UNITS = Collections.unmodifiableMap(units);
+    }
+
+    // special message keys/prefixes/suffixes
+    protected static final String CURRENT_PREFIX = "current.";
+    protected static final String AFTER_KEY = "after";
+    protected static final String BEFORE_KEY = "before";
+    protected static final String EQUAL_KEY = "equal";
+    protected static final String ZERO_KEY = "zero";
+    protected static final String ABBR_SUFFIX = ".abbr";
+    protected static final String ONE_DAY_SUFFIX = ".day";
+    protected static final String PLURAL_SUFFIX = "s";
+
+    // display types
+    protected static final int CURRENT_TYPE = 0;
+    protected static final int RELATIVE_TYPE = 1;
+    protected static final int DIFF_TYPE = 2;
+
+    private String bundleName = DEFAULT_BUNDLE_NAME;
+    private ResourceBundle defaultBundle;
+    private Map timeUnits = TIME_UNITS;
+    private int depth = 1;
+
+
+    /**
+     * Calls the superclass implementation, then looks for a bundle name
+     * and any time units to be skipped.
+     */
+    protected void configure(ValueParser values)
+    {
+        // do DateTool config
+        super.configure(values);
+
+        // look for an alternate bundle
+        String bundle = values.getString(BUNDLE_NAME_KEY);
+        if (bundle != null)
+        {
+            this.bundleName = bundle;
+        }
+
+        this.depth = values.getInt(DEPTH_KEY, 1);
+
+        // look for time units to be ignored
+        String[] skip = values.getStrings(SKIPPED_UNITS_KEY);
+        if (skip != null)
+        {
+            timeUnits = new LinkedHashMap(TIME_UNITS);
+            for (int i=0; i < skip.length; i++)
+            {
+                timeUnits.remove(skip[i]);
+            }
+        }
+    }
+
+    /**
+     * Retrieves the specified text resource.
+     */
+    protected String getText(String key, Locale locale)
+    {
+        Locale defaultLocale = getLocale();
+        ResourceBundle bundle = null;
+        // if there is no locale or the specified locale equals the tool's default
+        if (locale == null || locale.equals(defaultLocale))
+        {
+            if (defaultBundle == null)
+            {
+                // load the bundle for the default locale
+                try
+                {
+                    // and cache it
+                    defaultBundle = ResourceBundle.getBundle(this.bundleName,
+                                                             defaultLocale);
+                }
+                catch (MissingResourceException e) {}
+            }
+
+            // use the default locale's bundle
+            bundle = defaultBundle;
+        }
+        else
+        {
+            // load the bundle for the specified locale
+            try
+            {
+                bundle = ResourceBundle.getBundle(this.bundleName, locale);
+            }
+            catch (MissingResourceException e) {}
+        }
+
+        // if we found a bundle...
+        if (bundle != null)
+        {
+            try
+            {
+                // try to return the specified key
+                return bundle.getString(key);
+            }
+            catch (MissingResourceException e) {}
+        }
+
+        // otherwise, return the key as an error
+        return "???" + key + "???";
+    }
+
+
+    // ------------------------- static millisecond conversions ----------------
+
+    /**
+     * Returns the number of whole Years in the specified number of milliseconds.
+     */
+    public static long toYears(long ms)
+    {
+        return ms / MILLIS_PER_YEAR;
+    }
+
+    /**
+     * Returns the number of whole Months in the specified number of milliseconds.
+     */
+    public static long toMonths(long ms)
+    {
+        return ms / MILLIS_PER_MONTH;
+    }
+
+    /**
+     * Returns the number of whole Weeks in the specified number of milliseconds.
+     */
+    public static long toWeeks(long ms)
+    {
+        return ms / MILLIS_PER_WEEK;
+    }
+
+    /**
+     * Returns the number of whole Days in the specified number of milliseconds.
+     */
+    public static long toDays(long ms)
+    {
+        return ms / MILLIS_PER_DAY;
+    }
+
+    /**
+     * Returns the number of whole Hours in the specified number of milliseconds.
+     */
+    public static long toHours(long ms)
+    {
+        return ms / MILLIS_PER_HOUR;
+    }
+
+    /**
+     * Returns the number of whole Minutes in the specified number of milliseconds.
+     */
+    public static long toMinutes(long ms)
+    {
+        return ms / MILLIS_PER_MINUTE;
+    }
+
+    /**
+     * Returns the number of whole Seconds in the specified number of milliseconds.
+     */
+    public static long toSeconds(long ms)
+    {
+        return ms / MILLIS_PER_SECOND;
+    }
+
+
+    // ------------------------- date comparison ---------------------------
+
+    /**
+     * Returns a {@link Comparison} between the result of
+     * {@link #getCalendar()} and the specified date.  The default
+     * rendering of that Comparison will be the largest unit difference
+     * between the dates followed by a description of their relative position.
+     * 
+     * @param date The date in question
+     */
+    public Comparison whenIs(Object then)
+    {
+        return compare(getCalendar(), then, CURRENT_TYPE);
+    }
+
+    /**
+     * Returns a {@link Comparison} between the second specified date
+     * and the first specified date.  The default
+     * rendering of that Comparison will be the largest unit difference
+     * between the dates followed by a description of their relative position.
+     * 
+     * @param now The date to use as representative of "now"
+     * @param then The date in question
+     */
+    public Comparison whenIs(Object now, Object then)
+    {
+        return compare(now, then, RELATIVE_TYPE);
+    }
+
+    /**
+     * Returns a {@link Comparison} between the result of
+     * the second specified date and the first specified date.  The default
+     * rendering of that Comparison will be the largest unit difference
+     * between the dates.
+     * 
+     * @param now The date to use as representative of "now"
+     * @param then The secondary date
+     */
+    public Comparison difference(Object now, Object then)
+    {
+        return compare(now, then, DIFF_TYPE);
+    }
+
+    protected Comparison compare(Object now, Object then, int type)
+    {
+        Calendar calThen = toCalendar(then);
+        Calendar calNow = toCalendar(now);
+        if (calThen == null || calNow == null)
+        {
+            return null;
+        }
+
+        long ms = calThen.getTimeInMillis() - calNow.getTimeInMillis();
+        return new Comparison(ms, type, this.depth, false, null);
+    }
+        
+
+    /**
+     * @param ms The time in milliseconds
+     * @param type Whether the time should be represented as relative to "now",
+     *             relative to some other time, or as a mere difference.
+     * @param depth The maximum number of units deep to show
+     * @param abbr Whether the units should be abbreviated or not
+     * @param loc The locale to be used when looking up resources
+     */
+    protected String toString(long ms, int type, int depth,
+                              boolean abbr, Locale loc)
+    {
+        // first check if there is a difference
+        if (ms == 0)
+        {
+            String sameKey = (abbr) ? ABBR_SUFFIX : "";
+            if (type == CURRENT_TYPE)
+            {
+                sameKey = CURRENT_PREFIX + EQUAL_KEY + sameKey;
+            }
+            else if (type == RELATIVE_TYPE)
+            {
+                sameKey = EQUAL_KEY + sameKey;
+            }
+            else
+            {
+                sameKey = ZERO_KEY + sameKey;
+            }
+            return getText(sameKey, loc);
+        }
+
+        boolean isBefore = false;
+        if (ms < 0)
+        {
+            isBefore = true;
+            // convert() only works with positive values
+            ms *= -1;
+        }
+
+        // get the base value
+        String friendly = toString(ms, depth, abbr, loc);
+
+        // if we only want the difference...
+        if (type == DIFF_TYPE)
+        {
+            // add the sign (if negative)
+            if (isBefore)
+            {
+                friendly = "-" + friendly;
+            }
+            // then return without direction suffix
+            return friendly;
+        }
+            
+        // otherwise, get the appropriate direction key
+        String directionKey = (isBefore) ? BEFORE_KEY : AFTER_KEY;
+        if (type == CURRENT_TYPE)
+        {
+            directionKey = CURRENT_PREFIX + directionKey;
+            
+            if (friendly != null && friendly.startsWith("1"))
+            {
+                // check for the corner case of "1 day ago" or "1 day away"
+                // and convert those to "yesterday" or "tomorrow"
+                String dayKey = (abbr) ? DAY_KEY + ABBR_SUFFIX : DAY_KEY;
+                if (friendly.equals("1 " + getText(dayKey, loc)))
+                {
+                    // add .day
+                    directionKey += ONE_DAY_SUFFIX;
+                    // and return only the value of this key
+                    // (which means we throw away the friendly value
+                    //  and don't bother abbreviating things)
+                    return getText(directionKey, loc);
+                }
+            }
+        }
+
+        // in the default bundle, this doesn't change anything.
+        // but in may in user-provided bundles
+        if (abbr)
+        {
+            directionKey += ABBR_SUFFIX;
+        }
+
+        // then combine them
+        return friendly +  " " + getText(directionKey, loc);
+    }
+
+
+    /**
+     * Converts the specified positive duration of milliseconds into larger
+     * units up to the specified number of positive units, beginning with the 
+     * largest positive unit.  e.g.
+     * <code>toString(181453, 3, false, null)</code> will return
+     * "3 minutes 1 second 453 milliseconds",
+     * <code>toString(181453, 2, false, null)</code> will return
+     * "3 minutes 1 second", and 
+     * <code>toString(180000, 2, true, null)</code> will return
+     * "3 min".
+     */
+    protected String toString(long diff, int maxUnitDepth,
+                              boolean abbreviate, Locale locale)
+    {
+        // these cases should be handled elsewhere
+        if (diff <= 0)
+        {
+            return null;
+        }
+        // can't go any deeper than we have units
+        if (maxUnitDepth > timeUnits.size())
+        {
+            maxUnitDepth = timeUnits.size();
+        }
+
+        long value = 0;
+        long remainder = 0;
+
+        // determine the largest unit and calculate the value and remainder
+        Iterator i = timeUnits.keySet().iterator();
+        String unitKey = (String)i.next();
+        Long unit = (Long)timeUnits.get(unitKey);
+        while (i.hasNext())
+        {
+            // get the next unit
+            String nextUnitKey = (String)i.next();
+            Long nextUnit = (Long)timeUnits.get(nextUnitKey);
+
+            // e.g. if diff < <nextUnit>
+            if (diff < nextUnit.longValue())
+            {
+                // then we're working with <unit>
+                value = diff / unit.longValue();
+                remainder = diff - (value * unit.longValue());
+                break;
+            }
+
+            // shift to the next unit
+            unitKey = nextUnitKey;
+            unit = nextUnit;
+        }
+
+        // if it was years, then we haven't done the math yet
+        if (unitKey.equals(YEAR_KEY))
+        {
+            value = diff / unit.longValue();
+            remainder = diff - (value * unit.longValue());
+        }
+
+        // select proper pluralization
+        if (value != 1)
+        {
+            unitKey += PLURAL_SUFFIX;
+        }
+
+        if (abbreviate)
+        {
+            unitKey += ABBR_SUFFIX;
+        }
+
+        // combine the value and the unit
+        String output = value + " " + getText(unitKey, locale);
+
+        // recurse over the remainder if it exists and more units are allowed
+        if (maxUnitDepth > 1 && remainder > 0)
+        {
+            output += " " + toString(remainder, maxUnitDepth - 1,
+                                     abbreviate, locale);
+        }
+        return output;
+    }
+
+
+
+    public class Comparison
+    {
+        private long milliseconds = 0;
+        private int type = CURRENT_TYPE;
+        private int maxUnitDepth = 1;
+        private boolean abbreviate = false;
+        private Locale locale;
+
+        public Comparison(long ms, int type, int depth, boolean abbr, Locale loc)
+        {
+            this.milliseconds = ms;
+            this.type = type;
+            this.maxUnitDepth = depth;
+            this.abbreviate = abbr;
+            this.locale = loc;
+        }
+
+        /**
+         * Sets whether or not this comparison is to be rendered in
+         * abbreviated form or not. By default, it is not abbreviated.
+         */
+        public Comparison abbr(boolean abbr)
+        {
+            return new Comparison(this.milliseconds, this.type,
+                                  this.maxUnitDepth, abbr, this.locale);
+        }
+
+        /**
+         * Set the maximum number of units to render for this comparison.
+         * By default, this is set to 1 unit.
+         */
+        public Comparison depth(int depth)
+        {
+            return new Comparison(this.milliseconds, this.type,
+                                  depth, this.abbreviate, this.locale);
+        }
+
+        /**
+         * Sets the locale used to look up the textual portions of the
+         * rendering. This defaults to the Locale configured for this tool,
+         * if any.  If no locale was configured, this defaults to the system
+         * default.
+         */
+        public Comparison locale(Locale loc)
+        {
+            return new Comparison(this.milliseconds, this.type,
+                                  this.maxUnitDepth, this.abbreviate, loc);
+        }
+
+        /**
+         * Return the approximate number of years between the dates being compared.
+         */
+        public long getYears()
+        {
+            return ComparisonDateTool.toYears(this.milliseconds);
+        }
+
+        /**
+         * Return the approximate number of months between the dates being compared.
+         */
+        public long getMonths()
+        {
+            return ComparisonDateTool.toMonths(this.milliseconds);
+        }
+
+        /**
+         * Return the number of weeks between the dates being compared.
+         */
+        public long getWeeks()
+        {
+            return ComparisonDateTool.toWeeks(this.milliseconds);
+        }
+
+        /**
+         * Return the number of days between the dates being compared.
+         */
+        public long getDays()
+        {
+            return ComparisonDateTool.toDays(this.milliseconds);
+        }
+
+        /**
+         * Return the number of hours between the dates being compared.
+         */
+        public long getHours()
+        {
+            return ComparisonDateTool.toHours(this.milliseconds);
+        }
+
+        /**
+         * Return the number of minutes between the dates being compared.
+         */
+        public long getMinutes()
+        {
+            return ComparisonDateTool.toMinutes(this.milliseconds);
+        }
+
+        /**
+         * Return the number of seconds between the dates being compared.
+         */
+        public long getSeconds()
+        {
+            return ComparisonDateTool.toSeconds(this.milliseconds);
+        }
+
+        /**
+         * Return the number of milliseconds between the dates being compared.
+         */
+        public long getMilliseconds()
+        {
+            return this.milliseconds;
+        }
+
+        /**
+         * Sets the {@link #depth(int depth)} to which this comparison is rendered
+         * to the maximum number of time units available to the tool. By default,
+         * there are 8 units available, but the tool may be configured to "skip"
+         * any of the standard units, thus shortening the maximum depth.
+         */
+        public Comparison getFull()
+        {
+            return depth(ComparisonDateTool.this.timeUnits.size());
+        }
+
+        /**
+         * Sets this comparison to be rendered as a 
+         * {@link ComparisonDateTool#difference}. This effectively means that
+         * the comparison will render as a period of time, without any suffix
+         * to describe the relative position of the dates being compared (e.g. "later"
+         * or "ago").
+         */
+        public Comparison getDifference()
+        {
+            return new Comparison(this.milliseconds, DIFF_TYPE,
+                                  this.maxUnitDepth, this.abbreviate, this.locale);
+        }
+
+        /**
+         * Sets this comparison to be rendered as if it where generated using
+         * the {@link ComparisonDateTool#whenIs(Object now, Object then)} method.
+         * This effectively means that the comparison will render with a suffix
+         * to describe the relative position of the dates being compared (e.g. "later"
+         * or "ago").
+         */
+        public Comparison getRelative()
+        {
+            return new Comparison(this.milliseconds, RELATIVE_TYPE,
+                                  this.maxUnitDepth, this.abbreviate, this.locale);
+        }
+
+        /**
+         * This is equivalent to calling {@link #abbr(boolean abbr)} with
+         * {@code true} as the argument, thus setting this comparison to be
+         * rendered in abbreviated form.
+         */
+        public Comparison getAbbr()
+        {
+            return abbr(true);
+        }
+
+        /**
+         * Renders this comparison to a String.
+         */
+        public String toString()
+        {
+            return ComparisonDateTool.this.toString(this.milliseconds,
+                                                    this.type,
+                                                    this.maxUnitDepth,
+                                                    this.abbreviate,
+                                                    this.locale);
+        }
+    }
+
+}

Propchange: velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/ComparisonDateTool.java
------------------------------------------------------------------------------
    svn:keywords = Revision

Added: velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/times.properties
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/times.properties?view=auto&rev=508269
==============================================================================
--- velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/times.properties (added)
+++ velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/times.properties Thu Feb 15 17:27:20 2007
@@ -0,0 +1,53 @@
+after=later
+before=earlier
+equal=same time
+zero=0 milliseconds
+current.after=away
+current.after.day=tomorrow
+current.before=ago
+current.before.day=yesterday
+current.equal=now
+
+millisecond=millisecond
+milliseconds=milliseconds
+second=second
+seconds=seconds
+minute=minute
+minutes=minutes
+hour=hour
+hours=hours
+day=day
+days=days
+week=week
+weeks=weeks
+month=month
+months=months
+year=year
+years=years
+
+
+# abbreviations
+after.abbr=later
+before.abbr=earlier
+equal.abbr=same
+zero.abbr=0 ms
+current.after.abbr=away
+current.before.abbr=ago
+current.equal.abbr=now
+
+millisecond.abbr=ms
+milliseconds.abbr=ms
+second.abbr=s
+seconds.abbr=s
+minute.abbr=min
+minutes.abbr=min
+hour.abbr=hr
+hours.abbr=hrs
+day.abbr=day
+days.abbr=days
+week.abbr=wk
+weeks.abbr=wks
+month.abbr=mo
+months.abbr=mos
+year.abbr=yr
+years.abbr=yrs

Propchange: velocity/tools/trunk/src/java/org/apache/velocity/tools/generic/times.properties
------------------------------------------------------------------------------
    svn:eol-style = native