001/*
002 *    Geotoolkit.org - An Open Source Java GIS Toolkit
003 *    http://www.geotoolkit.org
004 *
005 *    (C) 2000-2012, Open Source Geospatial Foundation (OSGeo)
006 *    (C) 2009-2012, Geomatys
007 *
008 *    This library is free software; you can redistribute it and/or
009 *    modify it under the terms of the GNU Lesser General Public
010 *    License as published by the Free Software Foundation;
011 *    version 2.1 of the License.
012 *
013 *    This library is distributed in the hope that it will be useful,
014 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
015 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
016 *    Lesser General Public License for more details.
017 *
018 *    NOTE: permission has been given to the JScience project (http://www.jscience.org)
019 *          to distribute this file under BSD-like license.
020 */
021package edu.wisc.ssec.mcidasv.util;
022
023import java.util.Date;
024import java.util.Locale;
025import java.util.TimeZone;
026import java.text.DateFormat;
027import java.text.ParseException;
028import java.text.SimpleDateFormat;
029import java.io.PrintStream;
030
031
032/**
033 * Approximations de quelques calculs astronomiques relatifs aux calendriers terrestres.
034 * Les différents cycles astronomiques (notamment le jour, le mois et l'année) ne sont pas
035 * constants. Par exemple, la longueur de l'année tropicale (le nombre moyen de jours entre
036 * deux équinoxes vernales) était d'environ 365,242196 jours en 1900 et devrait être d'environ
037 * 365,242184 jours en 2100, soit un changement d'environ 1 seconde. Cette classe permet de
038 * calculer la longueur d'une année ou d'un mois à une date spécifiée. Toutefois, il est
039 * important de noter que les intervalles de temps calculés par les méthodes de cette classe
040 * sont des <strong>moyennes</strong>. Pour une année en particulier, l'intervalle de temps
041 * d'un équinoxe vernale au prochain peut s'écarter de cette moyenne de plusieurs minutes.
042 * <p>
043 * Les calculs de la longueur de l'année tropicale sont basés sur les travaux de Laskar (1986).
044 * Les calculs de la longueur des mois synodiques sont basés sur les travaux de Chapront-Touze et
045 * Chapront (1988).On peut lire plus de détails au sujet des calendrier terrestre au site
046 * <a href="http://webexhibits.org/calendars/year-astronomy.html">http://webexhibits.org/calendars/year-astronomy.html</a>
047 * ainsi que
048 * <a href="http://www.treasure-troves.com/astro/TropicalYear.html">http://www.treasure-troves.com/astro/TropicalYear.html</a>.
049 *
050 * @author Martin Desruisseaux (IRD)
051 * @version 3.00
052 *
053 * @since 2.0
054 * @module
055 */
056public final class Calendar {
057    /**
058     * Nombre de millisecondes dans une journée. Cette constante est
059     * utilisée pour convertir des intervalles de temps du Java en
060     * nombre de jours.
061     */
062    private static final double MILLIS_IN_DAY = 1000*60*60*24;
063
064    /**
065     * Jour julien correspondant à l'époch du Java (1er janvier 1970 à minuit).
066     * Cette constante est utilisée pour convertir des dates du Java en jour
067     * julien.
068     *
069     * La valeur {@link #julianDay}   du 1er janvier 2000 00:00 GMT est 2451544.5 jours.
070     * La valeur {@link Date#getTime} du 1er janvier 2000 00:00 GMT est 10957 jours.
071     */
072    private static final double JULIAN_DAY_1970 = 2451544.5-10957;
073
074    /**
075     * Do not allow instantiation of this class.
076     */
077    private Calendar() {
078    }
079
080    /**
081     * Returns the julian day of the given date. This is the number of days elapsed since
082     * January 1st, 4713 before J.C. at noon GMT. This is named after <cite>Julius Scaliger</cite>,
083     * not to be confused to the number of days elapsed since the beginning of the year (named after
084     * <cite>Julius Caesar</cite>).
085     *
086     * @param  time The time for which to evaluate the julian day.
087     * @return Number of days elapsed since January 1st, 4713 before J.C. at noon GMT.
088     */
089    public static double julianDay(final Date time) {
090        return julianDay(time.getTime());
091    }
092
093    /**
094     * Computes the {@linkplain #julianDay(Date) julian day}.
095     *
096     * @param time The date in milliseconds elapsed since January 1st, 1970.
097     */
098    static double julianDay(final long time) {
099        return (time/MILLIS_IN_DAY) + JULIAN_DAY_1970;
100    }
101
102    /**
103     * Retourne le nombre de siècles écoulés depuis le 1 janvier 2000 à midi.
104     * Cette information est utilisée dans les formules de Laskar (1986) pour
105     * calculer la longueur d'une année tropicale, ainsi que par Chapront-Touze
106     * et Chapront (1988) pour la longueur d'un mois synodique.
107     */
108    static double julianCentury(final Date time) {
109        return ((time.getTime() / MILLIS_IN_DAY) + (JULIAN_DAY_1970 - 2451545.0)) / 36525;
110    }
111
112    /**
113     * Retourne la longueur de l'année tropicale. L'année tropicale est définie comme l'intervalle
114     * moyen entre deux équinoxes vernales (autour du 21 mars dans l'hémisphère nord). Il correspond
115     * au cycle des saisons. Cet intervalle de temps est une <strong>moyenne</strong>. Un cycle réel
116     * peut s'écarter de plusieurs minutes de cette moyenne. Notez aussi qu'une année tropicale
117     * n'est pas identique à une année sidérale, qui est le temps requis par la Terre pour compléter
118     * un orbite autour du Soleil. En l'an 2000, l'année tropicale avait une longueur d'environ
119     * 365,2422 jours tandis que l'année sidérale avait une longueur de 365,2564 jours.
120     *
121     * @param  time A date in the year for which to compute the length.
122     * @return The tropical length of the given year.
123     */
124    public static double tropicalYearLength(final Date time) {
125        final double T = julianCentury(time);
126        return 365.2421896698 + T*(-0.00000615359 + T*(-7.29E-10 + T*(2.64E-10)));
127    }
128
129    /**
130     * Retourne la longueur du mois synodique. Le mois synodique est l'intervalle de temps moyen
131     * entre deux conjonctions de la lune et du soleil. Il correspond au cycle des phases de la
132     * lune. Cet intervalle de temps est une <strong>moyenne</strong>. Un cycle réel peut s'écarter
133     * de plusieurs heures de cette moyenne.
134     *
135     * @param  time A date in the month for which to compute the length.
136     * @return The synodic length of the given month.
137     */
138    public static double synodicMonthLength(final Date time) {
139        final double T=julianCentury(time);
140        return 29.5305888531 + T*(0.00000021621 + T*(-3.64E-10));
141    }
142
143    /**
144     * Returns a date format to be used by the command line tools.
145     */
146    static DateFormat getDateFormat() {
147        final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CANADA);
148        format.setTimeZone(TimeZone.getTimeZone("UTC"));
149        return format;
150    }
151
152    /**
153     * Prints the length of a tropical year and a synodic month for the given date.
154     * Cette application peut être lancée avec la syntaxe suivante:
155     * This application can be launch from the command line as below:
156     *
157     * {@preformat shell
158     *     java org.geotoolkit.nature.Calendar [date]
159     * }
160     *
161     * where <var>date</var> is an optional argument specifying the date and time in the
162     * {@code "yyyy-MM-dd HH:mm:ss"} format, UTC time zone. If this argument is omitted,
163     * then the current time is used.
164     *
165     * @param args The command line argument.
166     * @throws ParseException If the date is not properly formatted.
167     */
168    public static void main(final String[] args) throws ParseException {
169        final DateFormat format = getDateFormat();
170        final Date time = (args.length != 0) ? format.parse(args[0]) : new Date();
171        final PrintStream out = System.out;
172        out.print("Date (UTC)   : "); out.println(format.format(time));
173        out.print("Tropical year: "); out.println(tropicalYearLength(time));
174        out.print("Synodic month: "); out.println(synodicMonthLength(time));
175    }
176}