//*********************************************************************************
// class tig.Dates
// Software released under the General Public License (version 2 or later), available at
// http://www.gnu.org/copyleft/gpl.html
//*********************************************************************************
package tig;

import tig.GeneralConstants;
import tig.DateFormatException;
import tig.HourFormatException;
import tig.Formats;
import java.util.GregorianCalendar;
import java.util.Calendar;

/**********************************************************************************
Class containing utility static methods for date manipulation.

Dates are represented as an array of integers.
The internationalization of this class is hard coded (French and English are supported).
This class handles :
  • Date - julian day conversions (dateToJd(), jdToDate()).
  • Conversion between a String and a date (parseDate(), parseHour()).
  • Date formatting for display (formatDate(), formatHour()).
  • @author Thierry Graff @history oct 04 2001 : creation from jephem.astro.spaceTime.Time.java. @history oct 15 2001 : added jdToDate and formatHourDate. @history feb 04 2002 : Separated date and hour parsing and formating. @todo : WARNING : coherence of exceptions thrown by the different methods @todo test with negative years in dateToJd() @todo check date validity in dateToJd() @todo DAYS_PER_YEAR should maybe be defined in tig API. **********************************************************************************/ public abstract class Dates implements GeneralConstants{ //================================================================================= // PRIVATE CONSTANTS //================================================================================= // codes used for languages private final static String EN = "en"; private final static String FR = "fr"; // Error message about lang private final static String ERROR_LANG = "Parameter 'lang' must be \"en\" or \"fr\""; //********************************* dateToJd(int[]) **************************** /** Convenient method equivalent to {@link #dateToJd(int, int, int, int, int, int)}. */ public static double dateToJd(int[] date){ if (date.length != 6) throw new IllegalArgumentException("Parameter 'date' must have 6 elements"); return dateToJd(date[0], date[1], date[2], date[3], date[4], date[5]); } // end dateToJd(int[]) //****************** dateToJd(year, month, day, hour, minut, sec) ************** /** Calculates the number of julian days elapsed since jan 0.5 4712 BC, from a date and an hour. */ public static double dateToJd(int year, int month, int day, int hour, int minut, int sec){ double jd; double y1, m1; double decDay = day + hour/24.0 + minut/1440.0 + sec/86400.0; if(month>2){ y1 = year; m1 = month; } else{ y1 = year-1; m1 = month+12; } if (year > 0) jd = Math.floor(365.25*y1) + Math.floor(30.6001*(m1+1)) + decDay + 1720994.5; else jd = Math.floor(365.25*y1 - 0.75) + Math.floor(30.6001*(m1+1)) + decDay + 1720994.5; if((year+month/100.0 + decDay/10000.0) >= 1582.1015){ double a = Math.floor(y1/100.0); double b = 2-a+Math.floor(a/4.0); jd = jd + b; } return jd; }// end dateToJd(year, month, day, hour, minut, sec) //******************************** jdToDate **************************** /** Computes the date and hour corresponding to a julian day (nb of days elapsed since jan 0.5 4712 BC).
    From Jean Meeus' Astronomical Algorithms, 2nd edition, p 63. @param jd A positive number expressing the julian day. @return An array of six integers containing (in this order) : year, month, day, hour, minut, second. @throws RuntimeException If parameter 'jd' < 0. */ public static int[] jdToDate(double jd){ int[] res = new int[6]; double DAYS_PER_YEAR = 365.25; if (jd < 0) throw new IllegalArgumentException("jdToDate() not valid for negative julian days"); double z = Math.floor(jd); double f = jd - z; double a; if (z < 2299161.0) a = z; else{ double alpha = Math.floor((z - 1867216.25) / 36524.25); a = z + 1 + alpha - Math.floor(alpha/4.0); } double b = a + 1524.0; double c = Math.floor((b - 122.1) / DAYS_PER_YEAR); double d = Math.floor(DAYS_PER_YEAR * c); double e = Math.floor((b - d) / 30.6001); // months if (e < 14.0) res[1] = (int)(e - 1); else res[1] = (int)(e - 13); // years if (res[1] > 2) res[0] = (int)(c - 4716); else res[0] = (int)(c - 4715); // days res[2] = (int)(b - d - Math.floor(30.6001 * e)); // hours double hours = (f + 0.5)%1.0 * 24.0; // decimal hours res[3] = (int)(Math.floor(hours)); // minuts double minuts = (hours - Math.floor(hours)) * 60.0; // decimal minuts res[4] = (int)(Math.floor(minuts)); // seconds double secs = (minuts - Math.floor(minuts)) * 60.0; // decimal seconds res[5] = (int)(Math.round(secs)); return res; }// end jdToDate //**************************** isLeapYear ************************************* /** Tests if y corresponds to a leap year. @param y positive number representing the year to test. @return true if y is positive and corresponds to a leap year. */ public static boolean isLeapYear(int y){ if (y < 0) return false; if ((y % 4 == 0 && !(y % 100 == 0)) || (y % 400 == 0)) return true; else return false; }// end isLeapYear //**************************** Now ************************************* /** Returns an array containing date fields (in this order : year, month, day, hour, minuts, seconds) initialized to the current date. */ public static int[] now(){ int[] date = new int[6]; GregorianCalendar c = new GregorianCalendar(); date[0] = c.get(Calendar.YEAR); date[1] = c.get(Calendar.MONTH); date[2] = c.get(Calendar.DAY_OF_MONTH); date[3] = c.get(Calendar.HOUR_OF_DAY); date[4] = c.get(Calendar.MINUTE); date[5] = c.get(Calendar.SECOND); return date; }// end now //*************************** format methods from an int[] *************************** /** Formats a date and an hour into a displayable String. @param hourDate An array containing integers representing : year, month, day, hour, minut, second. @param lang language code, as specified by ISO-639 norm (supported languages : "en" and "fr"). @return if lang='fr', format is "JJ/MM/AAAA HH:MM:SS" ;
    if lang='en', format is "MM/JJ/AAAA HH:MM:SS". @throws IllegalArgumentException if :
  • the length of 'hourDate' is different from 6 ;
  • the language is different from "en" or "fr".
  • */ public static String formatHourDate(int[] hourDate, String lang){ // parameters checking if(hourDate.length != 6) throw new IllegalArgumentException("Parameter 'hourDate' must contain 6 integers"); if(!(lang.equals(EN) || lang.equals(FR))) throw new IllegalArgumentException(ERROR_LANG); String strDate = formatDate(new int[]{hourDate[0], hourDate[1], hourDate[2]}, lang); String strHour = formatHour(new int[]{hourDate[3], hourDate[4], hourDate[5]}, lang); return strDate + " " + strHour; }// end formatHourDate(int[] hourDate, String lang) /** Formats a date into a displayable String. @param date An array containing 3 integers representing : year, month, day. @param lang language code, as specified by ISO-639 norm (supported languages : "en" and "fr"). @return if lang='fr', format is "JJ/MM/AAAA" ;
    if lang='en', format is "MM/JJ/AAAA". @throws IllegalArgumentException if :
  • the length of 'date' is different from 3 ;
  • the language is different from "en" or "fr".
  • */ public static String formatDate(int[] date, String lang){ // parameters checking if(date.length != 3) throw new IllegalArgumentException("Parameter 'date' must contain 3 integers"); if(!(lang.equals(EN) || lang.equals(FR))) throw new IllegalArgumentException(ERROR_LANG); String[] strDate = new String[3]; strDate[0] = String.valueOf(date[0]); for (int i = 1; i < 3; i++) strDate[i] = Formats.addZero(date[i]); if (lang.equals(EN)){ return strDate[1] + "/" + strDate[2] + "/" + strDate[0]; } else{ //if (lang.equals(FR)){ return strDate[2] + "/" + strDate[1] + "/" + strDate[0]; } }// end formatDate(int[] date, String lang) /** Formats an hour into a displayable String. @param hour An array containing 3 integers representing : hour, minut, second. @return A String formatted "HH:MM:SS", for lang='fr' or 'en'; @throws IllegalArgumentException if :
  • the length of 'hour' is different from 3 ;
  • the language is different from "en" or "fr".
  • */ public static String formatHour(int[] hour, String lang){ // parameters checking if(hour.length != 3) throw new IllegalArgumentException("Parameter 'hour' must contain 3 integers"); if(!(lang.equals(EN) || lang.equals(FR))) throw new IllegalArgumentException(ERROR_LANG); String[] strHour = new String[3]; for (int i = 0; i < 3; i++) strHour[i] = Formats.addZero(hour[i]); return strHour[0] + ":" + strHour[1] + ":" + strHour[2]; }// end formatHour(int[] hour, String lang) //*************************** format methods from a String *************************** /** Facility method, calling parseHourDate() and {@link #formatHourDate(int[], String)}. @throws DateFormatException if parameter 'strDate' is not correct. @throws HourFormatException if parameter 'strHour' is not correct. */ public static String formatHourDate(String strDate, String strHour, String lang) throws DateFormatException, HourFormatException{ try{ int[] hourDate = parseHourDate(strDate, strHour, lang); return formatHourDate(hourDate, lang); } catch(HourFormatException e){ throw e;} catch(DateFormatException e){ throw e;} }// end formatHourDate(String strHourDate, String lang) /** Facility method, calling parseDate() and {@link #formatDate(int[], String)}. @throws DateFormatException if parameter 'strDate' is not correct. */ public static String formatDate(String strDate, String lang) throws DateFormatException{ try{ int[] date = parseDate(strDate, lang); return formatDate(date, lang); } catch(DateFormatException e){ throw e;} }// end formatDate /** Facility method, calling parseHour() and {@link #formatHour(int[], String)}. @throws HourFormatException if parameter 'strHour' is not correct. */ public static String formatHour(String strHour, String lang) throws HourFormatException{ try{ int[] hour = parseHour(strHour, lang); return formatHour(hour, lang); } catch(HourFormatException e){ throw e;} }// end formatHour //************************************** parse methods ************************************** /** Transforms a date and an hour representated as Strings to an array of integers containing (in this order : years, months, days, hours, minutes, seconds.
    This method just calls {@link #parseHour(String, String)} and {@link #parseDate(String, String)}. See their documentation for informations about parameter formats and exception thrown. */ public static int[] parseHourDate(String strDate, String strHour, String lang) throws DateFormatException, HourFormatException, IllegalArgumentException{ // check language if (!lang.equals(EN) && !lang.equals(FR)) throw new IllegalArgumentException("Parameter 'lang' must be 'en' or 'fr'"); try{ int[] date = parseDate(strDate, lang); int[] hour = parseHour(strHour, lang); return new int[]{date[0], date[1], date[2], hour[0], hour[1], hour[2]}; } catch(DateFormatException e){throw e;} catch(HourFormatException e){throw e;} catch(IllegalArgumentException e){throw e;} }// end parseHourDate /** Transforms a date representated as a String to an array of integers containing the year, month, day.
    How to express the date in 'strDate' :
  • Years, months and days must be separated by any non-numeric separator.
  • Years, months and days can be expressed with any number of digits ; years can be preceeded by a minus sign (if you want to express a negative year, first put a separator, and then the '-').
  • For English ('lang' = "en"), the order in 'strDate' must be months, days, years.
  • For French ('lang' = "fr"), the order in 'strDate' must be days, months, years.


  • Example of valid inputs and results.
    French :
    strDateyearsmonthsdays
    18/07/1998 18 7 1998
    18.007:1998 18 7 1998
    9-10 2000 9 10 2000
    9 10 1 9 10 1
    9 10 -16 9 10 -16
    9-10--16 9 10 -16
    9-10-16 9 10 16

    English :
    strDateyearMonthDay
    07/18/1998 18 7 1998
    007:18.1998 18 7 1998
    9-10 2000 10 9 2000
    9 10 1 9 10 1
    9 10 -16 10 9 -16
    9-10--16 10 9 -16
    9-10-16 10 9 16
    @param strDate A String containing the date, as specified above. @param lang language code, as specified by ISO-639 norm (supported languages : "en" and "fr"). @return an array of integers in which are stored (in this order) years, months, days. @throws IllegalArgumentException if parameter 'lang' is different from 'en' or 'fr'. @throws DateFormatException if parameter 'strDate' is not correctly formatted. */ public static int[] parseDate(String strDate, String lang) throws DateFormatException, IllegalArgumentException{ // check language if (!lang.equals(EN) && !lang.equals(FR)) throw new IllegalArgumentException("Parameter 'lang' must be 'en' or 'fr'"); try{ int[] results = new int[3]; int len,i; String str1=BLANK, str2=BLANK, str3=BLANK, yearSep=BLANK; strDate = strDate.trim(); // *** 1 - Date parsing len = strDate.length(); i=0; // str1 while(i 1 && yearSep.charAt(yearSep.length() - 1)=='-') results[0] = -results[0]; //*** 2 - Check date validity. checkDate(results[0], results[1], results[2]); return results; } catch(DateFormatException e){throw e;} }// end parseDate /** Transforms an hour representated as a String to an array of integers containing (in this order : hours, minutes, seconds.
    Parameter 'lang' has no effect.
    How to express the hour in 'strHour' :
  • Hours, minuts and seconds must be separated by any non-numeric character
  • Any non-numeric digit at the beginning or at the end of 'strHour" are skipped
  • If 'strHour' is empty, hours, minuts, seconds will be considered as equal to 0.
  • Seconds or {minutes and seconds} can be omitted, they will be considered as equal to 0.


  • Example of valid inputs and results.
    strHourhoursminutsseconds
    (empty string) 0 0 0
    12 12 0 0
    12h30m 12 30 0
    -12:30:40s 12 30 40
    000012h000030m00040 12 30 40
    @param strHour A String containing the hour, as specified above.. @param lang language code, as specified by ISO-639 norm (supported languages : 'en' and 'fr'). @return an array of integers in which are stored (in this order) hours, minuts, seconds. @throws HourFormatException if parameter 'strHour' is not correctly formatted. @throws IllegalArgumentException if parameter 'lang' is different from 'en' or 'fr'. */ public static int[] parseHour(String strHour, String lang) throws HourFormatException, IllegalArgumentException{ // check language if (!lang.equals(EN) && !lang.equals(FR)) throw new IllegalArgumentException("Parameter 'lang' must be 'en' or 'fr'"); try{ int[] results = new int[3]; int len,i; String str1, str2, str3; strHour = strHour.trim(); //*** Hour parsing len = strHour.length(); if (len == 0){ // hour not specified results[0] = 0; results[1] = 0; results[2] = 0; // hours, minuts, seconds return results; } str1=str2=str3=BLANK; i=0; // remove useless characters at the begining of String. while(iex : 23.75 means 23rd, 18h00m00s. */ public static double calcDecDay(int d, int h, int m, int s){ return d + h/24.0 + m/1440.0 + s/86400.0; }//end calcDecDay //================================================================================= // PRIVATE METHODS //================================================================================= //********************** checkHour **************************** /** Check hour validity. */ private static void checkHour(int h, int m, int s) throws HourFormatException{ if (h < 0 || h > 23) throw new HourFormatException("Hours must be between 0 and 23"); if (m < 0 || m > 59) throw new HourFormatException("Minutes must be between 0 and 59"); if (s < 0 || s > 59) throw new HourFormatException("Seconds must be between 0 and 59"); }// end checkHour //********************** checkDate **************************** /** Check date validity. Parameters : year, month, day. */ private static void checkDate(int y, int m, int d) throws DateFormatException{ int[] dpm = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // days per month // month validity if (m < 1 || m > 12) throw new DateFormatException("Months must be between 1 and 12"); // day validity if (m == 2){ // february if (isLeapYear(y)){ if (d > 29) throw new DateFormatException("Days < 29 in february of a leap year"); } else{ if (d > 28) throw new DateFormatException("Days < 28 in february of a normal year"); } } else{ // other months if (d > dpm[m-1]) throw new DateFormatException("Days of this can't be greater than "+ dpm[m-1]); } }// end checkDate //================================================================================= //================================================================================= // TESTS //================================================================================= //================================================================================= /* // **************** For tests only **************** public static void main(String[] args){ // no complete argument checking if(args[0].equalsIgnoreCase("testParseDate")) testParseDate(args[1], args[2]); else if(args[0].equalsIgnoreCase("testParseHour")) testParseHour(args[1], args[2]); else if(args[0].equalsIgnoreCase("testParseHourDate")) testParseHourDate(args[1], args[2], args[3]); else if(args[0].equalsIgnoreCase("testFormatDate")) testFormatDate(args[1], args[2]); else if(args[0].equalsIgnoreCase("testFormatHour")) testFormatHour(args[1], args[2]); else if(args[0].equalsIgnoreCase("testFormatHourDate")) testFormatHourDate(args[1], args[2], args[3]); else System.out.println("first argument must be 'testParseDate' or 'testParseDate'" + " or 'testFormatDate' or 'testFormatHour' or 'testFormatHourDate'"); }// end main // **************** For tests only **************** private static void testParseDate(String strDate, String lang){ try{ String[] str = {"year", "month", "day"}; int[] res = parseDate(strDate, lang); for(int i = 0; i < 3; i++) System.out.println(str[i] + " = " + res[i]); } catch(Exception e){e.printStackTrace();} }// end testParseDate // **************** For tests only **************** private static void testParseHour(String strHour, String lang){ try{ String[] str = {"h", "m", "s"}; int[] res = parseHour(strHour, lang); for(int i = 0; i < 3; i++) System.out.println(str[i] + " = " + res[i]); } catch(Exception e){e.printStackTrace();} }// end testParseHour // **************** For tests only **************** private static void testParseHourDate(String strHour, String strDate, String lang){ try{ String[] str = {"year", "month", "day", "h", "m", "s"}; int[] res = parseHourDate(strHour, strDate, lang); for(int i = 0; i < 6; i++) System.out.println(str[i] + " = " + res[i]); } catch(Exception e){e.printStackTrace();} }// end testParseHour // **************** For tests only **************** private static void testFormatDate(String strDate, String lang){ try{ System.out.println(formatDate(strDate, lang)); } catch(Exception e){e.printStackTrace();} }// end testFormatDate // **************** For tests only **************** private static void testFormatHour(String strHour, String lang){ try{ System.out.println(formatHour(strHour, lang)); } catch(Exception e){e.printStackTrace();} }// end testFormatDate // **************** For tests only **************** private static void testFormatHourDate(String strHour, String strDate, String lang){ try{ System.out.println(formatHourDate(strHour, strDate, lang)); } catch(Exception e){e.printStackTrace();} }// end testFormatHourDate */ }//end abstract class Dates

    Ads help maintain this website.