| 
/** Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
 package com.opensymphony.util;
 
 
 /* ====================================================================
 * The OpenSymphony Software License, Version 1.1
 *
 * (this license is derived and fully compatible with the Apache Software
 * License - see http://www.apache.org/LICENSE.txt)
 *
 * Copyright (c) 2001 The OpenSymphony Group. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        OpenSymphony Group (http://www.opensymphony.com/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "OpenSymphony" and "The OpenSymphony Group"
 *    must not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [email protected] .
 *
 * 5. Products derived from this software may not be called "OpenSymphony"
 *    or "OSCore", nor may "OpenSymphony" or "OSCore" appear in their
 *    name, without prior written permission of the OpenSymphony Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 */
 import java.sql.Time;
 import java.sql.Timestamp;
 
 import java.text.DateFormatSymbols;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.TimeZone;
 
 
 /**
 * This class is used to convert Dates to ISO 8601 formatted dates and back.
 *
 * This class is an implementation of the ISO 8601 International Date Format standard.
 * The ISO 8601 helps to alleviate the following problem:
 *
 * <p>Ever been to a webpage to see that the time-sensitive information you are interested
 * in is dated 03/05/01?</p>
 * <p>Is this date the 3rd of May 2001 or the 5th of March 2001 or the 1st of May 2003 and
 * does the 01 refer to 2001 in the first two cases?</p>
 * <p>In order to make the right choice, you must ask yourself some questions :</p>
 * <ul>
 *                 <li>Where is the website you are visiting based?</li>
 *                 <li>Is the website in a country which supports the U.S.A. style date format
 *         (mm/dd/yy) or the European style (dd/mm/yy)?</li>
 *                 <li>Which countries do officially use the U.S.A. style date format? Is there an official list?</li>
 *                 <li>Is the page author :
 *                         <ul style="square">
 *                                <li>American?</li>
 *                                <li>European?</li>
 *                                <li>Japanese?</li>
 *                                <li>someone who thinks or wishes he/she was one of these nationalities?</li>
 *                                <li>one of these nationalities living in a foreign country?</li>
 *                                <li>someone using a particular format because they think it is a world standard?</li>
 *                                <li>confused, and has got the numeric fields in an order different to what they intended?</li>
 *                         </ul>
 *                 </li>
 * </ul>
 * <b>Which one is it?</b> It could be very important to you.
 *
 * <p>The Internet is a truly International method of communicating - there are no political or cultural
 * boundaries drawn on the www page you call up - the page could have been stored in the Smithsonian
 * Institute or on a small server in a basement in Ulan Bator, Mongolia. Often, you have no way of telling.
 * So, if anyone in the world can read your page, why not ensure that any date references on that page can
 * be read correctly and unambiguously by that person, by using the ISO 8601:1988 International Date Format?</p>
 *
 * <p>The basic format is: <b><i>"CCYYMMDDThhmmsssss±nnn"</i></b></p>
 *
 * <table border="1"><tr><th colspan="2">Characters used in place of digits or signs</th></tr>
 * <tr><td nowrap="nowrap"> [Y] </td><td> represents a digit used in the time element <b>year</b></td></tr>
 * <tr><td> [M] </td><td> represents a digit used in the time element <b>month</b></td></tr>
 * <tr><td> [D] </td><td> represents a digit used in the time element <b>day</b></td></tr>
 * <tr><td> [T] </td><td> place holder denoting time</td></tr>
 * <tr><td> [h] </td><td> represents a digit used in the time element <b>hour</b></td></tr>
 * <tr><td> [m] </td><td> represents a digit used in the time element <b>minute</b></td></tr>
 * <tr><td> [s] </td><td> represents a digit used in the time element <b>second</b></td></tr>
 * <tr><td> [n] </td><td> represents digit(s), constituting a positive integer or zero</td></tr>
 * <tr><td> [±] </td><td> represents a plus sign [+] if in combination with the following
 *  element a positive value or zero needs to be represented, or a minus sign [­] if in combination with the
 * following element a negative value needs to be represented.</td></tr>
 * </table>
 *
 * <p>The expanded format includes formating: <b><i>"CCYY-MM-DDThh:mm:ss,sss±nnn"</i></b></p>
 *
 * <p>Further there are some date math functions for ISO Dates.</p>
 *
 * <h2>Other Functions</h2>
 * <p> funtions for julian dates </p>
 *
 * @author <a href="mailto:[email protected]">SnowWolf Wagner</a>
 * @author <a href="mailto:[email protected]">Joseph B. Ottinger</a>
 * @version $Revision: 17 $
 */
 public class DateUtil {
 //~ Static fields/initializers /////////////////////////////////////////////
 
 /**
 * Base ISO 8601 Date format yyyyMMdd i.e., 20021225 for the 25th day of December in the year 2002
 */
 public static final String ISO_DATE_FORMAT = "yyyyMMdd";
 
 /**
 * Expanded ISO 8601 Date format yyyy-MM-dd i.e., 2002-12-25 for the 25th day of December in the year 2002
 */
 public static final String ISO_EXPANDED_DATE_FORMAT = "yyyy-MM-dd";
 
 /**
 * Basic ISO 8601 Time format HHmmssSSSzzz i.e., 143212333-500 for 2 pm 32 min 12 secs 333 mills -5 hours from GMT
 * 24 hour clock
 */
 public static final String ISO_TIME_FORMAT = "HHmmssSSSzzz";
 
 /**
 * Basic ISO 8601 Time format HH:mm:ss,SSSzzz i.e., 14:32:12,333-500 for 2 pm 32 min 12 secs 333 mills -5 hours from GMT
 * 24 hour clock
 */
 public static final String ISO_EXPANDED_TIME_FORMAT = "HH:mm:ss,SSSzzz";
 
 /**
 * Base ISO 8601 Date format yyyyMMddTHHmmssSSSzzz i.e., 20021225T143212333-500 for
 * the 25th day of December in the year 2002 at 2 pm 32 min 12 secs 333 mills -5 hours from GMT
 */
 public static final String ISO_DATE_TIME_FORMAT = "yyyyMMdd'T'HHmmssSSSzzz";
 
 /**
 * Base ISO 8601 Date format yyyy-MM-ddTHH:mm:ss,SSSzzz i.e., 2002-12-25T14:32:12,333-500 for
 * the 25th day of December in the year 2002 at 2 pm 32 min 12 secs 333 mills -5 hours from GMT
 */
 public static final String ISO_EXPANDED_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss,SSSzzz";
 public static final DateFormatSymbols dateFormatSymbles;
 private static final String[][] foo;
 private static final int JAN_1_1_JULIAN_DAY = 1721426; // January 1, year 1 (Gregorian)
 private static final int EPOCH_JULIAN_DAY = 2440588; // Jaunary 1, 1970 (Gregorian)
 private static final int EPOCH_YEAR = 1970;
 
 // Useful millisecond constants.  Although ONE_DAY and ONE_WEEK can fit
 // into ints, they must be longs in order to prevent arithmetic overflow
 // when performing (bug 4173516).
 private static final int ONE_SECOND = 1000;
 private static final int ONE_MINUTE = 60 * ONE_SECOND;
 private static final int ONE_HOUR = 60 * ONE_MINUTE;
 private static final long ONE_DAY = 24 * ONE_HOUR;
 private static final long ONE_WEEK = 7 * ONE_DAY;
 
 static {
 // override the timezone strings
 foo = new String[0][];
 dateFormatSymbles = new DateFormatSymbols();
 dateFormatSymbles.setZoneStrings(foo);
 }
 
 //~ Methods ////////////////////////////////////////////////////////////////
 
 /**
 *
 * @param isoString
 * @param expanded
 * @return True id leap year
 * @throws ParseException
 */
 public static final boolean isLeapYear(String isoString, boolean expanded) throws ParseException {
 GregorianCalendar cal = new GregorianCalendar();
 cal.setTime(isoToDate(isoString, expanded));
 
 return cal.isLeapYear(cal.get(Calendar.YEAR));
 }
 
 /**
 *
 * @param isoString
 * @return true if is leap year
 * @throws ParseException
 */
 public static final boolean isLeapYear(String isoString) throws ParseException {
 return isLeapYear(isoString, false);
 }
 
 public static final TimeZone getTimeZoneFromDateTime(String date, boolean expanded) throws ParseException {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_DATE_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_DATE_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 formatter.parse(date);
 
 return formatter.getTimeZone();
 }
 
 public static final TimeZone getTimeZoneFromDateTime(String date) throws ParseException {
 return getTimeZoneFromDateTime(date, false);
 }
 
 /**
 * Date Arithmetic function.
 * Adds the specified (signed) amount of time to the given time field,
 * based on the GregorianCalendar's rules.
 
 * @param isoString
 * @param field
 * @param amount
 * @param expanded use formating char's
 * @return ISO 8601 Date String
 * @throws ParseException
 */
 public static final String add(String isoString, int field, int amount, boolean expanded) throws ParseException {
 Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
 cal.setTime(isoToDate(isoString, expanded));
 cal.add(field, amount);
 
 return dateToISO(cal.getTime(), expanded);
 }
 
 /**
 * Date Arithmetic function.
 * Adds the specified (signed) amount of time to the given time field,
 * based on the GregorianCalendar's rules.
 * no formating char's
 *
 * @param isoString
 * @param field
 * @param amount
 * @return ISO 8601 Date String
 * @throws ParseException
 */
 public static final String add(String isoString, int field, int amount) throws ParseException {
 return add(isoString, field, amount, false);
 }
 
 /**
 * Return an ISO date string
 *
 * @param date
 * @param expanded use formating char's
 * @return ISO date String
 */
 public static final String dateToISO(Date date, boolean expanded) {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_DATE_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_DATE_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return formatter.format(date);
 }
 
 /**
 * non-expanded
 *
 * @param date
 * @return ISO Date String
 */
 public static final String dateToISO(Date date) {
 return dateToISO(date, false);
 }
 
 /**
 * Converts java Date to Julian day count
 * A Julian day is defined as the number of days since Jan 1, 1.
 *
 * @param date
 * @return julian day
 */
 public static final long dateToJulianDay(Date date) {
 return millisToJulianDay(date.getTime());
 }
 
 /**
 * Returns the days between two dates. Positive values indicate that
 * the second date is after the first, and negative values indicate, well,
 * the opposite. Relying on specific times is problematic.
 *
 * @param early the "first date"
 * @param late the "second date"
 * @return the days between the two dates
 * @deprecated
 */
 public static final int daysBetween(Date early, Date late) {
 Calendar c1 = Calendar.getInstance();
 Calendar c2 = Calendar.getInstance();
 c1.setTime(early);
 c2.setTime(late);
 
 return daysBetween(c1, c2);
 }
 
 /**
 * Returns the days between two dates. Positive values indicate that
 * the second date is after the first, and negative values indicate, well,
 * the opposite.
 *
 * @param early
 * @param late
 * @return the days between two dates.
 * @deprecated
 */
 public static final int daysBetween(Calendar early, Calendar late) {
 return (int) (toJulian(late) - toJulian(early));
 }
 
 /**
 * Returns the days between two dates. Positive values indicate that
 * the second date is after the first, and negative values indicate, well,
 * the opposite.
 *
 * @param isoEarly the "first date" in ISO DateTime Format
 * @param isoLate the "second date" in ISO Date Time format
 * @return the days between the two dates
 */
 public static final long daysBetween(String isoEarly, String isoLate, boolean expanded) throws ParseException {
 Calendar c1 = Calendar.getInstance();
 Calendar c2 = Calendar.getInstance();
 c1.setTimeZone(getTimeZoneFromDateTime(isoEarly, expanded));
 c1.setTime(isoToDate(isoEarly, expanded));
 c2.setTimeZone(getTimeZoneFromDateTime(isoLate, expanded));
 c2.setTime(isoToDate(isoLate, expanded));
 
 return millisToJulianDay(c2.getTime().getTime()) - millisToJulianDay(c1.getTime().getTime());
 }
 
 /**
 * Return an ISO date string as a java.util.Date
 *
 *
 * @param dateString
 * @param expanded use formating charaters
 * @return java.util.Date from the ISO Date in GMT
 * @throws java.text.ParseException
 */
 public static final Date isoToDate(String dateString, boolean expanded) throws ParseException {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_DATE_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_DATE_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return new Date(formatter.parse(dateString).getTime());
 }
 
 /**
 * non-expanded
 *
 * @param dateString
 * @return ISO java.util.Date
 * @throws ParseException
 */
 public static final Date isoToDate(String dateString) throws ParseException {
 return isoToDate(dateString, false);
 }
 
 /**
 * Return an ISO date string as a java.sql.Date
 *
 * @param dateString
 * @param expanded    expanded use formating charaters
 * @return java.util.Date from the ISO Date in GMT
 * @throws java.text.ParseException
 */
 public static final java.sql.Date isoToSQLDate(String dateString, boolean expanded) throws ParseException {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_DATE_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_DATE_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return new java.sql.Date(formatter.parse(dateString).getTime());
 }
 
 /**
 * non-expanded
 *
 * @param dateString
 * @return java.sql.Date
 * @throws ParseException
 */
 public static final java.sql.Date isoToSQLDate(String dateString) throws ParseException {
 return isoToSQLDate(dateString, false);
 }
 
 /**
 *
 * @param expanded expanded use formating charaters
 * @param dateString
 * @return ISO Date String
 * @throws java.text.ParseException
 */
 public static final Time isoToTime(String dateString, boolean expanded) throws ParseException {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_TIME_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_TIME_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return new Time(formatter.parse(dateString).getTime());
 }
 
 /**
 * non-expanded
 *
 * @param dateString
 * @return Time
 * @throws java.text.ParseException
 */
 public static final Time isoToTime(String dateString) throws ParseException {
 return isoToTime(dateString, false);
 }
 
 /**
 *
 * @param expanded expanded use formating charaters
 * @param dateString
 * @return ISO Time String
 * @throws java.text.ParseException
 */
 public static final Timestamp isoToTimestamp(String dateString, boolean expanded) throws ParseException {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_DATE_TIME_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_DATE_TIME_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return new Timestamp(formatter.parse(dateString).getTime());
 }
 
 /**
 * non-expanded
 *
 * @param dateString
 * @return Timestamp
 * @throws java.text.ParseException
 */
 public static final Timestamp isoToTimestamp(String dateString) throws ParseException {
 return isoToTimestamp(dateString, false);
 }
 
 /**
 * Convert a julian day count to a java sql Date @ midnight
 *
 * @param julian the given Julian day number.
 * @return java.sql.Date
 */
 public static final java.sql.Date julianDayCountToDate(long julian) {
 return new java.sql.Date(julianDayToMillis(julian));
 }
 
 /**
 * Convert a julian day count to a java util Date @ midnight
 *
 * @param julian the given Julian day number.
 * @return java.util.Date
 */
 public static final Date julianDayToDate(long julian) {
 return new Date(julianDayToMillis(julian));
 }
 
 /**
 * Converts Julian day to time as milliseconds.
 *
 * @param julian the given Julian day number.
 * @return time as milliseconds.
 */
 public static final long julianDayToMillis(long julian) {
 return (julian - EPOCH_JULIAN_DAY + JAN_1_1_JULIAN_DAY) * ONE_DAY;
 }
 
 /**
 * Converts time as milliseconds to Julian day count
 * A Julian day is defined as the number of days since Jan 1, 1.
 *
 * @param millis the given milliseconds.
 * @return the Julian day number.
 */
 public static final long millisToJulianDay(long millis) {
 return EPOCH_JULIAN_DAY - JAN_1_1_JULIAN_DAY + (millis / ONE_DAY);
 }
 
 /**
 * Time Field Rolling function.
 * Rolls (up/down) a single unit of time on the given time field.
 *
 * @param isoString
 * @param field the time field.
 * @param up Indicates if rolling up or rolling down the field value.
 * @param expanded use formating char's
 * @exception ParseException if an unknown field value is given.
 */
 public static final String roll(String isoString, int field, boolean up, boolean expanded) throws ParseException {
 Calendar cal = GregorianCalendar.getInstance(getTimeZoneFromDateTime(isoString, expanded));
 cal.setTime(isoToDate(isoString, expanded));
 cal.roll(field, up);
 
 return dateToISO(cal.getTime(), expanded);
 }
 
 /**
 * Time Field Rolling function.
 * Rolls (up/down) a single unit of time on the given time field.
 *
 * @param isoString
 * @param field the time field.
 * @param up Indicates if rolling up or rolling down the field value.
 * @exception ParseException if an unknown field value is given.
 */
 public static final String roll(String isoString, int field, boolean up) throws ParseException {
 return roll(isoString, field, up, false);
 }
 
 /**
 *
 * @param expanded expanded use formating charaters
 * @param date
 * @return ISO Time String
 */
 public static final String timeToISO(Time date, boolean expanded) {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_TIME_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_TIME_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return formatter.format(date);
 }
 
 /**
 * non-expanded
 *
 * @param date
 * @return ISO Time String
 */
 public static final String timeToISO(Time date) {
 return timeToISO(date, false);
 }
 
 /**
 *
 * @param expanded expanded use formating charaters
 * @param date
 * @return ISO Date Time String
 */
 public static final String timestampToISO(Timestamp date, boolean expanded) {
 SimpleDateFormat formatter;
 
 if (expanded) {
 formatter = new SimpleDateFormat(ISO_EXPANDED_DATE_TIME_FORMAT, dateFormatSymbles);
 } else {
 formatter = new SimpleDateFormat(ISO_DATE_TIME_FORMAT, dateFormatSymbles);
 }
 
 formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
 
 return formatter.format(date);
 }
 
 /**
 * non-expanded
 *
 * @param date
 * @return ISO Date/Time String
 */
 public static final String timestampToISO(Timestamp date) {
 return timestampToISO(date, false);
 }
 
 /**
 * Returns the Date from a julian. The Julian date will be converted to noon GMT,
 * such that it matches the nearest half-integer (i.e., a julian date of 1.4 gets
 * changed to 1.5, and 0.9 gets changed to 0.5.)
 *
 * @param JD the Julian date
 * @return the Gregorian date
 * @deprecated
 */
 public static final Date toDate(float JD) {
 /* To convert a Julian Day Number to a Gregorian date, assume that it is for 0 hours, Greenwich time (so
 * that it ends in 0.5). Do the following calculations, again dropping the fractional part of all
 * multiplicatons and divisions. Note: This method will not give dates accurately on the
 * Gregorian Proleptic Calendar, i.e., the calendar you get by extending the Gregorian
 * calendar backwards to years earlier than 1582. using the Gregorian leap year
 * rules. In particular, the method fails if Y<400. */
 float Z = (normalizedJulian(JD)) + 0.5f;
 float W = (int) ((Z - 1867216.25f) / 36524.25f);
 float X = (int) (W / 4f);
 float A = (Z + 1 + W) - X;
 float B = A + 1524;
 float C = (int) ((B - 122.1) / 365.25);
 float D = (int) (365.25f * C);
 float E = (int) ((B - D) / 30.6001);
 float F = (int) (30.6001f * E);
 int day = (int) (B - D - F);
 int month = (int) (E - 1);
 
 if (month > 12) {
 month = month - 12;
 }
 
 int year = (int) (C - 4715); //(if Month is January or February) or C-4716 (otherwise)
 
 if (month > 2) {
 year--;
 }
 
 Calendar c = Calendar.getInstance();
 c.set(Calendar.YEAR, year);
 c.set(Calendar.MONTH, month - 1); // damn 0 offsets
 c.set(Calendar.DATE, day);
 
 return c.getTime();
 }
 
 /**
 * Return a Julian date based on the input parameter. This is
 * based from calculations found at
 * <a href="http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html">Julian Day Calculations
 * (Gregorian Calendar)</a>, provided by Bill Jeffrys.
 * @param c a calendar instance
 * @return the julian day number
 */
 public static final float toJulian(Calendar c) {
 int Y = c.get(Calendar.YEAR);
 int M = c.get(Calendar.MONTH);
 int D = c.get(Calendar.DATE);
 int A = Y / 100;
 int B = A / 4;
 int C = 2 - A + B;
 float E = (int) (365.25f * (Y + 4716));
 float F = (int) (30.6001f * (M + 1));
 float JD = (C + D + E + F) - 1524.5f;
 
 return JD;
 }
 
 /**
 * Return a Julian date based on the input parameter. This is
 * based from calculations found at
 * <a href="http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html">Julian Day Calculations
 * (Gregorian Calendar)</a>, provided by Bill Jeffrys.
 * @param date
 * @return the julian day number
 */
 public static final float toJulian(Date date) {
 Calendar c = Calendar.getInstance();
 c.setTime(date);
 
 return toJulian(c);
 }
 
 protected static final float normalizedJulian(float JD) {
 float f = Math.round(JD + 0.5f) - 0.5f;
 
 return f;
 }
 
 /**
 * Divide two long integers, returning the floor of the quotient.
 * <p>
 * Unlike the built-in division, this is mathematically well-behaved.
 * E.g., <code>-1/4</code> => 0
 * but <code>floorDivide(-1,4)</code> => -1.
 * @param numerator the numerator
 * @param denominator a divisor which must be > 0
 * @return the floor of the quotient.
 */
 private static final long floorDivide(long numerator, long denominator) {
 // We do this computation in order to handle
 // a numerator of Long.MIN_VALUE correctly
 return (numerator >= 0) ? (numerator / denominator) : (((numerator + 1) / denominator) - 1);
 }
 }
 |