001 package org.rakeshv.utils;
002
003 import java.util.GregorianCalendar;
004 import java.util.Locale;
005 import java.util.TimeZone;
006
007 /**
008 * A wrapper around <code>java.util.GregorianCalendar</code>. Setting
009 * the <code>TimeZone</code> of this <code>Calendar</code> will
010 * automatically adjust the underlying <code>Date</code> by
011 * offsetting from the <code>existing TimeZone</code>.
012 *
013 * <p>Java always represents the time in UTC. This can sometimes be
014 * incovenient especially when dealing with RDBMS systems, where
015 * fetching a date/time/timestamp value will return a value with the
016 * millisecond value that represents the value in current TimeZone
017 * as opposed to UTC. This class can be used to conveniently correct
018 * the internal time represented.</p>
019 *
020 * <p>The following code snippet shows a way of using this class:</p>
021 * <pre>
022 * import java.util.Calendar;
023 * import java.util.TimeZone;
024 *
025 * Calendar local = org.rakeshv.utils.Calendar.getInstance();
026 * System.out.println( "local " + local.getTime() );
027 *
028 * Calendar utc = org.rakeshv.utils.Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
029 * System.out.println( "utc " + utc.getTime() );
030 *
031 * Calendar pst = org.rakeshv.utils.Calendar.getInstance( TimeZone.getTimeZone( "America/Los_Angeles" ) );
032 * System.out.println( "pst " + pst.getTime() );
033 * pst.setTimeZone( TimeZone.getDefault() );
034 * System.out.println( "pst in local " + pst.getTime() );
035 * </pre>
036 *
037 * <p>Copyright 2004-2006 Rakesh Vidyadharan</p>
038 * @author Rakesh Vidyadharan 2004 August 18
039 * @version $Id: Calendar.java,v 1.6 2006/03/11 04:15:14 rakesh Exp $
040 */
041 public class Calendar extends GregorianCalendar
042 {
043 /**
044 * Constructs a Calendar with the default time zone and locale.
045 * Just invokes the super class constructor.
046 */
047 public Calendar()
048 {
049 super();
050 }
051
052 /**
053 * Constructs a Calendar based on the current time in the given time
054 * zone with the default locale. The <code>time</code> for the
055 * calendar is set to the equivalent for the specified time zone
056 * based upon the system time in the system time zone.
057 *
058 * @see #setTimeZone( TimeZone )
059 * @param timeZone - The <code>TimeZone</code> to use.
060 */
061 public Calendar( TimeZone timeZone )
062 {
063 super();
064 setTimeZone( timeZone );
065 }
066
067 /**
068 * Constructs a Calendar based on the current time in the default
069 * time zone with the given locale.
070 *
071 * @param locale - The <code>Locale</code> to use.
072 */
073 public Calendar( Locale locale )
074 {
075 super( locale );
076 }
077
078 /**
079 * Constructs a calendar with the specified time zone and locale.
080 * The <code>time</code> for the calendar is set to the equivalent
081 * for the specified time zone based upon the system time in the
082 * system time zone.
083 *
084 * @see #setTimeZone( TimeZone )
085 * @param timeZone - The <code>TimeZone</code> to use.
086 * @param locale - The <code>Locale</code> to use.
087 */
088 public Calendar( TimeZone timeZone, Locale locale )
089 {
090 super( locale );
091 setTimeZone( timeZone );
092 }
093
094 /**
095 * Gets a calendar using the default time zone and locale. The
096 * Calendar returned is based on the current time in the default time
097 * zone with the default locale.
098 *
099 * @see #Calendar()
100 * @return java.util.Calendar - A new instance of {@link Calendar}.
101 */
102 public static java.util.Calendar getInstance()
103 {
104 return new Calendar();
105 }
106
107 /**
108 * Gets a calendar using the specified time zone and default locale.
109 * The Calendar returned is based on the current time in the given
110 * time zone corresponding to the system time in the system time
111 * zone with the default locale.
112 *
113 * @see #Calendar( TimeZone )
114 * @param timeZone - The <code>TimeZone</code> to use.
115 * @return java.util.Calendar - A new instance of {@link Calendar}.
116 */
117 public static java.util.Calendar getInstance( TimeZone timeZone )
118 {
119 return new Calendar( timeZone );
120 }
121
122 /**
123 * Gets a calendar using the default time zone and specified locale.
124 * The Calendar returned is based on the current time in the default
125 * time zone with the given locale.
126 *
127 * @see #Calendar( Locale )
128 * @param locale - The <code>Locale</code> to use.
129 * @return java.util.Calendar - A new instance of {@link Calendar}.
130 */
131 public static java.util.Calendar getInstance( Locale locale )
132 {
133 return new Calendar( locale );
134 }
135
136 /**
137 * Gets a calendar with the specified time zone and locale. The
138 * Calendar returned is based on the current time in the given time
139 * zone corresponding to the system time in the sytem time zone
140 * with the given locale.
141 *
142 * @see #Calendar( TimeZone, Locale )
143 * @param timeZone - The <code>TimeZone</code> to use.
144 * @param locale - The <code>Locale</code> to use.
145 * @return java.util.Calendar - A new instance of {@link Calendar}.
146 */
147 public static java.util.Calendar getInstance( TimeZone timeZone,
148 Locale locale )
149 {
150 return new Calendar( timeZone, locale );
151 }
152
153 /**
154 * Sets the time zone with the given time zone value. The <code>
155 * time</code> value is modified by the appropriate
156 * <code>offset</code> based upon the current time zone.
157 */
158 @Override
159 public void setTimeZone( TimeZone timeZone )
160 {
161 int currentOffset =
162 get( Calendar.ZONE_OFFSET ) + get( Calendar.DST_OFFSET );
163 super.setTimeZone( timeZone );
164 int newOffset =
165 get( Calendar.ZONE_OFFSET ) + get( Calendar.DST_OFFSET );
166 int correction = currentOffset - newOffset;
167 setTimeInMillis( getTimeInMillis() - correction );
168 }
169
170 /**
171 * Sets the given calendar field to the given value. Over-ridden
172 * to force <code>HOUR</code> and <code>HOUR_OF_DAY</code> to
173 * apply proper correction when the <code>timeZone</code> is not
174 * the local timeZone.
175 *
176 * @param field The given calendar field.
177 * @param value The value to be set for the given calendar field.
178 * @throws ArrayIndexOutOfBoundsException if the specified field is
179 * out of range (<code>field < 0 || field >= FIELD_COUNT</code>).
180 * In non-lenient mode.
181 */
182 @Override
183 public void set( int field, int value )
184 {
185 switch ( field )
186 {
187 case Calendar.HOUR:
188 case Calendar.HOUR_OF_DAY:
189 TimeZone timeZone = getTimeZone();
190 int currentOffset =
191 get( Calendar.ZONE_OFFSET ) + get( Calendar.DST_OFFSET );
192 setTimeZone( TimeZone.getDefault() );
193 int localOffset =
194 get( Calendar.ZONE_OFFSET ) + get( Calendar.DST_OFFSET );
195 int correction =
196 ( currentOffset - localOffset ) / ( 60 * 60 * 1000 );
197 value += correction;
198 setTimeZone( timeZone );
199 super.set( field, value );
200 break;
201 default:
202 super.set( field, value );
203 break;
204 }
205 }
206
207 /**
208 * A contant used to indicate that the <code>MINUTE</code> of this
209 * calendar is to be rounded up to the nearest multiple of 5. Used
210 * by the {@link #roundMinutes} method.
211 *
212 * {@value}
213 */
214 public static final int ROUND_UP = 1;
215
216 /**
217 * A contant used to indicate that the <code>MINUTE</code> of this
218 * calendar is to be rounded down to the nearest multiple of 5. Used
219 * by the {@link #roundMinutes} method.
220 *
221 * {@value}
222 */
223 public static final int ROUND_DOWN = 2;
224
225 /**
226 * A contant used to indicate that the <code>MINUTE</code> of this
227 * calendar is to be rounded automatically using normal rounding
228 * rules to the nearest multiple of 5. Used by the
229 * {@link #roundMinutes} method.
230 *
231 * {@value}
232 */
233 public static final int ROUND_AUTO = 3;
234
235 /**
236 * Round the <code>MINUTE</code> part of this calendar to the nearest
237 * 5 minute mark. This is useful for creating drop-down menus in
238 * user interface components.
239 *
240 * @param minInterval The interval to use for rounding. The value
241 * must be at least 5 (it will be corrected to 5 otherwise), and
242 * should be a multiple of 5 (will be corrected to a multiple of
243 * 5 otherwise).
244 * @param rounding A constant used to indicate the direction in which
245 * the rounding is to be performed. Specify one of {@link
246 * #ROUND_UP}, {@link #ROUND_DOWN}, or {@link #ROUND_AUTO}.
247 * @return Calendar Returns a reference to this object.
248 */
249 public Calendar roundMinutes( int minInterval, int rounding )
250 {
251 if ( minInterval < 5 )
252 {
253 minInterval = 5;
254 }
255
256 // Round only in 5 minutes increments
257 int fixedInterval = ( minInterval / 5 ) * 5;
258
259 if ( fixedInterval > 30 && fixedInterval != 60 )
260 {
261 fixedInterval = 30;
262 }
263
264 int minute = get( Calendar.MINUTE );
265 set( Calendar.SECOND, 0 );
266 set( Calendar.MINUTE, 0 );
267 double tmpMinMin = (double) ( minute / fixedInterval );
268
269 switch ( rounding )
270 {
271 case ROUND_UP:
272 minute = (int) ( Math.ceil( tmpMinMin ) * fixedInterval );
273 break;
274 case ROUND_DOWN:
275 minute= (int) ( Math.floor( tmpMinMin ) * fixedInterval );
276 break;
277 case ROUND_AUTO:
278 default:
279 minute= (int) ( Math.round( tmpMinMin ) * fixedInterval );
280 break;
281 }
282
283 add( Calendar.MINUTE, minute );
284
285 return this;
286 }
287 }