Source for org.jfree.formula.function.datetime.DateDifFunction

   1: /**
   2:  * =========================================
   3:  * LibFormula : a free Java formula library
   4:  * =========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/libformula/
   7:  *
   8:  * (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  *
  26:  * ------------
  27:  * $Id: DateDifFunction.java,v 1.4 2007/04/01 13:51:52 taqua Exp $
  28:  * ------------
  29:  * (C) Copyright 2006-2007, by Pentaho Corporation.
  30:  */
  31: package org.jfree.formula.function.datetime;
  32: 
  33: import java.util.Calendar;
  34: import java.util.Date;
  35: import java.util.GregorianCalendar;
  36: import java.util.Locale;
  37: import java.util.TimeZone;
  38: 
  39: import org.jfree.formula.EvaluationException;
  40: import org.jfree.formula.FormulaContext;
  41: import org.jfree.formula.LibFormulaErrorValue;
  42: import org.jfree.formula.LocalizationContext;
  43: import org.jfree.formula.function.Function;
  44: import org.jfree.formula.function.ParameterCallback;
  45: import org.jfree.formula.lvalues.TypeValuePair;
  46: import org.jfree.formula.typing.TypeRegistry;
  47: import org.jfree.formula.typing.coretypes.NumberType;
  48: 
  49: /**
  50:  * This function returns the number of years, months, or days between two date
  51:  * numbers.<br/>
  52:  * <p/>
  53:  * The Format is a code from the following table, entered as text, that
  54:  * specifies the format you want: <TABLE> <TR> <TH>format</TH> <TH>Returns the
  55:  * number of</TH> </TR> <TR> <TD>y</TD> <TD>Years</TD> </TR> <TR> <TD>m</TD>
  56:  * <TD>Months. If there is not a complete month between the dates, 0 will be
  57:  * returned.</TD> </TR> <TR> <TD>d</TD> <TD>Days</TD> </TR> <TR> <TD>md</TD>
  58:  * <TD>Days, ignoring months and years</TD> </TR> <TR> <TD>ym</TD> <TD>Months,
  59:  * ignoring years</TD> </TR> <TR> <TD>yd</TD> <TD>Days, ignoring years</TD>
  60:  * </TR> <TR> <TD></TD> <TD></TD> </TR> </TABLE>
  61:  *
  62:  * @author Cedric Pronzato
  63:  */
  64: public class DateDifFunction implements Function
  65: {
  66:   public static final String YEARS_CODE = "y";
  67:   public static final String MONTHS_CODE = "m";
  68:   public static final String DAYS_CODE = "d";
  69:   public static final String DAYS_IGNORING_YEARS = "yd";
  70:   public static final String MONTHS_IGNORING_YEARS = "ym";
  71:   public static final String DAYS_IGNORING_MONTHS_YEARS = "md";
  72: 
  73: //  private static final String[] CODES = {
  74: //    YEARS_CODE, MONTHS_CODE, DAYS_CODE, DAYS_IGNORING_YEARS,
  75: //    MONTHS_IGNORING_YEARS, DAYS_IGNORING_MONTHS_YEARS
  76: //  };
  77: //
  78: 
  79:   public DateDifFunction()
  80:   {
  81:   }
  82: 
  83:   public String getCanonicalName()
  84:   {
  85:     return "DATEDIF";
  86:   }
  87: 
  88:   public TypeValuePair evaluate(final FormulaContext context,
  89:                                 final ParameterCallback parameters)
  90:       throws EvaluationException
  91:   {
  92:     if (parameters.getParameterCount() != 3)
  93:     {
  94:       throw new EvaluationException(LibFormulaErrorValue.ERROR_ARGUMENTS_VALUE);
  95:     }
  96: 
  97:     final TypeRegistry typeRegistry = context.getTypeRegistry();
  98:     final Date date1 = typeRegistry.convertToDate
  99:         (parameters.getType(0), parameters.getValue(0));
 100:     final Date date2 = typeRegistry.convertToDate
 101:         (parameters.getType(1), parameters.getValue(1));
 102:     final String formatCode = typeRegistry.convertToText
 103:         (parameters.getType(2), parameters.getValue(2));
 104: 
 105:     if (date1 == null || date2 == null || formatCode == null || "".equals(
 106:         formatCode))
 107:     {
 108:       throw new EvaluationException(
 109:           LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
 110:     }
 111: 
 112:     final LocalizationContext localizationContext = context.getLocalizationContext();
 113:     final TimeZone timeZone = localizationContext.getTimeZone();
 114:     final Locale locale = localizationContext.getLocale();
 115:     final GregorianCalendar calandar1 =
 116:         new GregorianCalendar(timeZone, locale);
 117:     calandar1.setTime(date1);
 118: 
 119:     final GregorianCalendar calandar2 =
 120:         new GregorianCalendar(timeZone, locale);
 121:     calandar2.setTime(date2);
 122: 
 123:     int res;
 124: 
 125:     if (DateDifFunction.YEARS_CODE.equals(formatCode))
 126:     {
 127:       res = calandar2.get(Calendar.YEAR) - calandar1.get(Calendar.YEAR);
 128:     }
 129:     else if (DateDifFunction.MONTHS_CODE.equals(formatCode))
 130:     {
 131:       final int month1 = calandar1.get(Calendar.MONTH);
 132:       final int month2 = calandar2.get(Calendar.MONTH);
 133:       final int year1 = calandar1.get(Calendar.YEAR);
 134:       final int year2 = calandar2.get(Calendar.YEAR);
 135: 
 136:       res = (year2 - year1) * 12 +
 137:             month2 - month1;
 138:     }
 139:     else if (DateDifFunction.DAYS_IGNORING_MONTHS_YEARS.equals(formatCode))
 140:     {
 141:       // The number of days between Date1 and Date2, as if Date1 and
 142:       // Date2 were in the same month and the same year.
 143: 
 144:       // Not sure what happens to leap years, so this solution may be invalid.
 145:       calandar1.set(Calendar.YEAR, calandar2.get(Calendar.YEAR));
 146:       calandar1.set(Calendar.MONTH, calandar2.get(Calendar.MONTH));
 147: 
 148:       res = calandar2.get(Calendar.DAY_OF_MONTH) -
 149:             calandar1.get(Calendar.DAY_OF_MONTH);
 150:     }
 151:     else if (DateDifFunction.DAYS_CODE.equals(formatCode))
 152:     {
 153:       final int dayOfYear1 = calandar1.get(Calendar.DAY_OF_YEAR);
 154:       final int dayOfYear2 = calandar2.get(Calendar.DAY_OF_YEAR);
 155:       final int year1 = calandar1.get(Calendar.YEAR);
 156:       final int year2 = calandar2.get(Calendar.YEAR);
 157: 
 158:       final GregorianCalendar workingCalandar =
 159:           new GregorianCalendar(timeZone, locale);
 160: 
 161:       res = dayOfYear2 - dayOfYear1;
 162: 
 163:       // run through the inner years, without counting the border years
 164:       // Always run from the lower to the higher, so that we prevent infinite
 165:       // loops ..
 166:       final int targetYear = Math.max(year1, year2);
 167:       for (int i = Math.min(year1, year2); i < targetYear; i++)
 168:       {
 169:         workingCalandar.set(Calendar.YEAR, i);
 170:         res += workingCalandar.getActualMaximum(Calendar.DAY_OF_YEAR);
 171:       }
 172:     }
 173:     else if (DateDifFunction.MONTHS_IGNORING_YEARS.equals(formatCode))
 174:     {
 175:       final int month1 = calandar1.get(Calendar.MONTH);
 176:       final int month2 = calandar2.get(Calendar.MONTH);
 177: 
 178:       res = month2 - month1;
 179:     }
 180:     else if (DateDifFunction.DAYS_IGNORING_YEARS.equals(formatCode))
 181:     {
 182:       //Isn't that a stupid case? How could we count the days while ignoring
 183:       //how much days there are in each months without using the year?
 184: 
 185:       // The number of days between Date1 and Date2, as if Date1 and Date2
 186:       // were in the same year.
 187: 
 188:       // Not sure what happens to leap years, so this solution may be invalid.
 189:       calandar1.set(Calendar.YEAR, calandar2.get(Calendar.YEAR));
 190:       final int dayOne = calandar1.get(Calendar.DAY_OF_YEAR);
 191:       final int dayTwo = calandar2.get(Calendar.DAY_OF_YEAR);
 192:       res = Math.abs(dayOne - dayTwo);
 193:     }
 194:     else
 195:     {
 196:       throw new EvaluationException(
 197:           LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
 198:     }
 199: 
 200:     if (res < 0)
 201:     {
 202:       throw new EvaluationException(
 203:           LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
 204:     }
 205: 
 206:     return new TypeValuePair(NumberType.GENERIC_NUMBER, new Integer(res));
 207:   }
 208: }