Frames | No Frames |
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: HSSFDateUtil.java,v 1.2 2007/05/20 21:45:54 mimil Exp $ 28: * ------------ 29: * (C) Copyright 2006-2007, by Pentaho Corporation. 30: */ 31: 32: /* ==================================================================== 33: Copyright 2002-2004 Apache Software Foundation 34: 35: Licensed under the Apache License, Version 2.0 (the "License"); 36: you may not use this file except in compliance with the License. 37: You may obtain a copy of the License at 38: 39: http://www.apache.org/licenses/LICENSE-2.0 40: 41: Unless required by applicable law or agreed to in writing, software 42: distributed under the License is distributed on an "AS IS" BASIS, 43: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 44: See the License for the specific language governing permissions and 45: limitations under the License. 46: ==================================================================== */ 47: 48: 49: 50: /* 51: * DateUtil.java 52: * 53: * Created on January 19, 2002, 9:30 AM 54: */ 55: package org.jfree.formula.util; 56: 57: import java.util.Calendar; 58: import java.util.Date; 59: import java.util.GregorianCalendar; 60: 61: import org.jfree.formula.LibFormulaBoot; 62: 63: /** 64: * Contains methods for dealing with Excel dates. 65: * <br/> 66: * Modified by Cedric Pronzato 67: * 68: * @author Michael Harhen 69: * @author Glen Stampoultzis (glens at apache.org) 70: * @author Dan Sherman (dsherman at isisph.com) 71: * @author Hack Kampbjorn (hak at 2mba.dk) 72: */ 73: 74: public class HSSFDateUtil 75: { 76: private HSSFDateUtil() 77: { 78: } 79: 80: private static final int BAD_DATE = 81: -1; // used to specify that date is invalid 82: private static final long DAY_MILLISECONDS = 24 * 60 * 60 * 1000; 83: private static final double CAL_1900_ABSOLUTE = 84: ( double ) absoluteDay(new GregorianCalendar(1900, Calendar 85: .JANUARY, 1)) - 2.0; 86: 87: /** 88: * Given a Date, converts it into a double representing its internal Excel representation, 89: * which is the number of days since 1/1/1900. Fractional days represent hours, minutes, and seconds. 90: * 91: * @return Excel representation of Date (-1 if error - test for error by checking for less than 0.1) 92: * @param date the Date 93: */ 94: 95: public static double getExcelDate(Date date) 96: { 97: Calendar calStart = new GregorianCalendar(); 98: 99: calStart.setTime( 100: date); // If date includes hours, minutes, and seconds, set them to 0 101: // if (calStart.get(Calendar.YEAR) < 1900) 102: // { 103: // return BAD_DATE; 104: // } 105: // else 106: // { 107: // Because of daylight time saving we cannot use 108: // date.getTime() - calStart.getTimeInMillis() 109: // as the difference in milliseconds between 00:00 and 04:00 110: // can be 3, 4 or 5 hours but Excel expects it to always 111: // be 4 hours. 112: // E.g. 2004-03-28 04:00 CEST - 2004-03-28 00:00 CET is 3 hours 113: // and 2004-10-31 04:00 CET - 2004-10-31 00:00 CEST is 5 hours 114: double fraction = (((calStart.get(Calendar.HOUR_OF_DAY) * 60 115: + calStart.get(Calendar.MINUTE) 116: ) * 60 + calStart.get(Calendar.SECOND) 117: ) * 1000 + calStart.get(Calendar.MILLISECOND) 118: ) / ( double ) DAY_MILLISECONDS; 119: calStart = dayStart(calStart); 120: 121: return fraction + ( double ) absoluteDay(calStart) 122: - CAL_1900_ABSOLUTE; 123: } 124: // } 125: 126: /** 127: * Given a excel date, converts it into a Date. 128: * Assumes 1900 date windowing. 129: * 130: * @param date the Excel Date 131: * 132: * @return Java representation of a date (null if error) 133: * @see #getJavaDate(double,boolean) 134: */ 135: 136: public static Date getJavaDate(double date) 137: { 138: final String dateSystem = LibFormulaBoot.getInstance().getGlobalConfig() 139: .getConfigProperty("org.jfree.formula.datesystem.1904", "false"); 140: return getJavaDate(date, "true".equals(dateSystem)); 141: } 142: 143: /** 144: * Given an Excel date with either 1900 or 1904 date windowing, 145: * converts it to a java.util.Date. 146: * 147: * NOTE: If the default <code>TimeZone</code> in Java uses Daylight 148: * Saving Time then the conversion back to an Excel date may not give 149: * the same value, that is the comparison 150: * <CODE>excelDate == getExcelDate(getJavaDate(excelDate,false))</CODE> 151: * is not always true. For example if default timezone is 152: * <code>Europe/Copenhagen</code>, on 2004-03-28 the minute after 153: * 01:59 CET is 03:00 CEST, if the excel date represents a time between 154: * 02:00 and 03:00 then it is converted to past 03:00 summer time 155: * 156: * @param date The Excel date. 157: * @param use1904windowing true if date uses 1904 windowing, 158: * or false if using 1900 date windowing. 159: * @return Java representation of the date, or null if date is not a valid Excel date 160: * @see java.util.TimeZone 161: */ 162: public static Date getJavaDate(double date, boolean use1904windowing) { 163: if (isValidExcelDate(date)) { 164: int startYear = 1900; 165: int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't 166: int wholeDays = (int)Math.floor(date); 167: if (use1904windowing) { 168: startYear = 1904; 169: dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day 170: } 171: else if (wholeDays < 61) { 172: // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists 173: // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation 174: dayAdjust = 0; 175: } 176: GregorianCalendar calendar = new GregorianCalendar(startYear,0, 177: wholeDays + dayAdjust); 178: int millisecondsInDay = (int)((date - Math.floor(date)) * 179: (double) DAY_MILLISECONDS + 0.5); 180: calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay); 181: return calendar.getTime(); 182: } 183: else { 184: return null; 185: } 186: } 187: 188: /** 189: * given a format ID this will check whether the format represents 190: * an internal date format or not. 191: */ 192: public static boolean isInternalDateFormat(int format) { 193: boolean retval =false; 194: 195: switch(format) { 196: // Internal Date Formats as described on page 427 in 197: // Microsoft Excel Dev's Kit... 198: case 0x0e: 199: case 0x0f: 200: case 0x10: 201: case 0x11: 202: case 0x12: 203: case 0x13: 204: case 0x14: 205: case 0x15: 206: case 0x16: 207: case 0x2d: 208: case 0x2e: 209: case 0x2f: 210: retval = true; 211: break; 212: 213: default: 214: retval = false; 215: break; 216: } 217: return retval; 218: } 219: 220: 221: 222: /** 223: * Given a double, checks if it is a valid Excel date. 224: * 225: * @return true if valid 226: * @param value the double value 227: */ 228: 229: public static boolean isValidExcelDate(double value) 230: { 231: return (value > -Double.MIN_VALUE); 232: } 233: 234: /** 235: * Given a Calendar, return the number of days since 1600/12/31. 236: * 237: * @return days number of days since 1600/12/31 238: * @param cal the Calendar 239: * @exception IllegalArgumentException if date is invalid 240: */ 241: 242: private static int absoluteDay(Calendar cal) 243: { 244: return cal.get(Calendar.DAY_OF_YEAR) 245: + daysInPriorYears(cal.get(Calendar.YEAR)); 246: } 247: 248: /** 249: * Return the number of days in prior years since 1601 250: * 251: * @return days number of days in years prior to yr. 252: * @param yr a year (1600 < yr < 4000) 253: * @exception IllegalArgumentException if year is outside of range. 254: */ 255: 256: private static int daysInPriorYears(int yr) 257: { 258: if (yr < 1601) 259: { 260: throw new IllegalArgumentException( 261: "'year' must be 1601 or greater"); 262: } 263: int y = yr - 1601; 264: int days = 365 * y // days in prior years 265: + y / 4 // plus julian leap days in prior years 266: - y / 100 // minus prior century years 267: + y / 400; // plus years divisible by 400 268: 269: return days; 270: } 271: 272: // set HH:MM:SS fields of cal to 00:00:00:000 273: private static Calendar dayStart(final Calendar cal) 274: { 275: cal.get(Calendar 276: .HOUR_OF_DAY); // force recalculation of internal fields 277: cal.set(Calendar.HOUR_OF_DAY, 0); 278: cal.set(Calendar.MINUTE, 0); 279: cal.set(Calendar.SECOND, 0); 280: cal.set(Calendar.MILLISECOND, 0); 281: cal.get(Calendar 282: .HOUR_OF_DAY); // force recalculation of internal fields 283: return cal; 284: } 285: 286: // --------------------------------------------------------------------------------------------------------- 287: }