/*
* Copyright (C) 2007 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.
*/
import java.util.Calendar;
/**
* Encapsulates a truck-load of commonly used date functions.
*
* @author barclay
*/
public final class DateUtil {
private Calendar mCal;
private DateUtil.Period mSpan;
private int mSpanOffset = 0;
private boolean mUnitChange = false;
/** Number of milliseconds in a second */
public static final long SECOND_MS = 1000;
/** Number of milliseconds in a minute */
public static final long MINUTE_MS = SECOND_MS * 60;
/** Number of milliseconds in an hour */
public static final long HOUR_MS = MINUTE_MS * 60;
/** Number of milliseconds in a morning or evening (1/2 day) */
public static final long AMPM_MS = HOUR_MS * 12;
/** Number of milliseconds in a day */
public static final long DAY_MS = HOUR_MS * 24;
/** Number of milliseconds in a week */
public static final long WEEK_MS = DAY_MS * 7;
/** Number of milliseconds in a year */
public static final long YEAR_MS = WEEK_MS * 52;
/** Number of milliseconds in a quarter (as defined by 1/4 of a year) */
public static final long QUARTER_MS = WEEK_MS * 13;
/** Number of milliseconds in a month (as defined by 1/12 of a year) */
public static final long MONTH_MS = YEAR_MS / 12;
/**
* Encapsulation of a date broken down by both milliseconds since epoch (as
* defined by the system), and year, month, day, hour, minute, and second. The
* reason for storing both is essentially to cache information and glue the
* variable together into a single item. Purely convenience.
*
* @author barclay
*/
public static class DateItem {
public int mYear = 0;
public int mMonth = 0;
public int mDay = 0;
public int mHour = 0;
public int mMinute = 0;
public int mSecond = 0;
public long mMillis = 0;
/**
* Set all the fields of the DateItem to the date/time represented by the
* current value of the Calendar passed in.
*
* @param c
* The Calendar that's the source data.
*/
public void setTo(Calendar c) {
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
mSecond = c.get(Calendar.SECOND);
mMillis = c.getTimeInMillis();
}
/**
* Compares all the fields of the DateItem to another DateItem. All fields
* are compared, instead of just the millisecond field, in the event that
* all the fields are not in sync for some reason.
*
* @param other
* @return true if the two DateItems are equal in all fields, else false.
*/
public boolean isEqual(DateItem other) {
if (this.mYear == other.mYear && this.mMonth == other.mMonth
&& this.mDay == other.mDay && this.mHour == other.mHour
&& this.mMinute == other.mMinute && this.mSecond == other.mSecond
&& this.mMillis == other.mMillis)
return true;
return false;
}
}
private DateItem mBase;
private DateItem mCursor;
/**
* Code shoulder reference these Periods when refering to spans of time
* instead of Calendar.*, as Calendar doesn't support the notion of a strict
* QUARTER, and we use WEEK slightly differently.
*
* @author barclay
*/
public enum Period {
MINUTE, HOUR, AMPM, DAY, WEEK, MONTH, QUARTER, YEAR
}
public Period[] PERIODS = { Period.MINUTE, Period.HOUR, Period.AMPM,
Period.DAY, Period.WEEK, Period.MONTH, Period.QUARTER, Period.YEAR };
public static final String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May",
"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
public static final String[] DAYS = { "Sun", "M", "Tu", "W", "Th", "F",
"Sat", };
public static final String[] QUARTERS = { "Q1", "Q2", "Q3", "Q4", };
/**
* Constructor. Instantiates a Calendar member variable to prevent having to
* continually fetch an instance, which is moderately expensive, and create
* cursor used to walk a timeline.
*/
public DateUtil() {
mCal = Calendar.getInstance();
mBase = new DateItem();
mCursor = new DateItem();
}
/**
* Returns the internal Calendar object in it's current state.
*
* @return The Calendar.
*/
public Calendar getCalendar() {
return mCal;
}
/**
* Used when going to stride along a timeline. This sets the start time of the
* walk.
*
* @param ms
* The start time in milliseconds since epoch.
*/
public void setBaseTime(long ms) {
mBase.mMillis = ms;
millisToComponent(mBase);
copyDate(mBase, mCursor);
}
/**
* Returns the milliseconds until the next Period, as based on the difference
* between the current cursor and the Period. If the current cursor is at the
* start of a Period, ignoring milliseconds, 0 is returned.
*
* @param u
* @return milliseconds until next period.
*/
public long msToNextPeriod(Period u) {
long ms = 0;
switch (u) {
case YEAR:
mCal.set(mCursor.mYear + 1, 0, 0, 0, 0, 0);
ms = mCal.getTimeInMillis() - mCursor.mMillis;
if (ms == YEAR_MS)
ms = 0;
break;
case QUARTER:
if (mCursor.mMonth >= 9)
mCal.set(mCursor.mYear + 1, 0, 0, 0, 0, 0);
else if (mCursor.mMonth >= 6)
mCal.set(mCursor.mYear, 9, 0, 0, 0, 0);
else if (mCursor.mMonth >= 3)
mCal.set(mCursor.mYear, 6, 0, 0, 0, 0);
else
mCal.set(mCursor.mYear, 3, 0, 0, 0, 0);
ms = mCal.getTimeInMillis() - mCursor.mMillis;
if (ms == QUARTER_MS)
ms = 0;
break;
case MONTH:
if (mCursor.mMonth == 11)
mCal.set(mCursor.mYear + 1, 0, 0, 0, 0, 0);
else
mCal.set(mCursor.mYear, mCursor.mMonth + 1, 0, 0, 0, 0);
ms = mCal.getTimeInMillis() - mCursor.mMillis;
if (ms == MONTH_MS)
ms = 0;
break;
case WEEK:
mCal.setTimeInMillis(mCursor.mMillis);
int first = mCal.getFirstDayOfWeek();
mCal.add(Calendar.WEEK_OF_YEAR, 1);
mCal.set(Calendar.DAY_OF_WEEK, first);
mCal.set(Calendar.HOUR_OF_DAY, 0);
mCal.set(Calendar.MINUTE, 0);
mCal.set(Calendar.SECOND, 0);
mCal.set(Calendar.MILLISECOND, 0);
ms = mCal.getTimeInMillis() - mCursor.mMillis;
if (ms == WEEK_MS)
ms = 0;
break;
case DAY:
if (mCursor.mMinute == 0 && mCursor.mHour == 0)
return 0;
ms = ((60 - mCursor.mMinute) + (60 * (24 - mCursor.mHour))) * MINUTE_MS;
break;
case AMPM:
if (mCursor.mMinute == 0 && (mCursor.mHour == 0 || mCursor.mHour == 12))
return 0;
ms = ((60 - mCursor.mMinute) + (60 * (24 - (mCursor.mHour % 12))))
* MINUTE_MS;
break;
case HOUR:
if (mCursor.mMinute == 0)
return 0;
ms = (60 - mCursor.mMinute) * MINUTE_MS;
break;
case MINUTE:
default:
if (mCursor.mSecond == 0)
return 0;
ms = (60 - mCursor.mSecond) * SECOND_MS;
break;
}
return ms;
}
/**
* Sets an offset of the internal marker recording the class of time spanned
* (as a Period). This offset is indexes into the Period enum, e.g., if the
* span is calculated to be Period.YEAR with an offset of 0, then it will be
* calculated to be Period.MONTH with an offset of -2.
*
* @param milliStart
* The milliseconds since epoch in start time, inclusive.
* @param milliEnd
* The milliseconds since epoch in end time, inclusive.
*/
public void setSpanOffset(int offset) {
mSpanOffset = offset;
}
/**
* Sets the internal marker recording the class of time spanned (as a Period)
* for the range of time specified. This is used to determine how to generate
* labels while striding through time. If milliStart == milliEnd, the span
* will be set to the smallest known span.
*
* @param milliStart
* The milliseconds since epoch in start time, inclusive.
* @param milliEnd
* The milliseconds since epoch in end time, inclusive.
*/
public void setSpan(long milliStart, long milliEnd) {
int index = 0;
long range = milliEnd - milliStart;
if (range == 0)
range = 1;
if (range < 0)
range = -range;
if (range > (long) (DateUtil.YEAR_MS * 3)) {
index = DateUtil.Period.YEAR.ordinal();
} else if (range > (long) (DateUtil.QUARTER_MS * 6)) {
index = DateUtil.Period.QUARTER.ordinal();
} else if (range > (long) (DateUtil.MONTH_MS * 6)) {
index = DateUtil.Period.MONTH.ordinal();
} else if (range > (long) (DateUtil.WEEK_MS * 4)) {
index = DateUtil.Period.WEEK.ordinal();
} else if (range > (long) (DateUtil.DAY_MS * 5)) {
index = DateUtil.Period.DAY.ordinal();
} else if (range > (long) (DateUtil.HOUR_MS * 24)) {
index = DateUtil.Period.AMPM.ordinal();
} else if (range > (long) (DateUtil.HOUR_MS * 5)) {
index = DateUtil.Period.HOUR.ordinal();
} else {
index = DateUtil.Period.MINUTE.ordinal();
}
index += mSpanOffset;
if (index < 0)
index = 0;
else if (index >= PERIODS.length)
index = PERIODS.length - 1;
mSpan = PERIODS[index];
return;
}
/**
* Returns the span calculated by {@link #setSpan(long, long)}
*
* @return The span as a DateUtil.Period
* @see DateUtil#Period
*/
public DateUtil.Period getSpan() {
return mSpan;
}
/**
* Returns the selected Calendar.* field of the time under the current cursor
* when striding.
*
* @param p
* The Period in which to format the output.
* @return The field datum.
*/
public int get(int field) {
return mCal.get(field);
}
/**
* Returns an array of two strings yielding a textual representation of the
* time under the current cursor when striding. Neither string will be null,
* but either may be the empty ("") string. Typically, the second string will
* be empty rather than the first, and will contain additional information
* about the label, such as the the month when the days roll over into the
* next month, or the day of the week. This method sets an internal marker
* recording if current label has rolled past a period boundary, such as from
* one week to the next or one year to the next, which is queryable via
* {@link #isUnitChanged()}
*
* @param p
* The Period in which to format the output.
* @return String[2], containing two description strings of the date/time. The
* first string will be withing the Period <code>p</code>, and the
* second is typically auxiliary information.
*/
public String[] getLabel(Period p) {
String[] strings = new String[2];
int minute;
int hour;
int day;
int month;
int year;
int dow;
mUnitChange = false;
switch (p) {
case YEAR:
strings[0] = "" + mCal.get(Calendar.YEAR);
strings[1] = "";
break;
case QUARTER:
year = mCal.get(Calendar.YEAR);
month = mCal.get(Calendar.MONTH);
if (month >= 9)
strings[0] = QUARTERS[3];
else if (month >= 6)
strings[0] = QUARTERS[2];
else if (month >= 3)
strings[0] = QUARTERS[1];
else
strings[0] = QUARTERS[0];
strings[1] = "";
if (year != mBase.mYear) {
strings[1] = "" + mCal.get(Calendar.YEAR);
mUnitChange = true;
}
break;
case MONTH:
year = mCal.get(Calendar.YEAR);
month = mCal.get(Calendar.MONTH);
strings[0] = MONTHS[month];
if (year != mBase.mYear) {
strings[1] = "" + mCal.get(Calendar.YEAR);
mUnitChange = true;
} else {
strings[1] = "";
}
break;
case WEEK:
case DAY:
month = mCal.get(Calendar.MONTH);
day = mCal.get(Calendar.DAY_OF_MONTH);
strings[0] = "" + day;
if (month != mBase.mMonth) {
strings[1] = MONTHS[month];
mUnitChange = true;
} else {
dow = mCal.get(Calendar.DAY_OF_WEEK);
strings[1] = DAYS[dow - 1];
if (dow == 1)
mUnitChange = true;
}
break;
case AMPM:
case HOUR:
day = mCal.get(Calendar.DAY_OF_MONTH);
hour = mCal.get(Calendar.HOUR_OF_DAY);
if (hour == 0) {
strings[0] = "12a";
strings[1] = "midnight";
} else if (hour == 12) {
strings[0] = "12p";
strings[1] = "noon";
} else if (hour > 11) {
strings[0] = (hour - 12) + "p";
strings[1] = "";
} else {
strings[0] = hour + "a";
strings[1] = "";
}
if (day != mBase.mDay) {
dow = mCal.get(Calendar.DAY_OF_WEEK);
strings[0] = mCal.get(Calendar.MONTH) + 1 + "/" + day;
strings[1] = DAYS[dow - 1];
mUnitChange = true;
}
break;
case MINUTE:
default:
minute = mCal.get(Calendar.MINUTE);
hour = mCal.get(Calendar.HOUR_OF_DAY);
strings[0] = l2pad(minute);
strings[1] = "";
if (hour != mBase.mHour) {
if (hour == 0) {
day = mCal.get(Calendar.DAY_OF_MONTH);
dow = mCal.get(Calendar.DAY_OF_WEEK);
strings[0] = mCal.get(Calendar.MONTH) + 1 + "/" + day;
strings[1] = DAYS[dow - 1];
} else if (hour == 12) {
strings[0] = "12";
strings[1] = "noon";
} else if (hour > 11) {
strings[0] = (hour - 12) + "p";
} else {
strings[0] = hour + "a";
}
mUnitChange = true;
} else
break;
}
return strings;
}
/**
* Advances the internal cursor <code>milliseconds</code> in time.
*
* @param milliseconds
* The number of milliseconds to advance.
*/
public void advanceInMs(long milliseconds) {
copyDate(mCursor, mBase);
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.MILLISECOND, (int) milliseconds);
mCursor.mMillis = mCal.getTimeInMillis();
}
/**
* Advances the internal cursor <code>step</code> units of Period
* <code>p</code> in time. Note that for MONTH and QUARTER, this works out to
* 1 and 3 months respectively, as defined by the Calendar class and based on
* the current cursor, not precisely MONTH_MS or QUARTER_MS milliseconds.
*
* @param p
* The DateUtil.Period unit.
* @param step
* The number of Period units to advance.
*/
public void advance(Period p, int step) {
copyDate(mCursor, mBase);
switch (p) {
case YEAR:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.YEAR, step);
break;
case QUARTER:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.MONTH, step * 3);
break;
case MONTH:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.MONTH, step);
break;
case WEEK:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.WEEK_OF_YEAR, step);
break;
case DAY:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.DAY_OF_MONTH, step);
break;
case HOUR:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.HOUR_OF_DAY, step);
break;
case MINUTE:
default:
mCal.setTimeInMillis(mCursor.mMillis);
mCal.add(Calendar.MINUTE, step);
break;
}
mCursor.mMillis = mCal.getTimeInMillis();
millisToComponent(mCursor);
return;
}
/**
* Return whether or not the last getLabel() noted a rollover from one period
* to another, as determine by the Period passed to getLabel().
*
* @return boolean
* @see #getLabel(Period)
*/
public boolean isUnitChanged() {
return mUnitChange;
}
/**
* Returns the average number of milliseconds in a Period. These are constant.
*
* @param u
* @return the number of millseconds
* @see #YEAR_MS
* @see #QUARTER_MS
* @see #MONTH_MS
* @see #DAY_MS
* @see #HOUR_MS
* @see #MINUTE_MS
*/
public long msInPeriod(Period u) {
long ms = 0;
switch (u) {
case YEAR:
ms = YEAR_MS;
break;
case QUARTER:
ms = QUARTER_MS;
break;
case MONTH:
ms = MONTH_MS;
break;
case WEEK:
ms = WEEK_MS;
break;
case DAY:
ms = DAY_MS;
break;
case AMPM:
ms = AMPM_MS;
break;
case HOUR:
ms = HOUR_MS;
break;
case MINUTE:
default:
ms = MINUTE_MS;
break;
}
return ms;
}
/**
* Some external entities still use Calendar.* fields to do some of their own
* date calculations, so this provides a mapping from DateUtil.*_MS to the
* closest Calendar.* field. Note that if the milliseconds is not one of the
* DateUtil constants, the smallest known field will be returned.
*
* @param millis
* The DateUtil.*_MS field to map from.
* @return The int representing the closest Calendar.* field.
*/
public static int mapLongToCal(long millis) {
if (millis == YEAR_MS)
return Calendar.YEAR;
else if (millis == QUARTER_MS)
return Calendar.MONTH; // There is no Calendar.QUARTER, return MONTH
else if (millis == MONTH_MS)
return Calendar.MONTH;
else if (millis == WEEK_MS)
return Calendar.WEEK_OF_YEAR;
else if (millis == DAY_MS)
return Calendar.DAY_OF_MONTH;
else if (millis == AMPM_MS)
return Calendar.AM_PM;
else if (millis == HOUR_MS)
return Calendar.HOUR_OF_DAY;
return Calendar.MINUTE;
}
/**
* Provide a mapping from number of millisecond (DateUtil.*_MS) to a
* DateUtil.Period. Note that if the milliseconds is not one of the DateUtil
* constants, the smallest known field will be returned.
*
* @param millis
* The DateUtil.*_MS field to map from.
* @return The Period enum representing the associated DateUtil.Period.
*/
public static Period mapLongToPeriod(long millis) {
if (millis == YEAR_MS)
return Period.YEAR;
else if (millis == QUARTER_MS)
return Period.QUARTER;
else if (millis == MONTH_MS)
return Period.MONTH;
else if (millis == WEEK_MS)
return Period.WEEK;
else if (millis == DAY_MS)
return Period.DAY;
else if (millis == AMPM_MS)
return Period.AMPM;
else if (millis == HOUR_MS)
return Period.HOUR;
return Period.MINUTE;
}
/**
* Provide a mapping from a Period to the number of millisecond
* (DateUtil.*_MS)
*
* @param The
* Period enum representing the associated DateUtil.Period.
* @return A String describing the period..
*/
public static String mapPeriodToString(Period p) {
if (p == Period.YEAR)
return "year";
if (p == Period.QUARTER)
return "quarter";
if (p == Period.MONTH)
return "month";
if (p == Period.WEEK)
return "week";
if (p == Period.DAY)
return "day";
if (p == Period.AMPM)
return "am/pm";
if (p == Period.HOUR)
return "hour";
return "minute";
}
/**
* Provide a mapping from string to a Period.
*
* @param s
* The string to map from. Case insensitive.
* @return The associated DateUtil.Period
*/
public static Period mapStringToPeriod(String s) {
if (s.toLowerCase().equals("year"))
return Period.YEAR;
if (s.toLowerCase().equals("quarter"))
return Period.QUARTER;
if (s.toLowerCase().equals("month"))
return Period.MONTH;
if (s.toLowerCase().equals("week"))
return Period.WEEK;
if (s.toLowerCase().equals("day"))
return Period.DAY;
if (s.toLowerCase().equals("am/pm"))
return Period.AMPM;
if (s.toLowerCase().equals("hour"))
return Period.HOUR;
return Period.MINUTE;
}
/**
* Provide a mapping from a Period to the number of millisecond
* (DateUtil.*_MS)
*
* @param millis
* The DateUtil.*_MS field to map from.
* @param The
* Period enum representing the associated DateUtil.Period.
* @return the DateUtil.*_MS constant representing the number of milliseconds
* in the period.
*/
public static long mapPeriodToLong(Period p) {
if (p == Period.YEAR)
return YEAR_MS;
if (p == Period.QUARTER)
return QUARTER_MS;
if (p == Period.MONTH)
return MONTH_MS;
if (p == Period.WEEK)
return WEEK_MS;
if (p == Period.DAY)
return DAY_MS;
if (p == Period.AMPM)
return AMPM_MS;
if (p == Period.HOUR)
return HOUR_MS;
return MINUTE_MS;
}
/**
* Returns a description of the milliseconds, scaled to the largest unit and
* rounded to the default number of decimal places, with the associated label
* (e.g., "years", "weeks", etc.)
*
* @param millis
* The milliseconds since epoch to format.
* @return The descriptive string.
*/
public static String toString(float millis) {
if (millis > YEAR_MS) {
return Round(millis / YEAR_MS) + " years";
} else if (millis > QUARTER_MS) {
return Round(millis / QUARTER_MS) + " quarters";
} else if (millis > MONTH_MS) {
return Round(millis / MONTH_MS) + " months";
} else if (millis > WEEK_MS) {
return Round(millis / WEEK_MS) + " weeks";
} else if (millis > DAY_MS) {
return Round(millis / DAY_MS) + " days";
} else if (millis > HOUR_MS) {
return Round(millis / HOUR_MS) + " hours";
} else { // if (millis > MINUTE_MS) {
return Round(millis / MINUTE_MS) + " minutes";
}
}
/**
* Returns a description of the square root of the milliseconds, scaled to the
* largest unit and rounded to the default number of decimal places, with the
* associated label (e.g., "years", "weeks", etc.). Note this is only used for
* displaying the variance, as the variance the a squared value, so this tests
* (millis > (unit^2)) ? and displays the value (millis/(unit^2)). Otherwise
* it is identical to {@link #toString(float)}.
*
* @param millis
* The (squared) milliseconds since epoch to format.
* @return The descriptive string.
*/
public static String toStringSquared(float millis) {
if (millis > (float) YEAR_MS * (float) YEAR_MS) {
return Round(millis / ((float) YEAR_MS * (float) YEAR_MS))
+ " years";
} else if (millis > (float) QUARTER_MS * (float) QUARTER_MS) {
return Round(millis / ((float) QUARTER_MS * (float) QUARTER_MS))
+ " quarters";
} else if (millis > (float) MONTH_MS * (float) MONTH_MS) {
return Round(millis / ((float) MONTH_MS * (float) MONTH_MS))
+ " months";
} else if (millis > (float) WEEK_MS * (float) WEEK_MS) {
return Round(millis / ((float) WEEK_MS * (float) WEEK_MS))
+ " weeks";
} else if (millis > (float) DAY_MS * (float) DAY_MS) {
return Round(millis / ((float) DAY_MS * (float) DAY_MS)) + " days";
} else if (millis > (float) HOUR_MS * (float) HOUR_MS) {
return Round(millis / ((float) HOUR_MS * (float) HOUR_MS))
+ " hours";
} else { // if (millis > MINUTE_MS) {
return Round(millis / ((float) MINUTE_MS * (float) MINUTE_MS))
+ " minutes";
}
}
/**
* Default number of decimal places to round to:
*/
public static final int DECIMAL_PLACES = 2;
/**
* Round a float to the default number of decimal places.
*
* @param value
* The value to round.
* @return The rounded value as a float.
* @see #DECIMAL_PLACES
*/
public static float Round(float value) {
return Round(value, DECIMAL_PLACES);
}
/**
* Round a float to the specified number of decimal places.
*
* @param value
* The value to round.
* @param places
* The number of decimal points.
* @return The rounded value as a float.
*/
public static float Round(float value, int places) {
float p = (float) Math.pow(10, places);
value = value * p;
float tmp = Math.round(value);
return (float) tmp / p;
}
/**
* Returns the "timestamp" string representation of the time in milliseconds:
* yyyy/mm/dd HH:MM:SS
*
* @param millis
* The milliseconds since epoch to format.
* @return The timestamp string.
*/
public static String toTimestamp(long millis) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(millis);
return DateUtil.toTimestamp(c);
}
/**
* Returns the "short timestamp" string representation of the time in
* milliseconds: HH:MM:SS
*
* @param millis
* The milliseconds since epoch to format.
* @return The short timestamp string.
*/
public static String toShortTimestamp(long millis) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(millis);
return DateUtil.toShortTimestamp(c);
}
/**
* Utility routine for padding zeros on the left side of an integer out to two
* digits, since string concatenations this small are much more efficient that
* using String.format("%02d",foo).
*
* @param i
* The integer to format.
* @return A zero-padded string representation of the integer.
*/
private static String l2pad(int i) {
if (i < 10)
return "0" + i;
return "" + i;
}
/**
* Returns a "timestamp" formated string representing the time:
* "yyyy/mm/dd HH:MM:SS"
*
* @param d
* The DateItem to format.
* @return The timestamp string.
*/
public static String toTimestamp(DateItem d) {
return d.mYear + "/" + l2pad(d.mMonth + 1) + "/" + l2pad(d.mDay) + " "
+ l2pad(d.mHour) + ":" + l2pad(d.mMinute) + ":" + l2pad(d.mSecond);
}
/**
* Returns a "timestamp" formated string representing the time:
* "yyyy/mm/dd HH:MM:SS"
*
* @param d
* The Calendar to format.
* @return The timestamp string.
*/
public static String toTimestamp(Calendar cal) {
return cal.get(Calendar.YEAR) + "/" + l2pad(cal.get(Calendar.MONTH) + 1)
+ "/" + l2pad(cal.get(Calendar.DAY_OF_MONTH)) + " "
+ l2pad(cal.get(Calendar.HOUR_OF_DAY)) + ":"
+ l2pad(cal.get(Calendar.MINUTE)) + ":"
+ l2pad(cal.get(Calendar.SECOND));
}
/**
* Returns a "short timestamp" formated string representing the time:
* "HH:MM:SS"
*
* @param d
* The Calendar to format.
* @return The timestamp string.
*/
public static String toShortTimestamp(Calendar cal) {
return l2pad(cal.get(Calendar.HOUR_OF_DAY)) + ":"
+ l2pad(cal.get(Calendar.MINUTE)) + ":"
+ l2pad(cal.get(Calendar.SECOND));
}
/**
* Returns a (generally) filesystem-safe formated string representing the
* time: "yyyy-mm-dd_HH.MM.SS"
*
* @param d
* The Calendar to format.
* @return The timestamp string.
*/
public static String toFSTimestamp(Calendar cal) {
return cal.get(Calendar.YEAR) + "-" + l2pad(cal.get(Calendar.MONTH) + 1)
+ "-" + l2pad(cal.get(Calendar.DAY_OF_MONTH)) + "_"
+ l2pad(cal.get(Calendar.HOUR_OF_DAY)) + "."
+ l2pad(cal.get(Calendar.MINUTE)) + "."
+ l2pad(cal.get(Calendar.SECOND));
}
/**
* Returns true if the two calendars represent dates that fall in the same
* year, else false.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameYear(Calendar c1, Calendar c2) {
if (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR))
return true;
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* quarter, else false. A quarter here is defined as the each group of three
* consecutive months, starting with the month designated as the first month
* by the Calendar package. Thus, it is not defined as the average number of
* milliseconds in a quarter, which would be {@link #YEAR_MS}/4.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameQuarter(Calendar c1, Calendar c2) {
if (inSameYear(c1, c2)) {
int m1 = c1.get(Calendar.MONTH);
int m2 = c2.get(Calendar.MONTH);
if (m1 >= 9 && m2 >= 9)
return true;
if (m1 >= 6 && m1 < 9 && m2 >= 6 && m2 < 9)
return true;
if (m1 >= 3 && m1 < 6 && m2 >= 3 && m2 < 6)
return true;
if (m1 >= 0 && m1 < 3 && m2 >= 0 && m2 < 3)
return true;
}
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* month, else false.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameMonth(Calendar c1, Calendar c2) {
if (inSameYear(c1, c2)
&& (c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH)))
return true;
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* week, else false. A week here is defined by the Calendar.WEEK_OF_YEAR
* package. Special provisions have been made to test weeks than may span the
* end/beginning of a year, and returning true if the two calendars are
* specifying dates within such a week, despite Calendar.WEEK_OF_YEAR being
* unequal for the two Calendars.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameWeek(Calendar c1, Calendar c2) {
if (inSameYear(c1, c2)
&& (c1.get(Calendar.WEEK_OF_YEAR) == c2.get(Calendar.WEEK_OF_YEAR)))
return true;
Calendar tmp;
if (c1.before(c2)) {
tmp = c2;
c2 = c1;
c1 = tmp;
}
int c1week = c1.get(Calendar.WEEK_OF_YEAR);
int c2week = c1.get(Calendar.WEEK_OF_YEAR);
if (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) + 1) {
if (c1week == c1.getActualMinimum(Calendar.WEEK_OF_YEAR)
&& c2week == c2.getActualMaximum(Calendar.WEEK_OF_YEAR)) {
tmp = (Calendar) c2.clone();
tmp.add(Calendar.DAY_OF_YEAR, 7);
if (tmp.get(Calendar.WEEK_OF_YEAR) > c1week)
return true;
}
}
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* day, else false.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameDay(Calendar c1, Calendar c2) {
if (inSameYear(c1, c2)
&& (c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR)))
return true;
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* morning or evening, as defined by [midnight,noon) and [noon,midnight), else
* false.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameAMPM(Calendar c1, Calendar c2) {
if (inSameDay(c1, c2) && (c1.get(Calendar.AM_PM) == c2.get(Calendar.AM_PM)))
return true;
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* hour, else false.
*
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSameHour(Calendar c1, Calendar c2) {
if (inSameDay(c1, c2)
&& (c1.get(Calendar.HOUR_OF_DAY) == c2.get(Calendar.HOUR_OF_DAY)))
return true;
return false;
}
/**
* Returns true if the two calendars represent dates that fall in the same
* period, else false.
*
* @param aggregationMillis
* The period as specified in milliseconds, e.g., DateUtil.YEAR_MS
* @param c1
* Calendar one.
* @param c2
* Calendar two.
* @return boolean.
*/
public static boolean inSamePeriod(Calendar c1, Calendar c2,
long aggregationMillis) {
if (aggregationMillis == 0)
return false;
if ((aggregationMillis == YEAR_MS && inSameYear(c1, c2))
|| (aggregationMillis == QUARTER_MS && inSameQuarter(c1, c2))
|| (aggregationMillis == MONTH_MS && inSameMonth(c1, c2))
|| (aggregationMillis == WEEK_MS && inSameWeek(c1, c2))
|| (aggregationMillis == DAY_MS && inSameDay(c1, c2))
|| (aggregationMillis == AMPM_MS && inSameAMPM(c1, c2))
|| (aggregationMillis == HOUR_MS && inSameHour(c1, c2))) {
return true;
}
return false;
}
/**
* Sets the date/time of the Calendar object to the beginning of the Period by
* setting all fields smaller than the specified period to the minimum value.
*
* @param c
* The calendar to set.
* @param p
* The DateUtil.Period to set.
*/
public static void setToPeriodStart(Calendar c, Period p) {
switch (p) {
case YEAR:
c.set(Calendar.MONTH, 0);
case MONTH:
c.set(Calendar.DAY_OF_MONTH, 1);
case DAY:
c.set(Calendar.HOUR_OF_DAY, 0);
case HOUR:
c.set(Calendar.MINUTE, 0);
case MINUTE:
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
break;
case WEEK:
c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
break;
case AMPM:
if (c.get(Calendar.AM_PM) == Calendar.AM)
c.set(Calendar.HOUR_OF_DAY, 0);
else
c.set(Calendar.HOUR_OF_DAY, 12);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
break;
case QUARTER:
int month = c.get(Calendar.MONTH);
if (month >= 9)
c.set(Calendar.MONTH, 9);
else if (month >= 9)
c.set(Calendar.MONTH, 6);
else if (month >= 9)
c.set(Calendar.MONTH, 3);
else
c.set(Calendar.MONTH, 0);
c.set(Calendar.DAY_OF_MONTH, 0);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
break;
}
return;
}
/**
* Utility routine to set each DateTime component field to that specified by
* the DateItem's millisecond field.
*
* @param d
* The DateItem to modify.
*/
private void millisToComponent(DateItem d) {
mCal.setTimeInMillis(d.mMillis);
d.mYear = mCal.get(Calendar.YEAR);
d.mMonth = mCal.get(Calendar.MONTH);
d.mDay = mCal.get(Calendar.DAY_OF_MONTH);
d.mHour = mCal.get(Calendar.HOUR_OF_DAY);
d.mMinute = mCal.get(Calendar.MINUTE);
}
/**
* Copy all member variable of one DateItem to that of another DateItem.
*
* @param src
* The DateItem to copy from.
* @param dst
* The DateItem to copy to.
*/
private void copyDate(DateItem src, DateItem dst) {
dst.mYear = src.mYear;
dst.mMonth = src.mMonth;
dst.mDay = src.mDay;
dst.mHour = src.mHour;
dst.mMinute = src.mMinute;
dst.mMillis = src.mMillis;
}
}
|