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