1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   /*
57    * DateUtil.java
58    *
59    * Created on January 19, 2002, 9:30 AM
60    */
61   package org.apache.poi.hssf.usermodel;
62   
63   import java.util.Calendar;
64   import java.util.Date;
65   import java.util.GregorianCalendar;
66   
67   /**
68    * Contains methods for dealing with Excel dates.
69    *
70    * @author  Michael Harhen
71    * @author  Glen Stampoultzis (glens at apache.org)
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              calStart = dayStart(calStart);
108              double fraction = (date.getTime() - calStart.getTime().getTime())
109                                / ( double ) DAY_MILLISECONDS;
110  
111              return fraction + ( double ) absoluteDay(calStart)
112                     - CAL_1900_ABSOLUTE;
113          }
114      }
115  
116      /**
117       * Given a excel date, converts it into a Date.
118       *
119       * @param  date the Excel Date
120       *
121       * @return Java representation of a date (null if error)
122       */
123  
124      public static Date getJavaDate(double date)
125      {
126          if (isValidExcelDate(date))
127          {
128              int               wholeDaysSince1900 = ( int ) Math.floor(date);
129              GregorianCalendar calendar           = new GregorianCalendar(1900,
130                                                         0, wholeDaysSince1900
131                                                         - 1);
132              int               millisecondsInDay  =
133                  ( int ) ((date - Math.floor(date))
134                           * ( double ) DAY_MILLISECONDS + 0.5);
135  
136              calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
137              return calendar.getTime();
138          }
139          else
140          {
141              return null;
142          }
143      }
144  
145      /**
146       * Given a double, checks if it is a valid Excel date.
147       *
148       * @return true if valid
149       * @param  value the double value
150       */
151  
152      public static boolean isValidExcelDate(double value)
153      {
154          return (value > -Double.MIN_VALUE);
155      }
156  
157      /**
158       * Given a Calendar, return the number of days since 1600/12/31.
159       *
160       * @return days number of days since 1600/12/31
161       * @param  cal the Calendar
162       * @exception IllegalArgumentException if date is invalid
163       */
164  
165      private static int absoluteDay(Calendar cal)
166      {
167          return cal.get(Calendar.DAY_OF_YEAR)
168                 + daysInPriorYears(cal.get(Calendar.YEAR));
169      }
170  
171      /**
172       * Return the number of days in prior years since 1601
173       *
174       * @return    days  number of days in years prior to yr.
175       * @param     yr    a year (1600 < yr < 4000)
176       * @exception IllegalArgumentException if year is outside of range.
177       */
178  
179      private static int daysInPriorYears(int yr)
180      {
181          if (yr < 1601)
182          {
183              throw new IllegalArgumentException(
184                  "'year' must be 1601 or greater");
185          }
186          int y    = yr - 1601;
187          int days = 365 * y      // days in prior years
188                     + y / 4      // plus julian leap days in prior years
189                     - y / 100    // minus prior century years
190                     + y / 400;   // plus years divisible by 400
191  
192          return days;
193      }
194  
195      // set HH:MM:SS fields of cal to 00:00:00:000
196      private static Calendar dayStart(final Calendar cal)
197      {
198          cal.get(Calendar
199              .HOUR_OF_DAY);   // force recalculation of internal fields
200          cal.set(Calendar.HOUR_OF_DAY, 0);
201          cal.set(Calendar.MINUTE, 0);
202          cal.set(Calendar.SECOND, 0);
203          cal.set(Calendar.MILLISECOND, 0);
204          cal.get(Calendar
205              .HOUR_OF_DAY);   // force recalculation of internal fields
206          return cal;
207      }
208  
209      // ---------------------------------------------------------------------------------------------------------
210  }
211