001    /*
002     * $Id: Time.java,v 1.3 2012/02/19 17:35:39 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    package edu.wisc.ssec.mcidasv.data.adde.sgp4;
032    
033    import java.text.DateFormat;
034    import java.text.DecimalFormat;
035    import java.text.SimpleDateFormat;
036    import java.util.Calendar;
037    import java.util.GregorianCalendar;
038    import java.util.TimeZone;
039    
040    /**
041     * Used to keep track of a time and convert to multiple formats.
042     * @author Shawn E. Gano
043     */
044    public class Time implements java.io.Serializable
045    {     
046        // time units
047        /**
048         * year type
049         */
050        public final static int YEAR=Calendar.YEAR;
051        /**
052         * month type
053         */
054        public final static int MONTH=Calendar.MONTH;
055        /**
056         * date type
057         */
058        public final static int DATE=Calendar.DATE;
059        /**
060         * hour type
061         */
062        public final static int HOUR=Calendar.HOUR;
063        /**
064         * Hour type
065         */
066        public final static int HOUR_OF_DAY=Calendar.HOUR_OF_DAY;
067        /**
068         * Minute type
069         */
070        public final static int MINUTE=Calendar.MINUTE;
071        /**
072         * Second type
073         */
074        public final static int SECOND=Calendar.SECOND;
075        /**
076         * Millisecond type
077         */
078        public final static int MILLISECOND=Calendar.MILLISECOND;
079        
080        // main calendar
081        private GregorianCalendar currentTime;
082        // other date formats
083        private double mjd; // Modified Julian date
084        private double mjde; // Modified Julian Day Ephemeris time 
085        
086        // decimal formats
087        private final static DecimalFormat fmt4Dig = new DecimalFormat("0000");
088        private final static DecimalFormat fmt2Dig = new DecimalFormat("00");
089        
090        // output format
091        private DateFormat dateFormat=null;
092        
093        // time zone
094        private final static TimeZone tz=TimeZone.getTimeZone("UTC");  // default internal timezone
095        private TimeZone tzStringFormat = TimeZone.getTimeZone("UTC"); // SEG removed static April 16 2009
096        
097        
098        public static void main(String args[])
099        {
100            Time t = new Time(2007,9,1,11,59,0.0);
101            System.out.println("Jul Date:" + t.getJulianDate() + ", string:" + t.getDateTimeStr());
102    //        System.out.println("MJD  :" + t.getMJD());
103    //        System.out.println("MJDE :" + t.getMJDE());
104    //        System.out.println("DT: " + t.deltaT( t.getMJD() )*60*60*24 );
105            // 2454344.5
106            t.addSeconds(120.0); // add 60 sec
107            System.out.println("Jul Date + 120 sec:" + t.getJulianDate()+ ", string:" + t.getDateTimeStr());
108            
109            t.add(Time.HOUR,12);
110            System.out.println("Jul Date + 12 hour:" + t.getJulianDate()+ ", string:" + t.getDateTimeStr());
111            
112        }
113        
114        
115        /**
116         * Default Constructor
117         */
118        public Time()
119        {
120            // create new calendar with default timezone
121            currentTime = new GregorianCalendar(tz);
122            
123            // update other time formates
124            updateTimeMeasures();
125        }
126        
127      
128        /**
129         * Constructor with given calendar date (UT)
130         *
131         * @param year year
132         * @param month month (1-12)
133         * @param day day of month
134         * @param hour hour 0-24
135         * @param min minute
136         * @param sec second and fraction of second (accurate up to 1 millisecond)
137         */
138        public Time(int year, int month, int day, int hour, int min, double sec)
139        {
140            int secInt = new Double( Math.floor(sec) ).intValue();
141            int millisec = new Double( Math.round((sec - Math.floor(sec))*1000.0) ).intValue();
142            
143            currentTime = new GregorianCalendar(tz); // set default timezone
144            
145            currentTime.set(Calendar.YEAR,year);
146            currentTime.set(Calendar.MONTH,month-1);
147            currentTime.set(Calendar.DATE,day);
148            currentTime.set(Calendar.HOUR_OF_DAY,hour);
149            currentTime.set(Calendar.MINUTE,min);
150            currentTime.set(Calendar.SECOND,secInt);
151            currentTime.set(Calendar.MILLISECOND,millisec);
152            
153            // update other time formats
154            updateTimeMeasures();
155        }
156        
157        
158    /**
159     * Updates the time to current system time
160     */
161        public void update2CurrentTime()
162        {
163            // update to current time (which is faster?)
164            //currentTime =new GregorianCalendar(tz);
165            currentTime.setTimeInMillis( System.currentTimeMillis() );
166            //currentTime.setTime( new Date() );
167            
168            // update other time formats
169            updateTimeMeasures();
170        } // update2CurrentTime
171    
172        /**
173         * Set the current time (UT) to the number of milliseconds
174         * @param milliseconds number of millisconds as in from the function Calendar.getTimeInMillis()
175         */
176        public void set(long milliseconds)
177        {
178            currentTime.setTimeInMillis(milliseconds);
179            
180            // update other time formats
181            updateTimeMeasures();
182        } // set
183        
184         /**
185         * Add specified value in specified time unit to current time
186         *
187         * @param unit int Time unit
188         * @param val int Time increment
189         */
190        public void add(int unit, int val) 
191        {
192            currentTime.add(unit, val);
193            
194            // update other time formats
195            updateTimeMeasures();
196        }
197        
198        /**
199         * Add specified seconds to current time
200         *
201         * @param seconds number of seconds to add to current time (can be fractional)
202         */
203        public void addSeconds(double seconds) 
204        {
205            // multiply input by 1000 then round off and add this number of milliseconds to date
206            int millis2Add = new Double(Math.round( seconds*1000 )).intValue();
207            
208            currentTime.add(Calendar.MILLISECOND, millis2Add);
209            
210            // update other time formats
211            updateTimeMeasures();
212        }
213        
214        /**
215         * Updates the Julian and Julian Epehermis Dates using Current GregorianCalendar
216         */
217        private void updateTimeMeasures()
218        {
219            mjd = calcMjd(currentTime);
220            mjde = mjd + deltaT(mjd);
221        }
222        
223        /**
224         * Gets the Julian Date (UT)
225         * @return Returns the Julian Date
226         */
227        public double getJulianDate()
228        {
229            return mjd + 2400000.5;
230        }
231        
232        /**
233         * Gets the Modified Julian Date (Julian date minus 2400000.5) (UT)
234         * @return Returns the Modified Julian Date
235         */
236        public double getMJD()
237        {
238            return mjd ;
239        }
240        
241        /**
242         * Gets the Modified Julian Ephemeris Date (Julian date minus 2400000.5) (TT)
243         * @return Returns the Modified Julian Ephemeris Date
244         */
245        public double getMJDE()
246        {
247            return mjde;
248        }
249        
250        /**
251         * Sets timezone for the output string to use via the function getDateTimeStr()
252         * @param aTzStringFormat time zone to format output strings with
253         */
254        public void setTzStringFormat(TimeZone aTzStringFormat)
255        {
256            tzStringFormat = aTzStringFormat;
257        }
258        
259        
260    /**
261     * Set SimpleDateFormat for displaying date/time string
262     * @param dateFormat SimpleDateFormat
263     */
264    public void setDateFormat(SimpleDateFormat dateFormat) 
265    {
266        this.dateFormat=dateFormat;
267    }
268    
269    /**
270     * Set SimpleDateFormat string
271     * ISSUE - only valid after Jan 1, 1970
272     *@param formatStr String format for simple date format to use for creating strings of the date
273     */
274    public void setDateFormat(java.lang.String formatStr)
275    {
276        if ((formatStr!=null)&&(formatStr.length()>0))
277        {
278            dateFormat=new SimpleDateFormat(formatStr);
279        }
280    }
281    
282    /**
283     * Gets the date format
284     * @return date format
285     */
286    public DateFormat getDateFormat()
287    {
288        return dateFormat;
289    }
290    
291    /**
292     * Returns the specified field
293     * @param field int The specified field
294     * @return int The field value
295     */
296    public final int get(int field) 
297    {
298        return currentTime.get(field);
299    }
300    
301    /*
302     * Get the UTC date/time string in the format  of yyyy-mm-dd hh:mm:ss
303     * If the dateFormat is not set or if the date is before Jan 1, 1970
304     * otherwise the empty string "" will be returned.)
305     * @return java.lang.String
306     */
307    public String getDateTimeStr()
308    {
309        String retStr="";
310        
311        if ((dateFormat!=null) &&( getJulianDate() >= 2440587.5))
312        {
313            dateFormat.setTimeZone(tzStringFormat);
314            retStr=dateFormat.format( currentTime.getTime() );
315        }
316        else
317        {
318            StringBuffer strBuf = new StringBuffer(fmt4Dig.format(get(Time.YEAR)));
319            strBuf.append("-");
320            strBuf.append(fmt2Dig.format(get(Time.MONTH)+1));
321            strBuf.append("-");
322            strBuf.append(fmt2Dig.format(get(Time.DATE)));
323            strBuf.append(" ");
324            strBuf.append(fmt2Dig.format(get(Time.HOUR_OF_DAY)));
325            strBuf.append(":");
326            strBuf.append(fmt2Dig.format(get(Time.MINUTE)));
327            strBuf.append(":");
328            strBuf.append(fmt2Dig.format(get(Time.SECOND)));
329            strBuf.append(" ");
330            strBuf.append( tz.getID() );
331            retStr=strBuf.toString();
332        }
333        return retStr;
334    }
335        
336        // ============================== STATIC Functions ====================================
337        
338      /** 
339       *  Calculate Modified Julian Date from calendar object
340       *
341       * @param cal  Calendar object
342       * @return Modified Julian Date (UT)
343       */
344        public static double calcMjd(Calendar cal)
345        {
346            double sec = cal.get(Calendar.SECOND) + cal.get(Calendar.MILLISECOND)/1000.0;
347            return calcMjd(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH)+1,cal.get(Calendar.DATE),cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), sec);
348        }
349            
350      /** 
351       *  Calculate Modified Julian Date from calendar date and time elements
352       *
353       * @param year  calendar year
354       * @param month calendar month
355       * @param day   calendar day
356       * @param hour  calendar hour (0-24)
357       * @param min   calendar min
358       * @param sec   calendar sec
359       * @return Modified Julian Date (UT)
360       */
361            public static double calcMjd(int year, int month, int day, int hour, int min, double sec)
362            {
363                // Variables
364                long    MjdMidnight;
365                double  FracOfDay;
366                int     b;
367                
368                if (month<=2)
369                { month+=12; --year;}
370                
371                if ( (10000L*year+100L*month+day) <= 15821004L )
372                {
373                    b = -2 + ((year+4716)/4) - 1179;     // Julian calendar
374                }
375                else
376                {
377                    b = (year/400)-(year/100)+(year/4);  // Gregorian calendar
378                }
379                
380                MjdMidnight = 365L*year - 679004L + b + (int) (30.6001*(month+1)) + day;
381                FracOfDay   = (hour+min/60.0+sec/3600.0) / 24.0;
382                
383                return MjdMidnight + FracOfDay;
384            } //calcMjd
385            
386            
387        /**
388         * Return TT minus UT.
389         *
390         * <p>Up to 1983 Ephemeris Time (ET) was used in place of TT, between
391         * 1984 and 2000 Temps Dynamique Terrestrial (TDT) was used in place of TT.
392         * The three time scales, while defined differently, form a continuous time
393         * scale for most purposes.  TT has a fixed offset from TAI (Temps Atomique
394         * International).
395         *
396         * <p>This method returns the difference TT - UT in days.  Usually this
397         * would be looked up in a table published after the fact.  Here we use
398         * polynomial fits for the distant past, for the future and also for the
399         * time where the table exists.  Except for 1987 to 2015, the expressions
400         * are taken from
401         * Jean Meeus, 1991, <I>Astronomical Algorithms</I>, Willmann-Bell, Richmond VA, p.73f.
402         * For the present (1987 to 2015 we use our own graphical linear fit to the
403         * data 1987 to 2001 from
404         * USNO/RAL, 2001, <I>Astronomical Almanach 2003</I>, U.S. Government Printing Office, Washington DC, Her Majesty's Stationery Office, London, p.K9:
405         *
406         * <p>t = Ep - 2002
407         * <p>DeltaT/s = 9.2 * t / 15 + 65
408         *
409         * <p>Close to the present (1900 to 1987) we use Schmadl and Zech:
410         *
411         * <p>t = (Ep - 1900) / 100
412         * <p>DeltaT/d = -0.000020      + 0.000297 * t
413         *    + 0.025184 * t<sup>2</sup> - 0.181133 * t<sup>3</sup><BR>
414         *    + 0.553040 * t<sup>4</sup> - 0.861938 * t<sup>5</sup>
415         *    + 0.677066 * t<sup>6</sup> - 0.212591 * t<sup>7</sup>
416         *
417         * <p>This work dates from 1988 and the equation is supposed to be valid only
418         * to 1987, but we extend its use into the near future.  For the 19th
419         * century we use Schmadl and Zech:
420         *
421         * <p>t = (Ep - 1900) / 100
422         * <p>DeltaT/d = -0.000009      +  0.003844 * t
423         *     +  0.083563 * t<sup>2</sup> +  0.865736 * t<sup>3</sup><BR>
424         *     +  4.867575 * t<sup>4</sup> + 15.845535 * t<sup>5</sup>
425         *     + 31.332267 * t<sup>6</sup> + 38.291999 * t<sup>7</sup><BR>
426         *     + 28.316289 * t<sup>8</sup> + 11.636204 * t<sup>9</sup>
427         *     +  2.043794 * t<sup>10</sup>
428         *
429         * <p>Stephenson and Houlden are credited with the equations for times before
430         * 1600.  First for the period 948 to 1600:
431         *
432         * <p>t = (Ep - 1850) / 100
433         * <p>DeltaT/s = 22.5 * t<sup>2</sup>
434         *
435         * <p>and before 948:
436         *
437         * <p>t = (Ep - 948) / 100
438         * <p>DeltaT/s = 1830 - 405 * t + 46.5 * t<sup>2</sup>
439         *
440         * <p>This leaves no equation for times between 1600 and 1800 and beyond
441         * 2015.  For such times we use the equation of Morrison and Stephenson:
442         *
443         * <p>t = Ep - 1810
444         * <p>DeltaT/s = -15 + 0.00325 * t<sup>2</sup> 
445         *
446         *  @param givenMJD Modified Julian Date (UT)
447         *  @return TT minus UT in days
448         */
449        
450        public static double deltaT(double givenMJD)
451        {
452            double theEpoch; /* Julian Epoch */
453            double t; /* Time parameter used in the equations. */
454            double D; /* The return value. */
455            
456            givenMJD -= 50000;
457            
458            theEpoch = 2000. + (givenMJD - 1545.) / 365.25;
459            
460        /* For 1987 to 2015 we use a graphical linear fit to the annual tabulation
461         * from USNO/RAL, 2001, Astronomical Almanach 2003, p.K9.  We use this up
462         * to 2015 about as far into the future as it is based on data in the past.
463         * The result is slightly higher than the predictions from that source. */
464            
465            if (1987 <= theEpoch && 2015 >= theEpoch)
466            {
467                t = (theEpoch - 2002.);
468                D = 9.2 * t / 15. + 65.;
469                D /= 86400.;
470            }
471            
472        /* For 1900 to 1987 we use the equation from Schmadl and Zech as quoted in
473         * Meeus, 1991, Astronomical Algorithms, p.74.  This is precise within
474         * 1.0 second. */
475            
476            else if (1900 <= theEpoch && 1987 > theEpoch)
477            {
478                t  = (theEpoch - 1900.) / 100.;
479                D = -0.212591 * t * t * t * t * t * t * t
480                        + 0.677066 * t * t * t * t * t * t
481                        - 0.861938 * t * t * t * t * t
482                        + 0.553040 * t * t * t * t
483                        - 0.181133 * t * t * t
484                        + 0.025184 * t * t
485                        + 0.000297 * t
486                        - 0.000020;
487            }
488            
489        /* For 1800 to 1900 we use the equation from Schmadl and Zech as quoted in
490         * Meeus, 1991, Astronomical Algorithms, p.74.  This is precise within 1.0
491         * second. */
492            
493            else if (1800 <= theEpoch && 1900 > theEpoch)
494            {
495                t  = (theEpoch - 1900.) / 100.;
496                D =  2.043794 * t * t * t * t * t * t * t * t * t * t
497                        + 11.636204 * t * t * t * t * t * t * t * t * t
498                        + 28.316289 * t * t * t * t * t * t * t * t
499                        + 38.291999 * t * t * t * t * t * t * t
500                        + 31.332267 * t * t * t * t * t * t
501                        + 15.845535 * t * t * t * t * t
502                        +  4.867575 * t * t * t * t
503                        +  0.865736 * t * t * t
504                        +  0.083563 * t * t
505                        +  0.003844 * t
506                        -  0.000009;
507            }
508            
509        /* For 948 to 1600 we use the equation from Stephenson and Houlden as
510         * quoted in Meeus, 1991, Astronomical Algorithms, p.73. */
511            
512            else if (948 <= theEpoch && 1600 >= theEpoch)
513            {
514                t  = (theEpoch - 1850.) / 100.;
515                D  = 22.5 * t * t;
516                D /= 86400.;
517            }
518            
519        /* Before 948 we use the equation from Stephenson and Houlden as quoted
520         * in Meeus, 1991, Astronomical Algorithms, p.73. */
521            
522            else if (948 > theEpoch)
523            {
524                t  = (theEpoch - 948.) / 100.;
525                D  = 46.5 * t * t - 405. * t + 1830.;
526                D /= 86400.;
527            }
528            
529        /* Else (between 1600 and 1800 and after 2010) we use the equation from
530         * Morrison and Stephenson, quoted as eqation 9.1 in Meeus, 1991,
531         * Astronomical Algorithms, p.73. */
532            
533            else
534            {
535                t  = theEpoch - 1810.;
536                D  = 0.00325 * t * t - 15.;
537                D /= 86400.;
538            }
539            
540            return D; // in days
541        } // deltaT
542    
543        public GregorianCalendar getCurrentGregorianCalendar()
544        {
545            return currentTime;
546        }
547        
548        // function to take a given Julian date and parse it to a Gerorian Calendar
549        public static GregorianCalendar convertJD2Calendar(double jd)
550        {
551             /**
552             * Calculate calendar date for Julian date field this.jd
553             */
554            Double jd2 = new Double(jd + 0.5);
555            long I = jd2.longValue();
556            double F = jd2.doubleValue() - (double) I;
557            long A = 0;
558            long B = 0;
559    
560            if (I > 2299160)
561            {
562                Double a1 = new Double(((double) I - 1867216.25) / 36524.25);
563                A = a1.longValue();
564                Double a3 = new Double((double) A / 4.0);
565                B = I + 1 + A - a3.longValue();
566            }
567            else
568            {
569                B = I;
570            }
571    
572            double C = (double) B + 1524;
573            Double d1 = new Double((C - 122.1) / 365.25);
574            long D = d1.longValue();
575            Double e1 = new Double(365.25 * (double) D);
576            long E = e1.longValue();
577            Double g1 = new Double((double) (C - E) / 30.6001);
578            long G = g1.longValue();
579            Double h = new Double((double) G * 30.6001);
580            long da = (long) C - E - h.longValue();
581            
582            Integer date = new Integer((int) da); // DATE
583    
584            Integer month;
585            Integer year;
586            
587            if (G < 14L)
588            {
589                month = new Integer((int) (G - 2L));
590            }
591            else
592            {
593                month = new Integer((int) (G - 14L));
594            }
595    
596            if (month.intValue() > 1)
597            {
598                year = new Integer((int) (D - 4716L));
599            }
600            else
601            {
602                year = new Integer((int) (D - 4715L));
603            }
604    
605            // Calculate fractional part as hours, minutes, and seconds
606            Double dhr = new Double(24.0 * F);
607            Integer hour = new Integer(dhr.intValue());
608            Double dmin = new Double((dhr.doubleValue() - (double) dhr.longValue()) * 60.0);
609            Integer minute = new Integer(dmin.intValue());
610            
611            Double dsec = new Double((dmin.doubleValue() - (double) dmin.longValue()) * 60.0);
612            Integer second=new Integer(dsec.intValue());
613            
614            //int ms = (int)((dsec.doubleValue() - (double) second.longValue()) * 1000.0);
615            // rounding fix - e-mailed to SEG by Hani A. Altwaijry 28 May 2009
616            int ms = (int) Math.round((dsec.doubleValue() - (double) second.longValue()) * 1000.0);
617            
618            // create Calendar object
619            GregorianCalendar newTime = new GregorianCalendar(tz); // set default timezone
620            
621            newTime.set(Calendar.YEAR,year);
622            newTime.set(Calendar.MONTH,month);
623            newTime.set(Calendar.DATE,date);
624            newTime.set(Calendar.HOUR_OF_DAY,hour);
625            newTime.set(Calendar.MINUTE,minute);
626            newTime.set(Calendar.SECOND,second);
627            newTime.set(Calendar.MILLISECOND,ms);
628            
629            return newTime;
630            
631        } // convertJD2Calendar
632        
633        // function used to take a given Julian Date and parse it as a string using the current settings 
634        public String convertJD2String(double jd)
635        {
636            // convert to calendar
637            GregorianCalendar newTime = convertJD2Calendar(jd);
638            
639            // format as String -- ASSUMES dateFromat is not NULL!!
640            dateFormat.setTimeZone(tzStringFormat);
641            String retStr=dateFormat.format( newTime.getTime() );
642            
643            return retStr;
644            
645    
646        } // convertJD2String
647        
648    
649        
650        
651    }