/* * Copyright (C) 2000 - 2012 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ * $Id: $ */ package com.naryx.tagfusion.expression.function; import java.math.BigDecimal; import java.util.Calendar; import com.naryx.tagfusion.cfm.engine.cfArgStructData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfDateData; import com.naryx.tagfusion.cfm.engine.cfNumberData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; /** * Infomation about the getNumericDate() function can be found in the following * O'Reilly Press book: * * Programming ColdFusion MX, 2nd Edition Creating Dynamic Web Applications By * Rob Brooks-Bilson 2nd Edition August 2003 ISBN: 0-596-00380-3 * * It says that 12 AM on 12-30-1899 represents the starting point (epoch) and so * GetNumericDate('12/30/1899 00:00:00') returns just zero. Otherwise, the * function returns a real number whose integer part represents the number of * days since 12 AM on 12-30-1899 and whose fractional part represents the time * value expressed in hours then divided by 24. * * Here is how the time is converted to the fractional part of the real number * that's returned using 19:28:10 as an example: * * 19 + 28/60 + 10/3600 = 194694444445 * * 194694444445 / 24 = 0.81122685185 */ public class getNumericDate extends functionBase { private static final long serialVersionUID = 1L; private static cfDateData epoch = null; /** * Each app server (cfmx 6.1, bdjava, bdnet) was returning the right values, * but with varying degrees of precision and total number of digits. For * example here is some sample output that was observed by Matt M. during * development of this function: cfmx 37621.8112269 bdjava 37621.81122685185 * bdnet 37621.811226851853 * * So we do rounding of the fractional part accounting for the number of * digits in the whole number part. This is done at the end of execute() */ private static int totalNumDigits = 12; // cfmx 6.1 returns a real number that // uses this many digits (this counts // all the digits, no matter which // side of the decimal they are on) public getNumericDate() { min = max = 1; setNamedParams( new String[]{ "date" } ); } @Override public String[] getParamInfo(){ return new String[]{ "date" }; } @Override public java.util.Map getInfo(){ return makeInfo( "date", "Returns a real number whose integer part represents the number of days since the EPOCH (12 AM on 12-30-1899) and whose fractional part represents the time value expressed in hours then divided by 24", ReturnType.NUMERIC ); } private synchronized void setupEpoch(cfSession _session) throws cfmRunTimeException { if (epoch == null) { int year = 1899; int month = 12 - 1; // months are 0-11 int day = 30; int hour = 0; int min = 0; int sec = 0; Calendar c = Calendar.getInstance(); c.setLenient(false); c.clear(); c.set(year, month, day, hour, min, sec); try { epoch = new cfDateData(c.getTime().getTime()); } catch (Exception e) { throwException(_session, "Unable to create epoch value. " + e); } } } @Override public cfData execute(cfSession _session, cfArgStructData argStruct ) throws cfmRunTimeException { if (epoch == null) setupEpoch(_session); cfData dateObj = getNamedParam(argStruct, "date", null ); if ( dateObj == null ) throwException(_session, "Missing 'date' parameter"); cfDateData dateTime; if ( dateObj.getDataType() == cfData.CFDATEDATA ) dateTime = (cfDateData)dateObj; else dateTime = (cfDateData)dateObj.coerce(cfData.CFDATEDATA); // use the dateDiff() function cfNumberData dayDiff = dateDiff.getDiff(epoch, dateTime, "d"); long numDays = dayDiff.getLong(); int numDaysDigits = String.valueOf(Math.abs(numDays)).length(); // now let's convert the hours, minutes and seconds into hours Calendar cal = dateTime.getCalendar(); int hour = cal.get( Calendar.HOUR ); int minute = cal.get( Calendar.MINUTE ); int second = cal.get( Calendar.SECOND ); double timeInHours = hour + (((double) minute) / 60) + (((double) second) / 3600); double time = timeInHours / 24; int scale = totalNumDigits - numDaysDigits; BigDecimal roundedTime = new BigDecimal(time); roundedTime = roundedTime.setScale(scale, BigDecimal.ROUND_HALF_UP); boolean isNegative = numDays < 0; double decimalDiff = roundedTime.doubleValue(); if (isNegative && decimalDiff > 0) decimalDiff = 1.0 - decimalDiff; double datePlusTime = Math.abs(numDays) + decimalDiff; if (isNegative) datePlusTime = -1.0 * datePlusTime; return new cfNumberData(datePlusTime); } }