/* Julian.java
 * Copyright (C) 1996 by William Giel
 *
 * E-mail: rvdi@usa.nai.net
 * WWW: http://www.nai.net/~rvdi/home.htm
 *
 ***************************************************************************
 * Abstract
 * --------
 * This unit provides julian calendar conversions, as well as several
 * date/time conversions used by TickTock.
 ***************************************************************************
 * Permission to use, copy, modify, and distribute this software and its
 * documentation without fee for NON-COMMERCIAL purposes is hereby granted.
 * 
 * THE AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
 * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THE AUTHOR SHALL NOT BE LIABLE
 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 ***************************************************************************/

import java.lang.*;
import java.util.*;

//////////////////////////////////////////////////
// Julian could be the basis for a more thorough
// set of methods for handling julian/gregorian
// calendar conversions, various date and time
// formats, etc. For now, it contains only those
// methods required in ticktock.
/////////////////////////////////////////////////
public class Julian
{
	//////////////////////////////////////////////////
	// Here's a compact way to get day and month names
	//////////////////////////////////////////////////
	static final String days="SunMonTueWedThuFriSat";
	static final String months="JanFebMarAprMayJunJulAugSepOctNovDec";

	///////////////////////////////////////////////////////////
	// Convert julian day number to IETF date format.
	// (This formula not valid for negative julian day numbers.)
	// Reference: Astronomical Formulae for Calculators
	// Meeus, Jean. Willmann-Bell,Inc. Richmond, VA 1985, p.26
	///////////////////////////////////////////////////////////
	public static String jd2ietf(double jd)
	{
		jd = jd + .5;
		double Z=(long)jd;
		double F=jd-Z;
		double A;

		if(Z < 2299161) A = Z;
		else{
			double alpha = (long)(Z - 1867216.25)/36524.25;
			A = Z + 1 + alpha - (long)(alpha/4.0);
		}

		double B = A + 1524;
		double C = (long)((B-122.1)/365.25);
		double D = (long)(365.25 * C);
		double E = (long)((B - D)/30.6001);
		double day_of_month = B - D - (long)(30.6001 * E) + F;
		int month = (E < 13.5)? (int)(E - 1) : (int)(E - 13);
		int year = ((double)month > 2.5)? (int)C - 4716 : (int)C - 4715;
		int day_of_week = (int)((long)(jd + 1.5) % 7L);
		month-=1;
		
		return new String(	days.substring(day_of_week*3, day_of_week*3 + 3) + ", " +
							String.valueOf((int)day_of_month) + " " +
							months.substring(month*3, month*3 + 3) + " " +
							String.valueOf(year));
	}

	//////////////////////////////////////////////////
	// Convert a 'Modified Julian Day Number' to IETF
	// date format, by first converting mjd to jd.
	// See Network Time Protocol/RFC 1305 for
	// definition of MJD
	//////////////////////////////////////////////////
	public static String mjd2ietf(double mjd)
	{
		double jd = mjd + 2400000.5;
		return jd2ietf(jd);
	}

	/////////////////////////////////////////////
	// Produce an IETF Date/Time string from the
	// java Date class.
	/////////////////////////////////////////////
	public static String date2ietf(Date date)
	{
        if(null==date)return null;
	    
		/////////////////////////////////////////
		// Get what we need from the Date class
		/////////////////////////////////////////
		String hours 		= 	String.valueOf(date.getHours());
		String minutes 		= 	String.valueOf(date.getMinutes());
		String seconds 		= 	String.valueOf(date.getSeconds());
		String month 		= 	months.substring(date.getMonth()*3,date.getMonth()*3+3);
		String day_of_week 	= 	days.substring(date.getDay()*3,date.getDay()*3+3);
		String year 		= 	String.valueOf(1900+date.getYear());
		String day 			= 	String.valueOf(date.getDate());

		/////////////////////////////////////////////
		//If a single digit in time , place a leading
		//zero in front.
		/////////////////////////////////////////////
		if(hours.length()	< 2) hours= ("0" + hours);
		if(minutes.length()	< 2) minutes= ("0" + minutes);
		if(seconds.length()	< 2) seconds= ("0" + seconds);

		////////////////////////////////////////
		// Build our date string and return
		////////////////////////////////////////
		return new String( 	day_of_week + " " + day + " " + month +
							" " + year + " " + hours + ":" + minutes +
							":" + seconds );
	}

    //////////////////////////////////////////////////////////
	// Parse the data received from TCP/Port 13 time service
	// to IETF format.
	// Incoming format is unique to U.S. Naval Observatory
	// master clock "tick" and "tock" servers.
	//////////////////////////////////////////////////////////
	public static String usno2ietf(String string)
	{
	    if(null == string)return null;

	    String mjd=null;
	    String time=null;
	    String zone=null;
	    
        StringTokenizer st = new StringTokenizer(string);

        //Get the modified julian day number
        if(!st.hasMoreTokens())return null;
        else mjd = st.nextToken();

        //Get day of year, even though we do not use it.
        if(!st.hasMoreTokens())return null;
        else String doy = st.nextToken();

        //Get time token
        if(!st.hasMoreTokens())return null;
        else time = st.nextToken();

        zone = (st.hasMoreTokens())? st.nextToken(): "";

        if(time.length() != 6)return null;

        return new String(mjd2ietf(Double.valueOf(mjd).doubleValue()) + " " +
                          time.substring(0,2) + ":" + time.substring(2,4) + ":" +
                          time.substring(4));
 	}
 	
	///////////////////////////////////////////////////////////
	// Convert java.util.Date to julian day number.
 	// Not valid for negative years.
	// Reference: Astronomical Formulae for Calculators
	// Meeus, Jean. Willmann-Bell,Inc. Richmond, VA 1985, p.24
	///////////////////////////////////////////////////////////
 	public static double date2julian(Date date)
 	{
 	    int month = date.getMonth()+ 1;
 	    int year = date.getYear() + 1900;

 	    if (month <= 2){
 	        year-=1;
 	        month+=12;
 	    }

 	    //////////////////////////////////////////////
 	    // We'll assume we're in the Gregorian
 	    // calendar (on or after 15 Oct 1582) thus...
 	    //////////////////////////////////////////////
 	    double A = (long)(year/100);
 	    double B = 2 - A + (long)(A/4);
 	    double jd=(long)(365.25 * (double)year) + (long)(30.6001 * (month + 1))
 	                        + date.getDate() + 1720994.5 - B;

 	    //Now, add in the time...
 	    jd=jd + (double)date.getHours()/24.0 + (double)date.getMinutes()/1440.0
 	                        + date.getSeconds()/86400.0;

        //Th... Th... That's all, folks!	                        
        return jd;
 	}
  
}

