summaryrefslogtreecommitdiffstats
path: root/core/java/android/pim/Time.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/pim/Time.java')
-rw-r--r--core/java/android/pim/Time.java570
1 files changed, 570 insertions, 0 deletions
diff --git a/core/java/android/pim/Time.java b/core/java/android/pim/Time.java
new file mode 100644
index 0000000..59ba87b
--- /dev/null
+++ b/core/java/android/pim/Time.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.pim;
+
+
+
+import java.util.TimeZone;
+
+/**
+ * {@hide}
+ *
+ * The Time class is a faster replacement for the java.util.Calendar and
+ * java.util.GregorianCalendar classes. An instance of the Time class represents
+ * a moment in time, specified with second precision. It is modelled after
+ * struct tm, and in fact, uses struct tm to implement most of the
+ * functionality.
+ */
+public class Time {
+ public static final String TIMEZONE_UTC = "UTC";
+
+ /**
+ * The Julian day of the epoch, that is, January 1, 1970 on the Gregorian
+ * calendar.
+ */
+ public static final int EPOCH_JULIAN_DAY = 2440588;
+
+ /**
+ * True if this is an allDay event. The hour, minute, second fields are
+ * all zero, and the date is displayed the same in all time zones.
+ */
+ public boolean allDay;
+
+ /**
+ * Seconds [0-61] (2 leap seconds allowed)
+ */
+ public int second;
+
+ /**
+ * Minute [0-59]
+ */
+ public int minute;
+
+ /**
+ * Hour of day [0-23]
+ */
+ public int hour;
+
+ /**
+ * Day of month [1-31]
+ */
+ public int monthDay;
+
+ /**
+ * Month [0-11]
+ */
+ public int month;
+
+ /**
+ * Year. TBD. Is this years since 1900 like in struct tm?
+ */
+ public int year;
+
+ /**
+ * Day of week [0-6]
+ */
+ public int weekDay;
+
+ /**
+ * Day of year [0-365]
+ */
+ public int yearDay;
+
+ /**
+ * This time is in daylight savings time. One of:
+ * <ul>
+ * <li><b>positive</b> - in dst</li>
+ * <li><b>0</b> - not in dst</li>
+ * <li><b>negative</b> - unknown</li>
+ */
+ public int isDst;
+
+ /**
+ * Offset from UTC (in seconds).
+ */
+ public long gmtoff;
+
+ /**
+ * The timezone for this Time. Should not be null.
+ */
+ public String timezone;
+
+ /*
+ * Define symbolic constants for accessing the fields in this class. Used in
+ * getActualMaximum().
+ */
+ public static final int SECOND = 1;
+ public static final int MINUTE = 2;
+ public static final int HOUR = 3;
+ public static final int MONTH_DAY = 4;
+ public static final int MONTH = 5;
+ public static final int YEAR = 6;
+ public static final int WEEK_DAY = 7;
+ public static final int YEAR_DAY = 8;
+ public static final int WEEK_NUM = 9;
+
+ public static final int SUNDAY = 0;
+ public static final int MONDAY = 1;
+ public static final int TUESDAY = 2;
+ public static final int WEDNESDAY = 3;
+ public static final int THURSDAY = 4;
+ public static final int FRIDAY = 5;
+ public static final int SATURDAY = 6;
+
+ /**
+ * Construct a Time object in the timezone named by the string
+ * argument "timezone". The time is initialized to Jan 1, 1970.
+ */
+ public Time(String timezone) {
+ if (timezone == null) {
+ throw new NullPointerException("timezone is null!");
+ }
+ this.timezone = timezone;
+ this.year = 1970;
+ this.monthDay = 1;
+ // Set the daylight-saving indicator to the unknown value -1 so that
+ // it will be recomputed.
+ this.isDst = -1;
+ }
+
+ /**
+ * Construct a Time object in the local timezone. The time is initialized to
+ * Jan 1, 1970.
+ */
+ public Time() {
+ this(TimeZone.getDefault().getID());
+ }
+
+ /**
+ * A copy constructor. Construct a Time object by copying the given
+ * Time object. No normalization occurs.
+ *
+ * @param other
+ */
+ public Time(Time other) {
+ set(other);
+ }
+
+ /**
+ * Ensures the values in each field are in range. For example if the
+ * current value of this calendar is March 32, normalize() will convert it
+ * to April 1. It also fills in weekDay, yearDay, isDst and gmtoff.
+ *
+ * <p>
+ * If "ignoreDst" is true, then this method sets the "isDst" field to -1
+ * (the "unknown" value) before normalizing. It then computes the
+ * correct value for "isDst".
+ *
+ * <p>
+ * See {@link #toMillis(boolean)} for more information about when to
+ * use <tt>true</tt> or <tt>false</tt> for "ignoreDst".
+ *
+ * @return the UTC milliseconds since the epoch
+ */
+ native public long normalize(boolean ignoreDst);
+
+ /**
+ * Convert this time object so the time represented remains the same, but is
+ * instead located in a different timezone. This method automatically calls
+ * normalize() in some cases
+ */
+ native public void switchTimezone(String timezone);
+
+ private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31,
+ 31, 30, 31, 30, 31 };
+
+ /**
+ * Return the maximum possible value for the given field given the value of
+ * the other fields. Requires that it be normalized for MONTH_DAY and
+ * YEAR_DAY.
+ */
+ public int getActualMaximum(int field) {
+ switch (field) {
+ case SECOND:
+ return 59; // leap seconds, bah humbug
+ case MINUTE:
+ return 59;
+ case HOUR:
+ return 23;
+ case MONTH_DAY: {
+ int n = DAYS_PER_MONTH[this.month];
+ if (n != 28) {
+ return n;
+ } else {
+ int y = this.year;
+ return ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 29 : 28;
+ }
+ }
+ case MONTH:
+ return 11;
+ case YEAR:
+ return 2037;
+ case WEEK_DAY:
+ return 6;
+ case YEAR_DAY: {
+ int y = this.year;
+ // Year days are numbered from 0, so the last one is usually 364.
+ return ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 365 : 364;
+ }
+ case WEEK_NUM:
+ throw new RuntimeException("WEEK_NUM not implemented");
+ default:
+ throw new RuntimeException("bad field=" + field);
+ }
+ }
+
+ /**
+ * Clears all values, setting the timezone to the given timezone. Sets isDst
+ * to a negative value to mean "unknown".
+ */
+ public void clear(String timezone) {
+ if (timezone == null) {
+ throw new NullPointerException("timezone is null!");
+ }
+ this.timezone = timezone;
+ this.allDay = false;
+ this.second = 0;
+ this.minute = 0;
+ this.hour = 0;
+ this.monthDay = 0;
+ this.month = 0;
+ this.year = 0;
+ this.weekDay = 0;
+ this.yearDay = 0;
+ this.gmtoff = 0;
+ this.isDst = -1;
+ }
+
+ /**
+ * return a negative number if a is less than b, a positive number if a is
+ * greater than b, and 0 if they are equal.
+ */
+ native public static int compare(Time a, Time b);
+
+ /**
+ * Print the current value given the format string provided. See man
+ * strftime for what means what. The final string must be less than 256
+ * characters.
+ */
+ native public String format(String format);
+
+ /**
+ * Return the current time in YYYYMMDDTHHMMSS<tz> format
+ */
+ @Override
+ native public String toString();
+
+ /**
+ * Parse a time in the current zone in YYYYMMDDTHHMMSS format.
+ */
+ native public void parse(String s);
+
+ /**
+ * Parse a time in RFC 2445 format. Returns whether or not the time is in
+ * UTC (ends with Z).
+ *
+ * @param s the string to parse
+ * @return true if the resulting time value is in UTC time
+ */
+ public boolean parse2445(String s) {
+ if (nativeParse2445(s)) {
+ timezone = TIMEZONE_UTC;
+ return true;
+ }
+ return false;
+ }
+
+ native private boolean nativeParse2445(String s);
+
+ /**
+ * Parse a time in RFC 3339 format. This method also parses simple dates
+ * (that is, strings that contain no time or time offset). If the string
+ * contains a time and time offset, then the time offset will be used to
+ * convert the time value to UTC.
+ * Returns true if the resulting time value is in UTC time.
+ *
+ * @param s the string to parse
+ * @return true if the resulting time value is in UTC time
+ */
+ public boolean parse3339(String s) {
+ if (nativeParse3339(s)) {
+ timezone = TIMEZONE_UTC;
+ return true;
+ }
+ return false;
+ }
+
+ native private boolean nativeParse3339(String s);
+
+ /**
+ * Returns the timezone string that is currently set for the device.
+ */
+ public static String getCurrentTimezone() {
+ return TimeZone.getDefault().getID();
+ }
+
+ /**
+ * Sets the time of the given Time object to the current time.
+ */
+ native public void setToNow();
+
+ /**
+ * Converts this time to milliseconds. Suitable for interacting with the
+ * standard java libraries. The time is in UTC milliseconds since the epoch.
+ * This does an implicit normalization to compute the milliseconds but does
+ * <em>not</em> change any of the fields in this Time object. If you want
+ * to normalize the fields in this Time object and also get the milliseconds
+ * then use {@link #normalize(boolean)}.
+ *
+ * <p>
+ * If "ignoreDst" is false, then this method uses the current setting of the
+ * "isDst" field and will adjust the returned time if the "isDst" field is
+ * wrong for the given time. See the sample code below for an example of
+ * this.
+ *
+ * <p>
+ * If "ignoreDst" is true, then this method ignores the current setting of
+ * the "isDst" field in this Time object and will instead figure out the
+ * correct value of "isDst" (as best it can) from the fields in this
+ * Time object. The only case where this method cannot figure out the
+ * correct value of the "isDst" field is when the time is inherently
+ * ambiguous because it falls in the hour that is repeated when switching
+ * from Daylight-Saving Time to Standard Time.
+ *
+ * <p>
+ * Here is an example where <tt>toMillis(true)</tt> adjusts the time,
+ * assuming that DST changes at 2am on Sunday, Nov 4, 2007.
+ *
+ * <pre>
+ * Time time = new Time();
+ * time.set(2007, 10, 4); // set the date to Nov 4, 2007, 12am
+ * time.normalize(); // this sets isDst = 1
+ * time.monthDay += 1; // changes the date to Nov 5, 2007, 12am
+ * millis = time.toMillis(false); // millis is Nov 4, 2007, 11pm
+ * millis = time.toMillis(true); // millis is Nov 5, 2007, 12am
+ * </pre>
+ *
+ * <p>
+ * To avoid this problem, use <tt>toMillis(true)</tt>
+ * after adding or subtracting days or explicitly setting the "monthDay"
+ * field. On the other hand, if you are adding
+ * or subtracting hours or minutes, then you should use
+ * <tt>toMillis(false)</tt>.
+ *
+ * <p>
+ * You should also use <tt>toMillis(false)</tt> if you want
+ * to read back the same milliseconds that you set with {@link #set(long)}
+ * or {@link #set(Time)} or after parsing a date string.
+ */
+ native public long toMillis(boolean ignoreDst);
+
+ /**
+ * Sets the fields in this Time object given the UTC milliseconds. After
+ * this method returns, all the fields are normalized.
+ * This also sets the "isDst" field to the correct value.
+ *
+ * @param millis the time in UTC milliseconds since the epoch.
+ */
+ native public void set(long millis);
+
+ /**
+ * Format according to RFC 2445 DATETIME type.
+ *
+ * <p>
+ * The same as format("%Y%m%dT%H%M%S").
+ */
+ native public String format2445();
+
+ /**
+ * Copy the value of that to this Time object. No normalization happens.
+ */
+ public void set(Time that) {
+ this.timezone = that.timezone;
+ this.allDay = that.allDay;
+ this.second = that.second;
+ this.minute = that.minute;
+ this.hour = that.hour;
+ this.monthDay = that.monthDay;
+ this.month = that.month;
+ this.year = that.year;
+ this.weekDay = that.weekDay;
+ this.yearDay = that.yearDay;
+ this.isDst = that.isDst;
+ this.gmtoff = that.gmtoff;
+ }
+
+ /**
+ * Set the fields. Sets weekDay, yearDay and gmtoff to 0. Call
+ * normalize() if you need those.
+ */
+ public void set(int second, int minute, int hour, int monthDay, int month, int year) {
+ this.allDay = false;
+ this.second = second;
+ this.minute = minute;
+ this.hour = hour;
+ this.monthDay = monthDay;
+ this.month = month;
+ this.year = year;
+ this.weekDay = 0;
+ this.yearDay = 0;
+ this.isDst = -1;
+ this.gmtoff = 0;
+ }
+
+ public void set(int monthDay, int month, int year) {
+ this.allDay = true;
+ this.second = 0;
+ this.minute = 0;
+ this.hour = 0;
+ this.monthDay = monthDay;
+ this.month = month;
+ this.year = year;
+ this.weekDay = 0;
+ this.yearDay = 0;
+ this.isDst = -1;
+ this.gmtoff = 0;
+ }
+
+ public boolean before(Time that) {
+ return Time.compare(this, that) < 0;
+ }
+
+ public boolean after(Time that) {
+ return Time.compare(this, that) > 0;
+ }
+
+ /**
+ * This array is indexed by the weekDay field (SUNDAY=0, MONDAY=1, etc.)
+ * and gives a number that can be added to the yearDay to give the
+ * closest Thursday yearDay.
+ */
+ private static final int[] sThursdayOffset = { -3, 3, 2, 1, 0, -1, -2 };
+
+ /**
+ * Computes the week number according to ISO 8601. The current Time
+ * object must already be normalized because this method uses the
+ * yearDay and weekDay fields.
+ *
+ * In IS0 8601, weeks start on Monday.
+ * The first week of the year (week 1) is defined by ISO 8601 as the
+ * first week with four or more of its days in the starting year.
+ * Or equivalently, the week containing January 4. Or equivalently,
+ * the week with the year's first Thursday in it.
+ *
+ * The week number can be calculated by counting Thursdays. Week N
+ * contains the Nth Thursday of the year.
+ *
+ * @return the ISO week number.
+ */
+ public int getWeekNumber() {
+ // Get the year day for the closest Thursday
+ int closestThursday = yearDay + sThursdayOffset[weekDay];
+
+ // Year days start at 0
+ if (closestThursday >= 0 && closestThursday <= 364) {
+ return closestThursday / 7 + 1;
+ }
+
+ // The week crosses a year boundary.
+ Time temp = new Time(this);
+ temp.monthDay += sThursdayOffset[weekDay];
+ temp.normalize(true /* ignore isDst */);
+ return temp.yearDay / 7 + 1;
+ }
+
+ public String format3339(boolean allDay) {
+ if (allDay) {
+ return format("%Y-%m-%d");
+ } else if (TIMEZONE_UTC.equals(timezone)) {
+ return format("%Y-%m-%dT%H:%M:%S.000Z");
+ } else {
+ String base = format("%Y-%m-%dT%H:%M:%S.000");
+ String sign = (gmtoff < 0) ? "-" : "+";
+ int offset = (int)Math.abs(gmtoff);
+ int minutes = (offset % 3600) / 60;
+ int hours = offset / 3600;
+
+ return String.format("%s%s%02d:%02d", base, sign, hours, minutes);
+ }
+ }
+
+ public static boolean isEpoch(Time time) {
+ long millis = time.toMillis(true);
+ return getJulianDay(millis, 0) == EPOCH_JULIAN_DAY;
+ }
+
+ /**
+ * Computes the Julian day number, given the UTC milliseconds
+ * and the offset (in seconds) from UTC. The Julian day for a given
+ * date will be the same for every timezone. For example, the Julian
+ * day for July 1, 2008 is 2454649. This is the same value no matter
+ * what timezone is being used. The Julian day is useful for testing
+ * if two events occur on the same day and for determining the relative
+ * time of an event from the present ("yesterday", "3 days ago", etc.).
+ *
+ * <p>
+ * Use {@link #toMillis(boolean)} to get the milliseconds.
+ *
+ * @param millis the time in UTC milliseconds
+ * @param gmtoff the offset from UTC in seconds
+ * @return the Julian day
+ */
+ public static int getJulianDay(long millis, long gmtoff) {
+ long offsetMillis = gmtoff * 1000;
+ long julianDay = (millis + offsetMillis) / DateUtils.DAY_IN_MILLIS;
+ return (int) julianDay + EPOCH_JULIAN_DAY;
+ }
+
+ /**
+ * <p>Sets the time from the given Julian day number, which must be based on
+ * the same timezone that is set in this Time object. The "gmtoff" field
+ * need not be initialized because the given Julian day may have a different
+ * GMT offset than whatever is currently stored in this Time object anyway.
+ * After this method returns all the fields will be normalized and the time
+ * will be set to 12am at the beginning of the given Julian day.
+ *
+ * <p>
+ * The only exception to this is if 12am does not exist for that day because
+ * of daylight saving time. For example, Cairo, Eqypt moves time ahead one
+ * hour at 12am on April 25, 2008 and there are a few other places that
+ * also change daylight saving time at 12am. In those cases, the time
+ * will be set to 1am.
+ *
+ * @param julianDay the Julian day in the timezone for this Time object
+ * @return the UTC milliseconds for the beginning of the Julian day
+ */
+ public long setJulianDay(int julianDay) {
+ // Don't bother with the GMT offset since we don't know the correct
+ // value for the given Julian day. Just get close and then adjust
+ // the day.
+ long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS;
+ set(millis);
+
+ // Figure out how close we are to the requested Julian day.
+ // We can't be off by more than a day.
+ int approximateDay = getJulianDay(millis, gmtoff);
+ int diff = julianDay - approximateDay;
+ monthDay += diff;
+
+ // Set the time to 12am and re-normalize.
+ hour = 0;
+ minute = 0;
+ second = 0;
+ millis = normalize(true);
+ return millis;
+ }
+}