Time.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.text.format;
18
19import android.content.res.Resources;
20
21import java.util.Locale;
22import java.util.TimeZone;
23
24/**
25 * The Time class is a faster replacement for the java.util.Calendar and
26 * java.util.GregorianCalendar classes. An instance of the Time class represents
27 * a moment in time, specified with second precision. It is modelled after
28 * struct tm, and in fact, uses struct tm to implement most of the
29 * functionality.
30 */
31public class Time {
32    private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
33    private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z";
34    private static final String Y_M_D = "%Y-%m-%d";
35
36    public static final String TIMEZONE_UTC = "UTC";
37
38    /**
39     * The Julian day of the epoch, that is, January 1, 1970 on the Gregorian
40     * calendar.
41     */
42    public static final int EPOCH_JULIAN_DAY = 2440588;
43
44    /**
45     * True if this is an allDay event. The hour, minute, second fields are
46     * all zero, and the date is displayed the same in all time zones.
47     */
48    public boolean allDay;
49
50    /**
51     * Seconds [0-61] (2 leap seconds allowed)
52     */
53    public int second;
54
55    /**
56     * Minute [0-59]
57     */
58    public int minute;
59
60    /**
61     * Hour of day [0-23]
62     */
63    public int hour;
64
65    /**
66     * Day of month [1-31]
67     */
68    public int monthDay;
69
70    /**
71     * Month [0-11]
72     */
73    public int month;
74
75    /**
76     * Year. TBD. Is this years since 1900 like in struct tm?
77     */
78    public int year;
79
80    /**
81     * Day of week [0-6]
82     */
83    public int weekDay;
84
85    /**
86     * Day of year [0-365]
87     */
88    public int yearDay;
89
90    /**
91     * This time is in daylight savings time. One of:
92     * <ul>
93     * <li><b>positive</b> - in dst</li>
94     * <li><b>0</b> - not in dst</li>
95     * <li><b>negative</b> - unknown</li>
96     * </ul>
97     */
98    public int isDst;
99
100    /**
101     * Offset from UTC (in seconds).
102     */
103    public long gmtoff;
104
105    /**
106     * The timezone for this Time.  Should not be null.
107     */
108    public String timezone;
109
110    /*
111     * Define symbolic constants for accessing the fields in this class. Used in
112     * getActualMaximum().
113     */
114    public static final int SECOND = 1;
115    public static final int MINUTE = 2;
116    public static final int HOUR = 3;
117    public static final int MONTH_DAY = 4;
118    public static final int MONTH = 5;
119    public static final int YEAR = 6;
120    public static final int WEEK_DAY = 7;
121    public static final int YEAR_DAY = 8;
122    public static final int WEEK_NUM = 9;
123
124    public static final int SUNDAY = 0;
125    public static final int MONDAY = 1;
126    public static final int TUESDAY = 2;
127    public static final int WEDNESDAY = 3;
128    public static final int THURSDAY = 4;
129    public static final int FRIDAY = 5;
130    public static final int SATURDAY = 6;
131
132    /*
133     * The Locale for which date formatting strings have been loaded.
134     */
135    private static Locale sLocale;
136    private static String[] sShortMonths;
137    private static String[] sLongMonths;
138    private static String[] sShortWeekdays;
139    private static String[] sLongWeekdays;
140    private static String sTimeOnlyFormat;
141    private static String sDateOnlyFormat;
142    private static String sDateTimeFormat;
143    private static String sAm;
144    private static String sPm;
145    private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y";
146
147    /**
148     * Construct a Time object in the timezone named by the string
149     * argument "timezone". The time is initialized to Jan 1, 1970.
150     * @param timezone string containing the timezone to use.
151     * @see TimeZone
152     */
153    public Time(String timezone) {
154        if (timezone == null) {
155            throw new NullPointerException("timezone is null!");
156        }
157        this.timezone = timezone;
158        this.year = 1970;
159        this.monthDay = 1;
160        // Set the daylight-saving indicator to the unknown value -1 so that
161        // it will be recomputed.
162        this.isDst = -1;
163    }
164
165    /**
166     * Construct a Time object in the default timezone. The time is initialized to
167     * Jan 1, 1970.
168     */
169    public Time() {
170        this(TimeZone.getDefault().getID());
171    }
172
173    /**
174     * A copy constructor.  Construct a Time object by copying the given
175     * Time object.  No normalization occurs.
176     *
177     * @param other
178     */
179    public Time(Time other) {
180        set(other);
181    }
182
183    /**
184     * Ensures the values in each field are in range. For example if the
185     * current value of this calendar is March 32, normalize() will convert it
186     * to April 1. It also fills in weekDay, yearDay, isDst and gmtoff.
187     *
188     * <p>
189     * If "ignoreDst" is true, then this method sets the "isDst" field to -1
190     * (the "unknown" value) before normalizing.  It then computes the
191     * correct value for "isDst".
192     *
193     * <p>
194     * See {@link #toMillis(boolean)} for more information about when to
195     * use <tt>true</tt> or <tt>false</tt> for "ignoreDst".
196     *
197     * @return the UTC milliseconds since the epoch
198     */
199    native public long normalize(boolean ignoreDst);
200
201    /**
202     * Convert this time object so the time represented remains the same, but is
203     * instead located in a different timezone. This method automatically calls
204     * normalize() in some cases
205     */
206    native public void switchTimezone(String timezone);
207
208    private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31,
209            31, 30, 31, 30, 31 };
210
211    /**
212     * Return the maximum possible value for the given field given the value of
213     * the other fields. Requires that it be normalized for MONTH_DAY and
214     * YEAR_DAY.
215     * @param field one of the constants for HOUR, MINUTE, SECOND, etc.
216     * @return the maximum value for the field.
217     */
218    public int getActualMaximum(int field) {
219        switch (field) {
220        case SECOND:
221            return 59; // leap seconds, bah humbug
222        case MINUTE:
223            return 59;
224        case HOUR:
225            return 23;
226        case MONTH_DAY: {
227            int n = DAYS_PER_MONTH[this.month];
228            if (n != 28) {
229                return n;
230            } else {
231                int y = this.year;
232                return ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 29 : 28;
233            }
234        }
235        case MONTH:
236            return 11;
237        case YEAR:
238            return 2037;
239        case WEEK_DAY:
240            return 6;
241        case YEAR_DAY: {
242            int y = this.year;
243            // Year days are numbered from 0, so the last one is usually 364.
244            return ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 365 : 364;
245        }
246        case WEEK_NUM:
247            throw new RuntimeException("WEEK_NUM not implemented");
248        default:
249            throw new RuntimeException("bad field=" + field);
250        }
251    }
252
253    /**
254     * Clears all values, setting the timezone to the given timezone. Sets isDst
255     * to a negative value to mean "unknown".
256     * @param timezone the timezone to use.
257     */
258    public void clear(String timezone) {
259        if (timezone == null) {
260            throw new NullPointerException("timezone is null!");
261        }
262        this.timezone = timezone;
263        this.allDay = false;
264        this.second = 0;
265        this.minute = 0;
266        this.hour = 0;
267        this.monthDay = 0;
268        this.month = 0;
269        this.year = 0;
270        this.weekDay = 0;
271        this.yearDay = 0;
272        this.gmtoff = 0;
273        this.isDst = -1;
274    }
275
276    /**
277     * return a negative number if a is less than b, a positive number if a is
278     * greater than b, and 0 if they are equal.
279     */
280    native public static int compare(Time a, Time b);
281
282    /**
283     * Print the current value given the format string provided. See man
284     * strftime for what means what. The final string must be less than 256
285     * characters.
286     * @param format a string containing the desired format.
287     * @return a String containing the current time expressed in the current locale.
288     */
289    public String format(String format) {
290        synchronized (Time.class) {
291            Locale locale = Locale.getDefault();
292
293            if (sLocale == null || locale == null || !(locale.equals(sLocale))) {
294                Resources r = Resources.getSystem();
295
296                sShortMonths = new String[] {
297                    r.getString(com.android.internal.R.string.month_medium_january),
298                    r.getString(com.android.internal.R.string.month_medium_february),
299                    r.getString(com.android.internal.R.string.month_medium_march),
300                    r.getString(com.android.internal.R.string.month_medium_april),
301                    r.getString(com.android.internal.R.string.month_medium_may),
302                    r.getString(com.android.internal.R.string.month_medium_june),
303                    r.getString(com.android.internal.R.string.month_medium_july),
304                    r.getString(com.android.internal.R.string.month_medium_august),
305                    r.getString(com.android.internal.R.string.month_medium_september),
306                    r.getString(com.android.internal.R.string.month_medium_october),
307                    r.getString(com.android.internal.R.string.month_medium_november),
308                    r.getString(com.android.internal.R.string.month_medium_december),
309                };
310                sLongMonths = new String[] {
311                    r.getString(com.android.internal.R.string.month_long_january),
312                    r.getString(com.android.internal.R.string.month_long_february),
313                    r.getString(com.android.internal.R.string.month_long_march),
314                    r.getString(com.android.internal.R.string.month_long_april),
315                    r.getString(com.android.internal.R.string.month_long_may),
316                    r.getString(com.android.internal.R.string.month_long_june),
317                    r.getString(com.android.internal.R.string.month_long_july),
318                    r.getString(com.android.internal.R.string.month_long_august),
319                    r.getString(com.android.internal.R.string.month_long_september),
320                    r.getString(com.android.internal.R.string.month_long_october),
321                    r.getString(com.android.internal.R.string.month_long_november),
322                    r.getString(com.android.internal.R.string.month_long_december),
323                };
324                sShortWeekdays = new String[] {
325                    r.getString(com.android.internal.R.string.day_of_week_medium_sunday),
326                    r.getString(com.android.internal.R.string.day_of_week_medium_monday),
327                    r.getString(com.android.internal.R.string.day_of_week_medium_tuesday),
328                    r.getString(com.android.internal.R.string.day_of_week_medium_wednesday),
329                    r.getString(com.android.internal.R.string.day_of_week_medium_thursday),
330                    r.getString(com.android.internal.R.string.day_of_week_medium_friday),
331                    r.getString(com.android.internal.R.string.day_of_week_medium_saturday),
332                };
333                sLongWeekdays = new String[] {
334                    r.getString(com.android.internal.R.string.day_of_week_long_sunday),
335                    r.getString(com.android.internal.R.string.day_of_week_long_monday),
336                    r.getString(com.android.internal.R.string.day_of_week_long_tuesday),
337                    r.getString(com.android.internal.R.string.day_of_week_long_wednesday),
338                    r.getString(com.android.internal.R.string.day_of_week_long_thursday),
339                    r.getString(com.android.internal.R.string.day_of_week_long_friday),
340                    r.getString(com.android.internal.R.string.day_of_week_long_saturday),
341                };
342                sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
343                sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
344                sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
345                sAm = r.getString(com.android.internal.R.string.am);
346                sPm = r.getString(com.android.internal.R.string.pm);
347
348                sLocale = locale;
349            }
350
351            return format1(format);
352        }
353    }
354
355    native private String format1(String format);
356
357    /**
358     * Return the current time in YYYYMMDDTHHMMSS<tz> format
359     */
360    @Override
361    native public String toString();
362
363    /**
364     * Parses a date-time string in either the RFC 2445 format or an abbreviated
365     * format that does not include the "time" field.  For example, all of the
366     * following strings are valid:
367     *
368     * <ul>
369     *   <li>"20081013T160000Z"</li>
370     *   <li>"20081013T160000"</li>
371     *   <li>"20081013"</li>
372     * </ul>
373     *
374     * Returns whether or not the time is in UTC (ends with Z).  If the string
375     * ends with "Z" then the timezone is set to UTC.  If the date-time string
376     * included only a date and no time field, then the <code>allDay</code>
377     * field of this Time class is set to true and the <code>hour</code>,
378     * <code>minute</code>, and <code>second</code> fields are set to zero;
379     * otherwise (a time field was included in the date-time string)
380     * <code>allDay</code> is set to false. The fields <code>weekDay</code>,
381     * <code>yearDay</code>, and <code>gmtoff</code> are always set to zero,
382     * and the field <code>isDst</code> is set to -1 (unknown).  To set those
383     * fields, call {@link #normalize(boolean)} after parsing.
384     *
385     * To parse a date-time string and convert it to UTC milliseconds, do
386     * something like this:
387     *
388     * <pre>
389     *   Time time = new Time();
390     *   String date = "20081013T160000Z";
391     *   time.parse(date);
392     *   long millis = time.normalize(false);
393     * </pre>
394     *
395     * @param s the string to parse
396     * @return true if the resulting time value is in UTC time
397     */
398    public boolean parse(String s) {
399        if (nativeParse(s)) {
400            timezone = TIMEZONE_UTC;
401            return true;
402        }
403        return false;
404    }
405
406    /**
407     * Parse a time in the current zone in YYYYMMDDTHHMMSS format.
408     */
409    native private boolean nativeParse(String s);
410
411    /**
412     * Parse a time in RFC 3339 format.  This method also parses simple dates
413     * (that is, strings that contain no time or time offset).  For example,
414     * all of the following strings are valid:
415     *
416     * <ul>
417     *   <li>"2008-10-13T16:00:00.000Z"</li>
418     *   <li>"2008-10-13T16:00:00.000+07:00"</li>
419     *   <li>"2008-10-13T16:00:00.000-07:00"</li>
420     *   <li>"2008-10-13"</li>
421     * </ul>
422     *
423     * <p>
424     * If the string contains a time and time offset, then the time offset will
425     * be used to convert the time value to UTC.
426     * </p>
427     *
428     * <p>
429     * If the given string contains just a date (with no time field), then
430     * the {@link #allDay} field is set to true and the {@link #hour},
431     * {@link #minute}, and  {@link #second} fields are set to zero.
432     * </p>
433     *
434     * <p>
435     * Returns true if the resulting time value is in UTC time.
436     * </p>
437     *
438     * @param s the string to parse
439     * @return true if the resulting time value is in UTC time
440     */
441     public boolean parse3339(String s) {
442         if (nativeParse3339(s)) {
443             timezone = TIMEZONE_UTC;
444             return true;
445         }
446         return false;
447     }
448
449     native private boolean nativeParse3339(String s);
450
451    /**
452     * Returns the timezone string that is currently set for the device.
453     */
454    public static String getCurrentTimezone() {
455        return TimeZone.getDefault().getID();
456    }
457
458    /**
459     * Sets the time of the given Time object to the current time.
460     */
461    native public void setToNow();
462
463    /**
464     * Converts this time to milliseconds. Suitable for interacting with the
465     * standard java libraries. The time is in UTC milliseconds since the epoch.
466     * This does an implicit normalization to compute the milliseconds but does
467     * <em>not</em> change any of the fields in this Time object.  If you want
468     * to normalize the fields in this Time object and also get the milliseconds
469     * then use {@link #normalize(boolean)}.
470     *
471     * <p>
472     * If "ignoreDst" is false, then this method uses the current setting of the
473     * "isDst" field and will adjust the returned time if the "isDst" field is
474     * wrong for the given time.  See the sample code below for an example of
475     * this.
476     *
477     * <p>
478     * If "ignoreDst" is true, then this method ignores the current setting of
479     * the "isDst" field in this Time object and will instead figure out the
480     * correct value of "isDst" (as best it can) from the fields in this
481     * Time object.  The only case where this method cannot figure out the
482     * correct value of the "isDst" field is when the time is inherently
483     * ambiguous because it falls in the hour that is repeated when switching
484     * from Daylight-Saving Time to Standard Time.
485     *
486     * <p>
487     * Here is an example where <tt>toMillis(true)</tt> adjusts the time,
488     * assuming that DST changes at 2am on Sunday, Nov 4, 2007.
489     *
490     * <pre>
491     * Time time = new Time();
492     * time.set(2007, 10, 4);  // set the date to Nov 4, 2007, 12am
493     * time.normalize();       // this sets isDst = 1
494     * time.monthDay += 1;     // changes the date to Nov 5, 2007, 12am
495     * millis = time.toMillis(false);   // millis is Nov 4, 2007, 11pm
496     * millis = time.toMillis(true);    // millis is Nov 5, 2007, 12am
497     * </pre>
498     *
499     * <p>
500     * To avoid this problem, use <tt>toMillis(true)</tt>
501     * after adding or subtracting days or explicitly setting the "monthDay"
502     * field.  On the other hand, if you are adding
503     * or subtracting hours or minutes, then you should use
504     * <tt>toMillis(false)</tt>.
505     *
506     * <p>
507     * You should also use <tt>toMillis(false)</tt> if you want
508     * to read back the same milliseconds that you set with {@link #set(long)}
509     * or {@link #set(Time)} or after parsing a date string.
510     */
511    native public long toMillis(boolean ignoreDst);
512
513    /**
514     * Sets the fields in this Time object given the UTC milliseconds.  After
515     * this method returns, all the fields are normalized.
516     * This also sets the "isDst" field to the correct value.
517     *
518     * @param millis the time in UTC milliseconds since the epoch.
519     */
520    native public void set(long millis);
521
522    /**
523     * Format according to RFC 2445 DATETIME type.
524     *
525     * <p>
526     * The same as format("%Y%m%dT%H%M%S").
527     */
528    native public String format2445();
529
530    /**
531     * Copy the value of that to this Time object. No normalization happens.
532     */
533    public void set(Time that) {
534        this.timezone = that.timezone;
535        this.allDay = that.allDay;
536        this.second = that.second;
537        this.minute = that.minute;
538        this.hour = that.hour;
539        this.monthDay = that.monthDay;
540        this.month = that.month;
541        this.year = that.year;
542        this.weekDay = that.weekDay;
543        this.yearDay = that.yearDay;
544        this.isDst = that.isDst;
545        this.gmtoff = that.gmtoff;
546    }
547
548    /**
549     * Sets the fields. Sets weekDay, yearDay and gmtoff to 0, and isDst to -1.
550     * Call {@link #normalize(boolean)} if you need those.
551     */
552    public void set(int second, int minute, int hour, int monthDay, int month, int year) {
553        this.allDay = false;
554        this.second = second;
555        this.minute = minute;
556        this.hour = hour;
557        this.monthDay = monthDay;
558        this.month = month;
559        this.year = year;
560        this.weekDay = 0;
561        this.yearDay = 0;
562        this.isDst = -1;
563        this.gmtoff = 0;
564    }
565
566    /**
567     * Sets the date from the given fields.  Also sets allDay to true.
568     * Sets weekDay, yearDay and gmtoff to 0, and isDst to -1.
569     * Call {@link #normalize(boolean)} if you need those.
570     *
571     * @param monthDay the day of the month (in the range [1,31])
572     * @param month the zero-based month number (in the range [0,11])
573     * @param year the year
574     */
575    public void set(int monthDay, int month, int year) {
576        this.allDay = true;
577        this.second = 0;
578        this.minute = 0;
579        this.hour = 0;
580        this.monthDay = monthDay;
581        this.month = month;
582        this.year = year;
583        this.weekDay = 0;
584        this.yearDay = 0;
585        this.isDst = -1;
586        this.gmtoff = 0;
587    }
588
589    /**
590     * Returns true if the time represented by this Time object occurs before
591     * the given time.
592     *
593     * @param that a given Time object to compare against
594     * @return true if this time is less than the given time
595     */
596    public boolean before(Time that) {
597        return Time.compare(this, that) < 0;
598    }
599
600
601    /**
602     * Returns true if the time represented by this Time object occurs after
603     * the given time.
604     *
605     * @param that a given Time object to compare against
606     * @return true if this time is greater than the given time
607     */
608    public boolean after(Time that) {
609        return Time.compare(this, that) > 0;
610    }
611
612    /**
613     * This array is indexed by the weekDay field (SUNDAY=0, MONDAY=1, etc.)
614     * and gives a number that can be added to the yearDay to give the
615     * closest Thursday yearDay.
616     */
617    private static final int[] sThursdayOffset = { -3, 3, 2, 1, 0, -1, -2 };
618
619    /**
620     * Computes the week number according to ISO 8601.  The current Time
621     * object must already be normalized because this method uses the
622     * yearDay and weekDay fields.
623     *
624     * <p>
625     * In IS0 8601, weeks start on Monday.
626     * The first week of the year (week 1) is defined by ISO 8601 as the
627     * first week with four or more of its days in the starting year.
628     * Or equivalently, the week containing January 4.  Or equivalently,
629     * the week with the year's first Thursday in it.
630     * </p>
631     *
632     * <p>
633     * The week number can be calculated by counting Thursdays.  Week N
634     * contains the Nth Thursday of the year.
635     * </p>
636     *
637     * @return the ISO week number.
638     */
639    public int getWeekNumber() {
640        // Get the year day for the closest Thursday
641        int closestThursday = yearDay + sThursdayOffset[weekDay];
642
643        // Year days start at 0
644        if (closestThursday >= 0 && closestThursday <= 364) {
645            return closestThursday / 7 + 1;
646        }
647
648        // The week crosses a year boundary.
649        Time temp = new Time(this);
650        temp.monthDay += sThursdayOffset[weekDay];
651        temp.normalize(true /* ignore isDst */);
652        return temp.yearDay / 7 + 1;
653    }
654
655    /**
656     * Return a string in the RFC 3339 format.
657     * <p>
658     * If allDay is true, expresses the time as Y-M-D</p>
659     * <p>
660     * Otherwise, if the timezone is UTC, expresses the time as Y-M-D-T-H-M-S UTC</p>
661     * <p>
662     * Otherwise the time is expressed the time as Y-M-D-T-H-M-S +- GMT</p>
663     * @param allDay
664     * @return string in the RFC 3339 format.
665     */
666    public String format3339(boolean allDay) {
667        if (allDay) {
668            return format(Y_M_D);
669        } else if (TIMEZONE_UTC.equals(timezone)) {
670            return format(Y_M_D_T_H_M_S_000_Z);
671        } else {
672            String base = format(Y_M_D_T_H_M_S_000);
673            String sign = (gmtoff < 0) ? "-" : "+";
674            int offset = (int)Math.abs(gmtoff);
675            int minutes = (offset % 3600) / 60;
676            int hours = offset / 3600;
677
678            return String.format("%s%s%02d:%02d", base, sign, hours, minutes);
679        }
680    }
681
682    /**
683     * Returns true if the day of the given time is the epoch on the Julian Calendar
684     * (January 1, 1970 on the Gregorian calendar).
685     *
686     * @param time the time to test
687     * @return true if epoch.
688     */
689    public static boolean isEpoch(Time time) {
690        long millis = time.toMillis(true);
691        return getJulianDay(millis, 0) == EPOCH_JULIAN_DAY;
692    }
693
694    /**
695     * Computes the Julian day number, given the UTC milliseconds
696     * and the offset (in seconds) from UTC.  The Julian day for a given
697     * date will be the same for every timezone.  For example, the Julian
698     * day for July 1, 2008 is 2454649.  This is the same value no matter
699     * what timezone is being used.  The Julian day is useful for testing
700     * if two events occur on the same day and for determining the relative
701     * time of an event from the present ("yesterday", "3 days ago", etc.).
702     *
703     * <p>
704     * Use {@link #toMillis(boolean)} to get the milliseconds.
705     *
706     * @param millis the time in UTC milliseconds
707     * @param gmtoff the offset from UTC in seconds
708     * @return the Julian day
709     */
710    public static int getJulianDay(long millis, long gmtoff) {
711        long offsetMillis = gmtoff * 1000;
712        long julianDay = (millis + offsetMillis) / DateUtils.DAY_IN_MILLIS;
713        return (int) julianDay + EPOCH_JULIAN_DAY;
714    }
715
716    /**
717     * <p>Sets the time from the given Julian day number, which must be based on
718     * the same timezone that is set in this Time object.  The "gmtoff" field
719     * need not be initialized because the given Julian day may have a different
720     * GMT offset than whatever is currently stored in this Time object anyway.
721     * After this method returns all the fields will be normalized and the time
722     * will be set to 12am at the beginning of the given Julian day.
723     * </p>
724     *
725     * <p>
726     * The only exception to this is if 12am does not exist for that day because
727     * of daylight saving time.  For example, Cairo, Eqypt moves time ahead one
728     * hour at 12am on April 25, 2008 and there are a few other places that
729     * also change daylight saving time at 12am.  In those cases, the time
730     * will be set to 1am.
731     * </p>
732     *
733     * @param julianDay the Julian day in the timezone for this Time object
734     * @return the UTC milliseconds for the beginning of the Julian day
735     */
736    public long setJulianDay(int julianDay) {
737        // Don't bother with the GMT offset since we don't know the correct
738        // value for the given Julian day.  Just get close and then adjust
739        // the day.
740        long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS;
741        set(millis);
742
743        // Figure out how close we are to the requested Julian day.
744        // We can't be off by more than a day.
745        int approximateDay = getJulianDay(millis, gmtoff);
746        int diff = julianDay - approximateDay;
747        monthDay += diff;
748
749        // Set the time to 12am and re-normalize.
750        hour = 0;
751        minute = 0;
752        second = 0;
753        millis = normalize(true);
754        return millis;
755    }
756}
757