001/*
002 *  Copyright 2001-2012 Stephen Colebourne
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.joda.time;
017
018import java.lang.reflect.Method;
019import java.text.DateFormatSymbols;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.LinkedHashMap;
023import java.util.Locale;
024import java.util.Map;
025
026import org.joda.time.chrono.ISOChronology;
027
028/**
029 * DateTimeUtils provide public utility methods for the date-time library.
030 * <p>
031 * DateTimeUtils is thread-safe although shared static variables are used.
032 *
033 * @author Stephen Colebourne
034 * @since 1.0
035 */
036public class DateTimeUtils {
037
038    /** The singleton instance of the system millisecond provider. */
039    private static final SystemMillisProvider SYSTEM_MILLIS_PROVIDER = new SystemMillisProvider();
040    /** The millisecond provider currently in use. */
041    private static volatile MillisProvider cMillisProvider = SYSTEM_MILLIS_PROVIDER;
042    /** The millisecond provider currently in use. */
043    private static volatile Map<String, DateTimeZone> cZoneNames;
044    static {
045        // names from RFC-822 / JDK
046        // this is all very US-centric and dubious, but perhaps it will help some
047        Map<String, DateTimeZone> map = new LinkedHashMap<String, DateTimeZone>();
048        map.put("UT", DateTimeZone.UTC);
049        map.put("UTC", DateTimeZone.UTC);
050        map.put("GMT", DateTimeZone.UTC);
051        put(map, "EST", "America/New_York");
052        put(map, "EDT", "America/New_York");
053        put(map, "CST", "America/Chicago");
054        put(map, "CDT", "America/Chicago");
055        put(map, "MST", "America/Denver");
056        put(map, "MDT", "America/Denver");
057        put(map, "PST", "America/Los_Angeles");
058        put(map, "PDT", "America/Los_Angeles");
059        cZoneNames = Collections.unmodifiableMap(map);
060    }
061    private static void put(Map<String, DateTimeZone> map, String name, String id) {
062        try {
063            map.put(name, DateTimeZone.forID(id));
064        } catch (RuntimeException ex) {
065            // ignore
066        }
067    }
068
069    /**
070     * Restrictive constructor
071     */
072    protected DateTimeUtils() {
073        super();
074    }
075
076    //-----------------------------------------------------------------------
077    /**
078     * Gets the current time in milliseconds.
079     * <p>
080     * By default this returns <code>System.currentTimeMillis()</code>.
081     * This may be changed using other methods in this class.
082     * 
083     * @return the current time in milliseconds from 1970-01-01T00:00:00Z
084     */
085    public static final long currentTimeMillis() {
086        return cMillisProvider.getMillis();
087    }
088
089    /**
090     * Resets the current time to return the system time.
091     * <p>
092     * This method changes the behaviour of {@link #currentTimeMillis()}.
093     * Whenever the current time is queried, {@link System#currentTimeMillis()} is used.
094     * 
095     * @throws SecurityException if the application does not have sufficient security rights
096     */
097    public static final void setCurrentMillisSystem() throws SecurityException {
098        checkPermission();
099        cMillisProvider = SYSTEM_MILLIS_PROVIDER;
100    }
101
102    /**
103     * Sets the current time to return a fixed millisecond time.
104     * <p>
105     * This method changes the behaviour of {@link #currentTimeMillis()}.
106     * Whenever the current time is queried, the same millisecond time will be returned.
107     * 
108     * @param fixedMillis  the fixed millisecond time to use
109     * @throws SecurityException if the application does not have sufficient security rights
110     */
111    public static final void setCurrentMillisFixed(long fixedMillis) throws SecurityException {
112        checkPermission();
113        cMillisProvider = new FixedMillisProvider(fixedMillis);
114    }
115
116    /**
117     * Sets the current time to return the system time plus an offset.
118     * <p>
119     * This method changes the behaviour of {@link #currentTimeMillis()}.
120     * Whenever the current time is queried, {@link System#currentTimeMillis()} is used
121     * and then offset by adding the millisecond value specified here.
122     * 
123     * @param offsetMillis  the fixed millisecond time to use
124     * @throws SecurityException if the application does not have sufficient security rights
125     */
126    public static final void setCurrentMillisOffset(long offsetMillis) throws SecurityException {
127        checkPermission();
128        if (offsetMillis == 0) {
129            cMillisProvider = SYSTEM_MILLIS_PROVIDER;
130        } else {
131            cMillisProvider = new OffsetMillisProvider(offsetMillis);
132        }
133    }
134
135    /**
136     * Sets the provider of the current time to class specified.
137     * <p>
138     * This method changes the behaviour of {@link #currentTimeMillis()}.
139     * Whenever the current time is queried, the specified class will be called.
140     * 
141     * @param millisProvider  the provider of the current time to use, not null
142     * @throws SecurityException if the application does not have sufficient security rights
143     * @since 2.0
144     */
145    public static final void setCurrentMillisProvider(MillisProvider millisProvider) throws SecurityException {
146        if (millisProvider == null) {
147            throw new IllegalArgumentException("The MillisProvider must not be null");
148        }
149        checkPermission();
150        cMillisProvider = millisProvider;
151    }
152
153    /**
154     * Checks whether the provider may be changed using permission 'CurrentTime.setProvider'.
155     * 
156     * @throws SecurityException if the provider may not be changed
157     */
158    private static void checkPermission() throws SecurityException {
159        SecurityManager sm = System.getSecurityManager();
160        if (sm != null) {
161            sm.checkPermission(new JodaTimePermission("CurrentTime.setProvider"));
162        }
163    }
164
165    //-----------------------------------------------------------------------
166    /**
167     * Gets the millisecond instant from the specified instant object handling null.
168     * <p>
169     * If the instant object is <code>null</code>, the {@link #currentTimeMillis()}
170     * will be returned. Otherwise, the millis from the object are returned.
171     * 
172     * @param instant  the instant to examine, null means now
173     * @return the time in milliseconds from 1970-01-01T00:00:00Z
174     */
175    public static final long getInstantMillis(ReadableInstant instant) {
176        if (instant == null) {
177            return DateTimeUtils.currentTimeMillis();
178        }
179        return instant.getMillis();
180    }
181
182    //-----------------------------------------------------------------------
183    /**
184     * Gets the chronology from the specified instant object handling null.
185     * <p>
186     * If the instant object is <code>null</code>, or the instant's chronology is
187     * <code>null</code>, {@link ISOChronology#getInstance()} will be returned.
188     * Otherwise, the chronology from the object is returned.
189     * 
190     * @param instant  the instant to examine, null means ISO in the default zone
191     * @return the chronology, never null
192     */
193    public static final Chronology getInstantChronology(ReadableInstant instant) {
194        if (instant == null) {
195            return ISOChronology.getInstance();
196        }
197        Chronology chrono = instant.getChronology();
198        if (chrono == null) {
199            return ISOChronology.getInstance();
200        }
201        return chrono;
202    }
203
204    //-----------------------------------------------------------------------
205    /**
206     * Gets the chronology from the specified instant based interval handling null.
207     * <p>
208     * The chronology is obtained from the start if that is not null, or from the
209     * end if the start is null. The result is additionally checked, and if still
210     * null then {@link ISOChronology#getInstance()} will be returned.
211     * 
212     * @param start  the instant to examine and use as the primary source of the chronology
213     * @param end  the instant to examine and use as the secondary source of the chronology
214     * @return the chronology, never null
215     */
216    public static final Chronology getIntervalChronology(ReadableInstant start, ReadableInstant end) {
217        Chronology chrono = null;
218        if (start != null) {
219            chrono = start.getChronology();
220        } else if (end != null) {
221            chrono = end.getChronology();
222        }
223        if (chrono == null) {
224            chrono = ISOChronology.getInstance();
225        }
226        return chrono;
227    }
228
229    //-----------------------------------------------------------------------
230    /**
231     * Gets the chronology from the specified interval object handling null.
232     * <p>
233     * If the interval object is <code>null</code>, or the interval's chronology is
234     * <code>null</code>, {@link ISOChronology#getInstance()} will be returned.
235     * Otherwise, the chronology from the object is returned.
236     * 
237     * @param interval  the interval to examine, null means ISO in the default zone
238     * @return the chronology, never null
239     */
240    public static final Chronology getIntervalChronology(ReadableInterval interval) {
241        if (interval == null) {
242            return ISOChronology.getInstance();
243        }
244        Chronology chrono = interval.getChronology();
245        if (chrono == null) {
246            return ISOChronology.getInstance();
247        }
248        return chrono;
249    }
250
251    //-----------------------------------------------------------------------
252    /**
253     * Gets the interval handling null.
254     * <p>
255     * If the interval is <code>null</code>, an interval representing now
256     * to now in the {@link ISOChronology#getInstance() ISOChronology}
257     * will be returned. Otherwise, the interval specified is returned.
258     * 
259     * @param interval  the interval to use, null means now to now
260     * @return the interval, never null
261     * @since 1.1
262     */
263    public static final ReadableInterval getReadableInterval(ReadableInterval interval) {
264        if (interval == null) {
265            long now = DateTimeUtils.currentTimeMillis();
266            interval = new Interval(now, now);
267        }
268        return interval;
269    }
270
271    //-----------------------------------------------------------------------
272    /**
273     * Gets the chronology handling null.
274     * <p>
275     * If the chronology is <code>null</code>, {@link ISOChronology#getInstance()}
276     * will be returned. Otherwise, the chronology is returned.
277     * 
278     * @param chrono  the chronology to use, null means ISO in the default zone
279     * @return the chronology, never null
280     */
281    public static final Chronology getChronology(Chronology chrono) {
282        if (chrono == null) {
283            return ISOChronology.getInstance();
284        }
285        return chrono;
286    }
287
288    //-----------------------------------------------------------------------
289    /**
290     * Gets the zone handling null.
291     * <p>
292     * If the zone is <code>null</code>, {@link DateTimeZone#getDefault()}
293     * will be returned. Otherwise, the zone specified is returned.
294     * 
295     * @param zone  the time zone to use, null means the default zone
296     * @return the time zone, never null
297     */
298    public static final DateTimeZone getZone(DateTimeZone zone) {
299        if (zone == null) {
300            return DateTimeZone.getDefault();
301        }
302        return zone;
303    }
304
305    //-----------------------------------------------------------------------
306    /**
307     * Gets the period type handling null.
308     * <p>
309     * If the zone is <code>null</code>, {@link PeriodType#standard()}
310     * will be returned. Otherwise, the type specified is returned.
311     * 
312     * @param type  the time zone to use, null means the standard type
313     * @return the type to use, never null
314     */
315    public static final PeriodType getPeriodType(PeriodType type) {
316        if (type == null) {
317            return PeriodType.standard();
318        }
319        return type;
320    }
321
322    //-----------------------------------------------------------------------
323    /**
324     * Gets the millisecond duration from the specified duration object handling null.
325     * <p>
326     * If the duration object is <code>null</code>, zero will be returned.
327     * Otherwise, the millis from the object are returned.
328     * 
329     * @param duration  the duration to examine, null means zero
330     * @return the duration in milliseconds
331     */
332    public static final long getDurationMillis(ReadableDuration duration) {
333        if (duration == null) {
334            return 0L;
335        }
336        return duration.getMillis();
337    }
338
339    //-----------------------------------------------------------------------
340    /**
341     * Checks whether the partial is contiguous.
342     * <p>
343     * A partial is contiguous if one field starts where another ends.
344     * <p>
345     * For example <code>LocalDate</code> is contiguous because DayOfMonth has
346     * the same range (Month) as the unit of the next field (MonthOfYear), and
347     * MonthOfYear has the same range (Year) as the unit of the next field (Year).
348     * <p>
349     * Similarly, <code>LocalTime</code> is contiguous, as it consists of
350     * MillisOfSecond, SecondOfMinute, MinuteOfHour and HourOfDay (note how
351     * the names of each field 'join up').
352     * <p>
353     * However, a Year/HourOfDay partial is not contiguous because the range
354     * field Day is not equal to the next field Year.
355     * Similarly, a DayOfWeek/DayOfMonth partial is not contiguous because
356     * the range Month is not equal to the next field Day.
357     * 
358     * @param partial  the partial to check
359     * @return true if the partial is contiguous
360     * @throws IllegalArgumentException if the partial is null
361     * @since 1.1
362     */
363    public static final boolean isContiguous(ReadablePartial partial) {
364        if (partial == null) {
365            throw new IllegalArgumentException("Partial must not be null");
366        }
367        DurationFieldType lastType = null;
368        for (int i = 0; i < partial.size(); i++) {
369            DateTimeField loopField = partial.getField(i);
370            if (i > 0) {
371                if (loopField.getRangeDurationField().getType() != lastType) {
372                    return false;
373                }
374            }
375            lastType = loopField.getDurationField().getType();
376        }
377        return true;
378    }
379
380    //-----------------------------------------------------------------------
381    /**
382     * Gets the {@link DateFormatSymbols} based on the given locale.
383     * <p>
384     * If JDK 6 or newer is being used, DateFormatSymbols.getInstance(locale) will
385     * be used in order to allow the use of locales defined as extensions.
386     * Otherwise, new DateFormatSymbols(locale) will be used.
387     * See JDK 6 {@link DateFormatSymbols} for further information.
388     * 
389     * @param locale  the {@link Locale} used to get the correct {@link DateFormatSymbols}
390     * @return the symbols
391     * @since 2.0
392     */
393    public static final DateFormatSymbols getDateFormatSymbols(Locale locale) {
394        try {
395            Method method = DateFormatSymbols.class.getMethod("getInstance", new Class[] {Locale.class});
396            return (DateFormatSymbols) method.invoke(null, new Object[] {locale});
397        } catch (Exception ex) {
398            return new DateFormatSymbols(locale);
399        }
400    }
401
402    //-----------------------------------------------------------------------
403    /**
404     * Gets the default map of time zone names.
405     * <p>
406     * This can be changed by {@link #setDefaultTimeZoneNames}.
407     * 
408     * @return the unmodifiable map of abbreviations to zones, not null
409     * @since 2.2
410     */
411    public static final Map<String, DateTimeZone> getDefaultTimeZoneNames() {
412        return cZoneNames;
413    }
414
415    /**
416     * Sets the default map of time zone names.
417     * <p>
418     * The map is copied before storage.
419     * 
420     * @param names  the map of abbreviations to zones, not null
421     * @since 2.2
422     */
423    public static final void setDefaultTimeZoneNames(Map<String, DateTimeZone> names) {
424        cZoneNames = Collections.unmodifiableMap(new HashMap<String, DateTimeZone>(names));
425    }
426
427    //-------------------------------------------------------------------------
428    /**
429     * Calculates the astronomical Julian Day for an instant.
430     * <p>
431     * The <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a> is a well-known
432     * system of time measurement for scientific use by the astronomy community.
433     * It expresses the interval of time in days and fractions of a day since
434     * January 1, 4713 BC (Julian) Greenwich noon.
435     * <p>
436     * Each day starts at midday (not midnight) and time is expressed as a fraction.
437     * Thus the fraction 0.25 is 18:00. equal to one quarter of the day from midday to midday.
438     * <p>
439     * Note that this method has nothing to do with the day-of-year.
440     * 
441     * @param epochMillis  the epoch millis from 1970-01-01Z
442     * @return the astronomical Julian Day represented by the specified instant
443     * @since 2.2
444     */
445    public static final double toJulianDay(long epochMillis) {
446        // useful links
447        // http://en.wikipedia.org/wiki/Julian_day#cite_note-13 - Wikipedia
448        // http://aa.usno.navy.mil/data/docs/JulianDate.php" - USNO
449        // http://users.zoominternet.net/~matto/Java/Julian%20Date%20Converter.htm - Julian Date Converter by Matt Oltersdorf
450        // http://ssd.jpl.nasa.gov/tc.cgi#top - CalTech
451        double epochDay = epochMillis / 86400000d;
452        return epochDay + 2440587.5d;
453    }
454
455    /**
456     * Calculates the astronomical Julian Day Number for an instant.
457     * <p>
458     * The {@link #toJulianDay(long)} method calculates the astronomical Julian Day
459     * with a fraction based on days starting at midday.
460     * This method calculates the variant where days start at midnight.
461     * JDN 0 is used for the date equivalent to Monday January 1, 4713 BC (Julian).
462     * Thus these days start 12 hours before those of the fractional Julian Day.
463     * <p>
464     * Note that this method has nothing to do with the day-of-year.
465     * 
466     * @param epochMillis  the epoch millis from 1970-01-01Z
467     * @return the astronomical Julian Day represented by the specified instant
468     * @since 2.2
469     */
470    public static final long toJulianDayNumber(long epochMillis) {
471        return (long) Math.floor(toJulianDay(epochMillis) + 0.5d);
472    }
473
474    /**
475     * Creates a date-time from a Julian Day.
476     * <p>
477     * Returns the {@code DateTime} object equal to the specified Julian Day.
478     * 
479     * @param julianDay  the Julian Day
480     * @return the epoch millis from 1970-01-01Z
481     * @since 2.2
482     */
483    public static final long fromJulianDay(double julianDay) {
484        double epochDay = julianDay - 2440587.5d;
485        return (long) (epochDay * 86400000d);
486    }
487
488    //-----------------------------------------------------------------------
489    /**
490     * A millisecond provider, allowing control of the system clock.
491     * 
492     * @author Stephen Colebourne
493     * @since 2.0 (previously private)
494     */
495    public static interface MillisProvider {
496        /**
497         * Gets the current time.
498         * <p>
499         * Implementations of this method must be thread-safe.
500         * 
501         * @return the current time in milliseconds
502         */
503        long getMillis();
504    }
505
506    /**
507     * System millis provider.
508     */
509    static class SystemMillisProvider implements MillisProvider {
510        /**
511         * Gets the current time.
512         * @return the current time in millis
513         */
514        public long getMillis() {
515            return System.currentTimeMillis();
516        }
517    }
518
519    /**
520     * Fixed millisecond provider.
521     */
522    static class FixedMillisProvider implements MillisProvider {
523        /** The fixed millis value. */
524        private final long iMillis;
525        
526        /**
527         * Constructor.
528         * @param offsetMillis  the millis offset
529         */
530        FixedMillisProvider(long fixedMillis) {
531            iMillis = fixedMillis;
532        }
533        
534        /**
535         * Gets the current time.
536         * @return the current time in millis
537         */
538        public long getMillis() {
539            return iMillis;
540        }
541    }
542
543    /**
544     * Offset from system millis provider.
545     */
546    static class OffsetMillisProvider implements MillisProvider {
547        /** The millis offset. */
548        private final long iMillis;
549        
550        /**
551         * Constructor.
552         * @param offsetMillis  the millis offset
553         */
554        OffsetMillisProvider(long offsetMillis) {
555            iMillis = offsetMillis;
556        }
557        
558        /**
559         * Gets the current time.
560         * @return the current time in millis
561         */
562        public long getMillis() {
563            return System.currentTimeMillis() + iMillis;
564        }
565    }
566
567}