/*-------------------------------------------------------------------------------------
 * Copyright:   R.S.J. Koster  2001
 *
 * Changes:
 * Ver. Date        Name       Searchcode    Reason
 * 1.0  2001-10-01  R. Koster  -             Initial coding.
 *
 *-------------------------------------------------------------------------------------
 */

import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * Calculates the Julian date for a given date-time.
 *
 * @see "Documentation on java.util.GregorianCalendar"
 * @see <a href="http://aa.usno.navy.mil/data/docs/JulianDate.html">Julian Dates</a>
 *
 * @version 1.0
 * @author  R. Koster
 */
public class JulianDate {
    /**
     * Validates a given date.
     *
     * @return true if valid, else false.
     */
    private static boolean isValidDate( int y, int m, int d ) {
        boolean valid = true;

        if (y < 1) {valid = false;}
        if ( (m < 1) || (m > 12) ) {valid = false;}
        if ( (d < 1) || (d > 31) ) {valid = false;}

        if ( (m == 4) || (m == 6) || (m == 9) || (m == 11) ) {
            if (d > 30) {valid = false;}
        }

        if (m == 2) {
            GregorianCalendar calendar = new GregorianCalendar(y, m - 1, d); // NB. See NB on month numbering method main.
            if ( calendar.isLeapYear(y) ) {
                if (d > 29) {valid = false;}
            }
            else {
                if (d > 28) {valid = false;}
            }
        }

        return valid;
    }

    /**
     * Validates a given time.
     *
     * @return true if valid, else false.
     */
    private static boolean isValidTime( int h, int m, int s )
    {
        if ((h < 0) || (h > 23)) {return false;}
        if ((m < 0) || (m > 59)) {return false;}
        if ((s < 0) || (s > 59)) {return false;}
        return true;
    }

    /**
     * Main loop.
     *
     * @param args optional date, time and era strings. When ran without arguments the syntax is displayed.
     */
    public static void main( String[] args ) {
        if (args.length == 0) {
            System.out.println( "JulianDate version 1.0" );
            System.out.println( "Usage: java JulianDate yyyy-mm-dd [hh:mm:ss [a|b]]" );
            System.out.println();
            System.out.println(
                "With a = AD and b = BC (era).\n" +
                "Defaults: 12:00:00 a\n" +
                "NB. Time entered is assumed to be UT (Universal Time).\n" +
                "\n" +
                "JulianDate is based on java.util.GregorianCalendar. For more information on calendars \n" +
                "and Julian Dates see the documentation on that class.\n" +
                "Also see http://aa.usno.navy.mil/data/docs/JulianDate.html" );
        }
        else {
            // Get date from arguments.
            int y = 0;
            int m = 0;
            int d = 0;
            boolean invalidDate = false;

            try {
                y = Integer.parseInt( args[0].substring(0, 4) );
                m = Integer.parseInt( args[0].substring(5, 7) );
                d = Integer.parseInt( args[0].substring(8, 10) );
            }
            catch (Exception e) {invalidDate = true;}
            if ( !isValidDate(y, m, d) ) {invalidDate = true;}
            if (invalidDate) {System.out.println("Invalid date.");}

            // Get time from arguments.
            int h = 12;
            int min = 0;
            int s = 0;
            boolean invalidTime = false;

            if (args.length > 1) {
                try {
                    h = Integer.parseInt( args[1].substring(0, 2) );
                    min = Integer.parseInt( args[1].substring(3, 5) );
                    s = Integer.parseInt( args[1].substring(6, 8) );
                }
                catch (Exception e) {invalidTime = true;}
                if ( !isValidTime(h, min, s) ) {invalidTime = true;}
                if (invalidTime) {System.out.println("Invalid time.");}
            }

            // Get era from arguments.
            boolean ad = true;
            boolean invalidEra = false;

            if (args.length == 3) {
                if ( args[2].equals("a") ) {ad = true;}
                else {
                    if ( args[2].equals("b") ) {ad = false;}
                    else {
                        invalidEra = true;
                        System.out.println("Invalid era.");
                    };
                }
            }

            if (!invalidDate && !invalidTime && !invalidEra) {
                // Calculate user date milliseconds.

                // NB. GregorianCalendar numbers the months jan = 0, feb = 1, ... Which is unhandy.
                Calendar calendar = new GregorianCalendar( y, m - 1, d, h, min, s );
                if (!ad) {calendar.set( Calendar.ERA, GregorianCalendar.BC );}
                long t1 = calendar.getTime().getTime();
                // Convert to the UT time the user meant.
                t1 = t1 + calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);

                // Calculate epoch date milliseconds.

                calendar.set( Calendar.ERA, GregorianCalendar.BC );
                calendar.set( Calendar.YEAR, 4713 );
                calendar.set( Calendar.MONTH, 0 );
                calendar.set( Calendar.DAY_OF_MONTH, 1 );
                calendar.set( Calendar.HOUR_OF_DAY, 12 );
                calendar.set( Calendar.MINUTE, 0 );
                calendar.set( Calendar.SECOND, 0 );
                long t0 = calendar.getTime().getTime();
                // Convert to the UT time.
                t0 = t0 + calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);

                // Finally calculate Julian date.

                long deltaT = t1 - t0;
                double delta = deltaT;
                delta = delta / (1000 * 60 * 60 * 24);

                System.out.println( "Julian date = " + delta );
            }
        }
    }
}

