001    package edu.nrao.sss.measure;
002    
003    import java.math.BigDecimal;
004    import java.math.RoundingMode;
005    
006    import static edu.nrao.sss.math.MathUtil.MC_FINAL_CALC;
007    
008    import edu.nrao.sss.util.EnumerationUtility;
009    import edu.nrao.sss.util.Symbolic;
010    
011    /**
012     * Units of time.
013     * <p>
014     * This class enumerates those units of time that are well defined.
015     * The unit <i>month</i>, for example, is not considered to be
016     * well-defined because sometimes it is 28 days, sometimes 29, and
017     * so on.</p>
018     * <p>
019     * <b><u>Table of Units</u></b></p>
020     * <p style="margin-left:1.5em">
021     * <table border="1" cellspacing="0">
022     *   <tr BGCOLOR="#EEEEFF"><th>Element</th>  <th>Name(s)<sup><i>1</i></sup></th>
023     *       <th>Symbol(s)<sup><i>1</i></sup></th>  <th>Seconds</th></tr>
024     *   <tr><td>MICROSECOND</td><td>MICROSECOND</td>
025     *       <td align="center">&#x00B5;s</td><td align="right">10<sup>-6</sup></td></tr>
026     *   <tr><td>MILLISECOND</td><td>MILLISECOND</td>
027     *       <td align="center">ms</td><td align="right">0.001</td></tr>
028     *   <tr><td>SECOND</td><td>SECOND</td>
029     *       <td align="center">s</td><td align="right">1.0</td></tr>
030     *   <tr><td>MINUTE</td><td>MINUTE</td>
031     *       <td align="center">m</td><td align="right">60.0</td></tr>
032     *   <tr><td>HOUR</td><td>HOUR</td>
033     *       <td align="center">h</td><td align="right">3,600.0</td></tr>
034     *   <tr><td>DAY</td><td>DAY</td>
035     *       <td align="center">d</td><td align="right">86,400.0</td></tr>
036     *   <tr><td>YEAR</td><td>YEAR</td>
037     *       <td align="center">y</td><td align="right">31,556,925.18748800</td></tr>
038     * </table>
039     * <sup><b>1</b></sup>The names in these columns may be sent to
040     * {@link #fromString(String)}.  Note that the names are
041     * not case sensitive.<br/>
042     * </p>
043     * <p>
044     * <b><u>Table of Conversion Factors<sup>2</sup></u></b></p>
045     * <p style="margin-left:1.5em">
046     * <table border="1" cellspacing="0">
047     * <tr BGCOLOR="#EEEEFF"><th> </th><th>ns</th><th>&micro;s</th><th>ms</th><th>s</th><th>m</th><th>h</th><th>d</th><th>w</th><th>ty</th><th>y</th></tr>
048     * <tr><td>NANOSECOND</td><td align="right">1</td><td align="right">0.001</td><td align="right">0.000001</td><td align="right">1E-9</td><td align="right">1.66666666666666666666666666666666666666666666666666666666666666666666666666666666666666667E-11</td><td align="right">2.777777777777777777777777777777777777777777777777777777777777777777777777777777777777778E-13</td><td align="right">1.15740740740740740740740740740740740740740740740740740740740740740740740740740740740741E-14</td><td align="right">1.6534391534391534391534391534391534391534391534391534391534391534391534391534391534392E-15</td><td align="right">3.16887653663353660911395687389964307585456367629474904594101671868225683971378495887E-17</td></tr>
049     * <tr><td>MICROSECOND</td><td align="right">1E+3</td><td align="right">1</td><td align="right">0.001</td><td align="right">0.000001</td><td align="right">1.66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667E-8</td><td align="right">2.777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778E-10</td><td align="right">1.15740740740740740740740740740740740740740740740740740740740740740740740740740740740740741E-11</td><td align="right">1.6534391534391534391534391534391534391534391534391534391534391534391534391534391534391534E-12</td><td align="right">3.16887653663353660911395687389964307585456367629474904594101671868225683971378495887162E-14</td></tr>
050     * <tr><td>MILLISECOND</td><td align="right">1E+6</td><td align="right">1E+3</td><td align="right">1</td><td align="right">0.001</td><td align="right">0.0000166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667</td><td align="right">2.777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778E-7</td><td align="right">1.15740740740740740740740740740740740740740740740740740740740740740740740740740740740740740741E-8</td><td align="right">1.6534391534391534391534391534391534391534391534391534391534391534391534391534391534391534392E-9</td><td align="right">3.16887653663353660911395687389964307585456367629474904594101671868225683971378495887162299E-11</td></tr>
051     * <tr><td>SECOND</td><td align="right">1E+9</td><td align="right">1E+6</td><td align="right">1E+3</td><td align="right">1</td><td align="right">0.0166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667</td><td align="right">0.0002777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778</td><td align="right">0.0000115740740740740740740740740740740740740740740740740740740740740740740740740740740740740740740741</td><td align="right">0.0000016534391534391534391534391534391534391534391534391534391534391534391534391534391534391534391534</td><td align="right">3.16887653663353660911395687389964307585456367629474904594101671868225683971378495887162298978E-8</td></tr>
052     * <tr><td>MINUTE</td><td align="right">6E+10</td><td align="right">6E+7</td><td align="right">6E+4</td><td align="right">6E+1</td><td align="right">1</td><td align="right">0.0166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667</td><td align="right">0.0006944444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444</td><td align="right">0.0000992063492063492063492063492063492063492063492063492063492063492063492063492063492063492063492063</td><td align="right">0.0000019013259219801219654683741243397858455127382057768494275646100312093541038282709753229737938657</td></tr>
053     * <tr><td>HOUR</td><td align="right">3.6E+12</td><td align="right">3.6E+9</td><td align="right">3.6E+6</td><td align="right">3.6E+3</td><td align="right">6E+1</td><td align="right">1</td><td align="right">0.0416666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667</td><td align="right">0.005952380952380952380952380952380952380952380952380952380952380952380952380952380952380952380952381</td><td align="right">0.0001140795553188073179281024474603871507307642923466109656538766018725612462296962585193784276319423</td></tr>
054     * <tr><td>DAY</td><td align="right">8.64E+13</td><td align="right">8.64E+10</td><td align="right">8.64E+7</td><td align="right">8.64E+4</td><td align="right">1.44E+3</td><td align="right">24</td><td align="right">1</td><td align="right">0.1428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571429</td><td align="right">0.0027379093276513756302744587390492916175383430163186631756930384449414699095127102044650822631666143</td></tr>
055     * <tr><td>WEEK</td><td align="right">6.048E+14</td><td align="right">6.048E+11</td><td align="right">6.048E+8</td><td align="right">6.048E+5</td><td align="right">1.008E+4</td><td align="right">168</td><td align="right">7</td><td align="right">1</td><td align="right">0.0191653652935596294119212111733450413227684011142306422298512691145902893665889714312555758421662999</td></tr>
056     * <tr><td>YEAR</td><td align="right">3.15569252522016E+16</td><td align="right">31556925252201.6</td><td align="right">31556925252.2016</td><td align="right">31556925.2522016</td><td align="right">525948.75420336</td><td align="right">8765.812570056</td><td align="right">365.242190419</td><td align="right">52.1774557741428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571</td><td align="right">1</td></tr>
057     * </table>
058     * <sup><b>2</b></sup>This table was generated from the conversion logic of this class.<br/>
059     * </p>
060     * <p>
061     * <b>Version Info:</b>
062     * <table style="margin-left:2em">
063     *   <tr><td>$Revision: 1725 $</td>
064     *   <tr><td>$Date: 2008-11-20 15:40:30 -0700 (Thu, 20 Nov 2008) $</td>
065     *   <tr><td>$Author: switz $</td>
066     * </table></p>
067     *
068     * @author David M. Harland
069     * @since 2006-03-23
070     */
071    public enum TimeUnits
072      implements Symbolic
073    {
074      /** @deprecated Same as {@link #YEAR}. */
075      @Deprecated TROPICAL_YEAR("31556925.18748800", "ty"),
076    
077      /**
078       * A tropical, as opposed to sidereal (or any other kind of), year.
079       * <p>
080       * The length of this year is taken as the J2000 value of
081       * 365.242 189 670 SI days.
082       * See <a href="http://en.wikipedia.org/wiki/Tropical_year">
083       * Wikipedia</a> for more information.</p>
084       */
085      YEAR("31556925.2522016", "y"),  //TODO: See if this is the year we want
086    
087      /** One week, or 7 days. */
088      WEEK("604800.0", "w"),
089    
090      /**
091       * One day, or 24 hours.
092       * <p>
093       * Note that this is one SI day, or one that does not vary with
094       * the earth's rotation and that never includes a leap second.
095       * It is exactly equal to 86,400.0 SI seconds.</p>
096       */
097      DAY("86400.0", "d"),
098    
099      /** One hour, or 60 minutes. */
100      HOUR("3600.0", "h"),
101    
102      /** One minute, or 60 seconds. */
103      MINUTE("60.0", "m"),
104    
105      /** One second. */
106      SECOND("1.0", "s"),
107    
108      /** One thousandth of a second. */
109      MILLISECOND("0.001", "ms"),
110    
111      /** One millionth of a second. */
112      MICROSECOND("1e-6", "\u00B5s"),
113    
114      /** One billionth of a second. */
115      NANOSECOND("1e-9", "ns");
116    
117      private static final int PRECISION = MC_FINAL_CALC.getPrecision(); 
118    
119      private BigDecimal seconds;
120      private String     symbol;
121    
122      private TimeUnits(String secondsPerUnit, String symbol)
123      {
124        this.symbol  = symbol;
125        this.seconds = new BigDecimal(secondsPerUnit);
126        
127        //It turns out that the PRECISION constant is zero when we're
128        //in this constructor, so we make the call directly.
129        //(See http://forums.java.net/jive/thread.jspa?threadID=40585&tstart=0)
130        this.seconds = this.seconds.setScale(MC_FINAL_CALC.getPrecision(),
131                                             RoundingMode.HALF_UP);
132      }
133      
134      /** Returns <i>false</i> -- these symbols are <i>not</i> case sensitive. */
135      public boolean symbolsAreCaseSensitive()  { return false; }
136    
137      /**
138       * Returns the symbol for this unit.
139       * For example, the symbol for {@code MILLISECOND} is <i>ms</i>.
140       *
141       * @return the symbol for this unit.
142       */
143      public String getSymbol()
144      {
145        return symbol;
146      }
147    
148      /**
149       * Returns the number of seconds in one of these units.
150       *
151       * @return the number of seconds in one of these units.
152       */
153      public BigDecimal toSeconds()
154      {
155        return seconds;
156      }
157    
158      /**
159       * Returns a factor for converting from this unit to {@code otherUnits}.
160       *
161       * @param otherUnits the unit to which conversion is desired.
162       *
163       * @return a factor for converting from this unit to {@code otherUnits}.
164       */
165      public BigDecimal toUnits(TimeUnits otherUnits)
166      {
167        return convertTo(otherUnits, BigDecimal.ONE);
168      }
169    
170      /**
171       * Non-public method for use by this and other classes in pkg.
172       * Does the actual conversion; keeps answer in BigDecimal form.
173       */
174      BigDecimal convertTo(TimeUnits otherUnits, BigDecimal value)
175      {
176        BigDecimal answer = value;
177    
178        if (!otherUnits.equals(this))
179        {
180          try  //to use BigDecimal, for better accuracy, but...
181          {
182            if (value.scale() < PRECISION)
183              value = value.setScale(PRECISION);
184            
185            BigDecimal dividend = this.seconds.multiply(value);
186    
187            answer = dividend.divide(otherUnits.seconds, MC_FINAL_CALC);
188          }
189          catch (ArithmeticException ex)
190          {
191            //...if it fails, use java primitives
192            double ratio =       this.seconds.doubleValue() /
193                           otherUnits.seconds.doubleValue();
194            
195            answer = BigDecimal.valueOf(value.doubleValue() * ratio);
196          }
197        }
198    
199        return answer.round(MC_FINAL_CALC);
200      }
201    
202      /**
203       * Returns a default unit of time.
204       * @return a default unit of time.
205       */
206      public static TimeUnits getDefault()
207      {
208        return HOUR;
209      }
210    
211      /**
212       * Returns a text representation of this enumeration constant.
213       * @return a text representation of this enumeration constant.
214       */
215      public String toString()
216      {
217        return EnumerationUtility.getSharedInstance().enumToString(this);
218      }
219    
220      /**
221       * Returns the time units represented by {@code text}.
222       * <p>
223       * For details about the transformation, see
224       * {@link EnumerationUtility#enumFromString(Class, String)}.</p>
225       *
226       * @param text a text representation of a unit of time.
227       *
228       * @return the time units represented by {@code text}.
229       */
230      public static TimeUnits fromString(String text)
231      {
232        return EnumerationUtility.getSharedInstance()
233                                 .enumFromString(TimeUnits.class, text);
234      }
235    
236      //Here for testing only
237      /*
238      private static String toHtmlTable()
239      {
240        StringBuilder table = new StringBuilder();
241    
242        TimeUnits[] sortedUnits = TimeUnits.values();
243        java.util.Arrays.sort(sortedUnits,
244                    new java.util.Comparator<TimeUnits>() {
245                      public int compare(TimeUnits a, TimeUnits b) {
246                        return a.toSeconds().compareTo(b.toSeconds());
247                      }
248                    });
249    
250    
251        //Column headers
252        table.append(" * <table border=\"1\" cellspacing=\"0\">\n");
253        table.append(" * <tr BGCOLOR=\"#EEEEFF\"><th> </th>");
254        for (TimeUnits u : sortedUnits)
255          table.append("<th>").append(u.getSymbol()).append("</th>");
256        table.append("</tr>\n");
257    
258        //Data rows
259        for (TimeUnits from : sortedUnits)
260        {
261          if (from == TROPICAL_YEAR)
262            continue;
263          
264          table.append(" * <tr><td>").append(from.name()).append("</td>");
265    
266          for (TimeUnits to : sortedUnits)
267          {
268            if (to == TROPICAL_YEAR)
269              continue;
270            
271            table.append("<td align=\"right\">").append(from.convertTo(to).stripTrailingZeros())
272                                                .append("</td>");
273          }
274          table.append("</tr>\n");
275        }
276    
277        //End table
278        table.append(" * </table>");
279    
280        return table.toString();
281      }
282    
283      public static void main(String[] args)
284      {
285        String htmlTable = TimeUnits.toHtmlTable();
286    
287        System.out.println(htmlTable);
288      }
289      */
290      /*
291      public static void main(String[] args)
292      {
293        for (TimeUnits tu : TimeUnits.values())
294          System.out.println(tu.name() + " " + tu.toString() + " " + tu.getSymbol() +
295                             " = " + tu.toSeconds() + " seconds.");
296      }
297      */
298      /*
299      public static void main(String[] args)
300      {
301        EnumSet<TimeUnits> miniSet = EnumSet.range(TimeUnits.DAY, TimeUnits.MILLISECOND);
302        for (TimeUnits tu : miniSet)
303          System.out.println(tu.name() + " " + tu.toString() + " " + tu.getSymbol());
304        System.out.println();
305        SortedSet<TimeUnits> ss = new TreeSet<TimeUnits>(miniSet);
306        for (TimeUnits tu : ss)
307          System.out.println(tu.name() + " " + tu.toString() + " " + tu.getSymbol());
308      }*/
309    }