Java tutorial
/* * 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; } }