Package org.pentaho.reporting.libraries.formula.function.datetime

Source Code of org.pentaho.reporting.libraries.formula.function.datetime.DateDifFunction

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2006 - 2013 Pentaho Corporation and Contributors.  All rights reserved.
*/

package org.pentaho.reporting.libraries.formula.function.datetime;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import org.pentaho.reporting.libraries.formula.EvaluationException;
import org.pentaho.reporting.libraries.formula.FormulaContext;
import org.pentaho.reporting.libraries.formula.LibFormulaErrorValue;
import org.pentaho.reporting.libraries.formula.LocalizationContext;
import org.pentaho.reporting.libraries.formula.function.Function;
import org.pentaho.reporting.libraries.formula.function.ParameterCallback;
import org.pentaho.reporting.libraries.formula.lvalues.TypeValuePair;
import org.pentaho.reporting.libraries.formula.typing.TypeRegistry;
import org.pentaho.reporting.libraries.formula.typing.coretypes.NumberType;
import org.pentaho.reporting.libraries.formula.util.NumberUtil;

/**
* This function returns the number of years, months, or days between two date
* numbers.<br/>
* <p/>
* The Format is a code from the following table, entered as text, that
* specifies the format you want: <TABLE> <TR> <TH>format</TH> <TH>Returns the
* number of</TH> </TR> <TR> <TD>y</TD> <TD>Years</TD> </TR> <TR> <TD>m</TD>
* <TD>Months. If there is not a complete month between the dates, 0 will be
* returned.</TD> </TR> <TR> <TD>d</TD> <TD>Days</TD> </TR> <TR> <TD>md</TD>
* <TD>Days, ignoring months and years</TD> </TR> <TR> <TD>ym</TD> <TD>Months,
* ignoring years</TD> </TR> <TR> <TD>yd</TD> <TD>Days, ignoring years</TD>
* </TR> <TR> <TD></TD> <TD></TD> </TR> </TABLE>
*
* @author Cedric Pronzato
*/
public class DateDifFunction implements Function
{
  public static final String YEARS_CODE = "y";
  public static final String MONTHS_CODE = "m";
  public static final String DAYS_CODE = "d";
  public static final String DAYS_IGNORING_YEARS = "yd";
  public static final String MONTHS_IGNORING_YEARS = "ym";
  public static final String DAYS_IGNORING_MONTHS_YEARS = "md";
  private static final long serialVersionUID = 81013707499607068L;

  public DateDifFunction()
  {
  }

  public String getCanonicalName()
  {
    return "DATEDIF"; // NON-NLS
  }

  public TypeValuePair evaluate(final FormulaContext context,
                                final ParameterCallback parameters)
      throws EvaluationException
  {
    if (parameters.getParameterCount() != 3)
    {
      throw EvaluationException.getInstance(LibFormulaErrorValue.ERROR_ARGUMENTS_VALUE);
    }

    final TypeRegistry typeRegistry = context.getTypeRegistry();
    final String formatCode = typeRegistry.convertToText
        (parameters.getType(2), parameters.getValue(2));

    if (formatCode == null || "".equals(formatCode))
    {
      throw EvaluationException.getInstance(
          LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
    }

    long days = computeDays(parameters, typeRegistry);

    if (DateDifFunction.DAYS_CODE.equals(formatCode))
    {
      return new TypeValuePair(NumberType.GENERIC_NUMBER, new BigDecimal(days));
    }

    final Date date1 = typeRegistry.convertToDate(parameters.getType(0), parameters.getValue(0));
    final Date date2 = typeRegistry.convertToDate(parameters.getType(1), parameters.getValue(1));

    if (date1 == null || date2 == null)
    {
      throw EvaluationException.getInstance(LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
    }

    final LocalizationContext localizationContext = context.getLocalizationContext();
    final TimeZone timeZone = localizationContext.getTimeZone();
    final Locale locale = localizationContext.getLocale();
    final GregorianCalendar calandar1 = new GregorianCalendar(timeZone, locale);
    calandar1.setTime(min(date1, date2));

    final GregorianCalendar calandar2 = new GregorianCalendar(timeZone, locale);
    calandar2.setTime(max(date1, date2));

    int sign = (date1.getTime() < date2.getTime()) ? 1 : -1;
    final long res = sign * computeDateDifference(formatCode, calandar1, calandar2, days);

    return new TypeValuePair(NumberType.GENERIC_NUMBER, new BigDecimal(res));
  }

  protected long computeDays(final ParameterCallback parameters,
                             final TypeRegistry typeRegistry) throws EvaluationException
  {
    final Number date1 = typeRegistry.convertToNumber(parameters.getType(0), parameters.getValue(0));
    final Number date2 = typeRegistry.convertToNumber(parameters.getType(1), parameters.getValue(1));

    final BigDecimal dn1 = NumberUtil.performIntRounding(NumberUtil.getAsBigDecimal(date1));
    final BigDecimal dn2 = NumberUtil.performIntRounding(NumberUtil.getAsBigDecimal(date2));
    return dn2.longValue() - dn1.longValue();
  }

  protected long computeDateDifference(final String formatCode,
                                       final GregorianCalendar min,
                                       final GregorianCalendar max,
                                       final long days) throws EvaluationException
  {
    if (DateDifFunction.YEARS_CODE.equals(formatCode))
    {
      // done
      return computeYears(min, max);
    }
    else if (DateDifFunction.MONTHS_CODE.equals(formatCode))
    {
      // done
      return computeMonths(min, max);
    }
    else if (DateDifFunction.DAYS_IGNORING_MONTHS_YEARS.equals(formatCode))
    {
      return computeMonthDays(min, max);

    }
    else if (DateDifFunction.MONTHS_IGNORING_YEARS.equals(formatCode))
    {
      // done
      return computeYearMonth(min, max);
    }
    else if (DateDifFunction.DAYS_IGNORING_YEARS.equals(formatCode))
    {
      // done
      return computeYearDays(min, max, days);
    }
    else
    {
      throw EvaluationException.getInstance(LibFormulaErrorValue.ERROR_INVALID_ARGUMENT_VALUE);
    }
  }

  private long computeYearDays(final GregorianCalendar min, final GregorianCalendar max, final long dayDiff)
  {
    final int year1 = min.get(Calendar.YEAR);
    final int year2 = max.get(Calendar.YEAR);
    if (year1 == year2)
    {
      // simple case: We are within the same year
      return Math.abs(dayDiff);
    }

    final int dayMinDate = min.get(Calendar.DAY_OF_YEAR);
    final int dayMaxDate = max.get(Calendar.DAY_OF_YEAR);
    if (dayMinDate <= dayMaxDate)
    {
      return dayMaxDate - dayMinDate;
    }

    int daysInMinYear = min.getActualMaximum(Calendar.DAY_OF_YEAR);
    int daysToEndOfYear = daysInMinYear - dayMinDate;
    return dayMaxDate + daysToEndOfYear;
  }


  private long computeYearMonth(final GregorianCalendar min, final GregorianCalendar max)
  {
    return computeMonths(min, max) % 12;
  }

  private long computeMonthDays(final GregorianCalendar min, final GregorianCalendar max)
  {
    // The number of days between Date1 and Date2, as if Date1 and
    // Date2 were in the same month and the same year.
    int dayMin = min.get(Calendar.DAY_OF_MONTH);
    int dayMax = max.get(Calendar.DAY_OF_MONTH);
    if (dayMin <= dayMax) {
      return dayMax - dayMin;
    }

    int maxDaysInMonth = max.getActualMaximum(Calendar.DAY_OF_MONTH);
    return maxDaysInMonth + dayMax - dayMin;
  }

  private int addFieldLoop(final GregorianCalendar c, final GregorianCalendar target, final int field)
  {
    c.set(Calendar.MILLISECOND, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.HOUR_OF_DAY, 0);

    target.set(Calendar.MILLISECOND, 0);
    target.set(Calendar.SECOND, 0);
    target.set(Calendar.MINUTE, 0);
    target.set(Calendar.HOUR_OF_DAY, 0);

    if (c.getTimeInMillis() == target.getTimeInMillis())
    {
      return 0;
    }

    int count = 0;
    while (true)
    {
      c.add(field, 1);
      if (c.getTimeInMillis() > target.getTimeInMillis())
      {
        return count;
      }
      count += 1;
    }
  }

  private long computeMonths(final GregorianCalendar min, final GregorianCalendar max)
  {
    return addFieldLoop(min, max, Calendar.MONTH);
  }

  private long computeYears(final GregorianCalendar min, final GregorianCalendar max)
  {
    return addFieldLoop(min, max, Calendar.YEAR);
  }

  private Date min(final Date d1, final Date d2)
  {
    if (d1.getTime() < d2.getTime())
    {
      return d1;
    }
    return d2;
  }

  private Date max(final Date d1, final Date d2)
  {
    if (d1.getTime() >= d2.getTime())
    {
      return d1;
    }
    return d2;
  }
}
TOP

Related Classes of org.pentaho.reporting.libraries.formula.function.datetime.DateDifFunction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.