001 /*
002 * Copyleft notice: This is public-domain software and documentation
003 * with no restrictions of any kind.
004 * Please feel free to use any of it in any way you want.
005 * This work is distributed in the hope that it will be useful,
006 * but WITHOUT ANY WARRANTY; without even the implied warranty of
007 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
008 */
009
010 package time;
011
012 import java.util.*;
013
014 /**
015 * This class provides a concrete gregorian chronology.
016 *
017 * It is backed by a <tt>java.util.GregorianChronology</tt>.
018 * It is written to make a example of Chronology.
019 *
020 * This class is immutable and <i>thread-safe</i>.
021 *
022 * <p>
023 * Please note that this current implementation is not still under work, and
024 * not finished.
025 *
026 * @author Arnaud Roques
027 *
028 * @navassoc - use - time.DayOfWeek
029 * @navassoc - use - time.Month
030 *
031 */
032 public final class GregorianChronology implements Chronology
033 {
034 /**
035 * Year, between -9999 and +9999.
036 * Unlike GregorianCalendar, there is no concept of ERA in this chronology.
037 * It means that "1 BC" Year in Gregorian calendar is "0" in this chronology, and
038 * "2 BC" is "-1"...
039 */
040 public final static TimeField<Integer> YEAR;
041
042 /**
043 * Month, which values must be a <a href=Month.html><tt>Month</tt></a> object,
044 * from JANUARY to DECEMBER
045 */
046 public final static TimeField<Month> MONTH;
047
048 /**
049 * Day of the month, from 1 to 31
050 */
051 public final static TimeField<Integer> DAY_OF_MONTH;
052
053 /**
054 * Day of year, from 1 to 366
055 */
056 public final static TimeField<Integer> DAY_OF_YEAR;
057
058 /**
059 * Week of year, from 1 to 53
060 */
061 public final static TimeField<Integer> WEEK_OF_YEAR;
062
063 //public final static TimeField<Integer> WEEK_OF_MONTH;
064
065 /**
066 * Hour of the day, from 0 to 23
067 */
068 public final static TimeField<Integer> HOUR_OF_DAY;
069
070
071 final static TimeField<DstHour> DST_HOUR;
072
073 /**
074 * Minute of the hour, from 0 to 59
075 */
076 public final static TimeField<Integer> MINUTE;
077
078 /**
079 * Second, from 0 to 59
080 */
081 public final static TimeField<Integer> SECOND;
082
083 /**
084 * Millisecond, from 0 to 999
085 */
086 public final static TimeField<Integer> MILLISECOND;
087
088 private final DayOfWeek firstDayOfWeek;
089
090 /**
091 * Day of the week, which values if a <a href=DayOfWeek.html><tt>DayOfWeek</tt></a>.
092 * Unlike other <tt>TimeField</tt> of the GregorianChronology,
093 * this field may vary upon different instance of object, as
094 * the order of weekday depends on the Locale.
095 *
096 * <p>On the other hand, this implementation garantees that if two different
097 * <tt>GrogorianChronology</tt> have the same first dayweek, they will share
098 * the same <tt>DAY_OF_WEEK</tt> field.
099 */
100 public final TimeField<DayOfWeek> DAY_OF_WEEK;
101
102 private static final Map<DayOfWeek, TimeField<DayOfWeek>> dayWeekMap;
103
104
105 private final GregorianCalendar gcInternal;
106
107 private final Map<TimeMask, SortedSet<Time>> asSortedSet;
108 private final Map<TimeMask, List<Time>> asList;
109 private final Map<TimeMask, TimeInstantBridge> instantBridgeMap;
110
111 static final int MIN_YEAR = -1000;
112 static final int MAX_YEAR = 2500;
113 private final YearStatDatabase yearStatDatabase;
114
115 /**
116 * Build a new <tt>GregorianChronology</tt> backed by
117 * a default <tt>GregorianCalendar</tt> using the
118 * default time zone and the default locale.
119 *
120 */
121 public GregorianChronology()
122 {
123 this(new GregorianCalendar());
124 }
125
126
127 /**
128 * Build a new <tt>GregorianChronology</tt> backed by
129 * a specific <tt>GregorianCalendar</tt>.
130 *
131 * <p>
132 * To garantee immutability, <tt>gc</tt> is cloned.
133 *
134 * @param gc calendar used by this Chronology.
135 */
136 public GregorianChronology(GregorianCalendar gc)
137 {
138 //System.err.println("gc="+gc);
139 //System.err.println("gc.getTimeZone()="+gc.getTimeZone());
140 //System.err.println("gc="+gc.get(GregorianCalendar.DST_OFFSET));
141 //System.err.println("gc="+gc.get(GregorianCalendar.DST_OFFSET));
142
143 this.gcInternal = (GregorianCalendar) gc.clone();
144 // this.gcInternal.set(GregorianCalendar.DST_OFFSET, 0);
145 // this.gcInternal.setTimeInMillis(0L);
146 this.gcInternal.setLenient(true);
147 this.gcInternal.clear();
148 System.err.println("***");
149 System.err.println("init dstOffset="+gcInternal.get(gc.DST_OFFSET));
150 System.err.println("init getTimeInMillis="+gcInternal.getTimeInMillis());
151 System.err.println("init="+gcInternal.getTime());
152 System.err.println("***");
153
154
155 firstDayOfWeek = DayOfWeek.fromGcValue(gc.getFirstDayOfWeek());
156
157 //final Collection<DayOfWeek> days = DayOfWeek.specialSet(firstDayOfWeek);
158 //DAY_OF_WEEK = new TimeField<DayOfWeek>("DAY_OF_WEEK", 1000L*3600*24, days, DayOfWeek.specialComparator(firstDayOfWeek));
159 DAY_OF_WEEK = dayWeekMap.get(firstDayOfWeek);
160
161 int i = 0;
162
163 for (DayOfWeek dw : DayOfWeek.specialSet(firstDayOfWeek))
164 {
165 allDays[i] = dw;
166 indiceDayOfWeek.put(dw, i++);
167 }
168
169
170 asSortedSet = new HashMap<TimeMask, SortedSet<Time>>();
171 asList = new HashMap<TimeMask, List<Time>>();
172 instantBridgeMap = new HashMap<TimeMask, TimeInstantBridge>();
173
174 yearStatDatabase = new YearStatDatabase(this.gcInternal, MIN_YEAR, MAX_YEAR);
175
176 build();
177
178
179 }
180
181 final private Map<DayOfWeek, Integer> indiceDayOfWeek = new HashMap<DayOfWeek, Integer>();
182 final private DayOfWeek allDays[] = new DayOfWeek[7];
183
184 int getIndiceDayOfWeek(DayOfWeek dw)
185 {
186 return indiceDayOfWeek.get(dw);
187 }
188
189 DayOfWeek getWeekDayFromIndice(int i)
190 {
191 return allDays[i];
192 }
193
194 private void build()
195 {
196 build(new TimeMask(YEAR), new BrigdeYear(),
197 new TimeInstantBridgeYear(this));
198 build(new TimeMask(YEAR, MONTH), new BrigdeYearMonth(),
199 new TimeInstantBridgeYearMonth(this));
200 build(new TimeMask(YEAR, DAY_OF_YEAR), new BrigdeYearDay(yearStatDatabase),
201 new TimeInstantBridgeYearDay(this));
202 build(new TimeMask(YEAR, MONTH, DAY_OF_MONTH),
203 new BrigdeYearMonthDay(yearStatDatabase, gcInternal),
204 new TimeInstantBridgeYearMonthDay(this));
205 build(new TimeMask(YEAR, WEEK_OF_YEAR),
206 new BrigdeYearWeek(yearStatDatabase),
207 new TimeInstantBridgeYearWeek(this));
208 build(new TimeMask(YEAR, WEEK_OF_YEAR, DAY_OF_WEEK),
209 new BrigdeYearWeekWeekDay(yearStatDatabase, this),
210 new TimeInstantBridgeYearWeekWeekDay(this));
211 }
212
213 private void build(TimeMask mask, TimeLongBridge longBridge, TimeInstantBridge instantBridge)
214 {
215 asSortedSet.put(mask, new TimeSortedSet(longBridge));
216 asList.put(mask, new TimeList(longBridge));
217 instantBridgeMap.put(mask, instantBridge);
218 }
219
220
221 synchronized GregorianCalendar getGc()
222 {
223 GregorianCalendar gc = (GregorianCalendar) gcInternal.clone();
224 //assert(gc.getTimeInMillis()==0L) : gc.getTimeInMillis();
225 return gc;
226 }
227
228
229 static
230 {
231
232 // Fields
233 YEAR = new TimeField<Integer>("YEAR", 1000L*3600*24*365, new IntegerSortedSet(MIN_YEAR, MAX_YEAR), null);
234 MONTH = new TimeField<Month>("MONTH", 1000L*3600*24*30, EnumSet.allOf(Month.class), null);
235 DAY_OF_MONTH = new TimeField<Integer>("DAY_OF_MONTH", 1000L*3600*24, new IntegerSortedSet(1, 32), null);
236 DAY_OF_YEAR = new TimeField<Integer>("DAY_OF_YEAR", 1000L*3600*24, new IntegerSortedSet(1, 367), null);
237
238
239 WEEK_OF_YEAR = new TimeField<Integer>("WEEK_OF_YEAR", 1000L*3600*24*7, new IntegerSortedSet(1, 54), null);
240 //WEEK_OF_MONTH = new TimeField<Integer>("WEEK_OF_MONTH", 1000L*3600*24*7, new IntegerSortedSet(1, 6), null);
241 HOUR_OF_DAY = new TimeField<Integer>("HOUR_OF_DAY", 1000L*3600, new IntegerSortedSet(0, 24), null);
242 DST_HOUR = new TimeField<DstHour>("DST_HOUR", 1000L*3600-1, EnumSet.allOf(DstHour.class), null);
243
244 MINUTE = new TimeField<Integer>("MINUTE", 1000L*60, new IntegerSortedSet(0, 60), null);
245 SECOND = new TimeField<Integer>("SECOND", 1000L, new IntegerSortedSet(0, 60), null);
246 MILLISECOND = new TimeField<Integer>("MILLISECOND", 1L, new IntegerSortedSet(0, 1000), null);
247
248 dayWeekMap = new HashMap<DayOfWeek, TimeField<DayOfWeek>>();
249
250 for (DayOfWeek dw : DayOfWeek.specialSet(DayOfWeek.SUNDAY))
251 {
252 Collection<DayOfWeek> days = DayOfWeek.specialSet(dw);
253 dayWeekMap.put(dw,
254 new TimeField<DayOfWeek>(
255 "DAY_OF_WEEK", 1000L*3600*24, days,
256 DayOfWeek.specialComparator(dw)));
257 }
258
259 }
260
261
262 public Time next(Time time) throws ClassCastException
263 {
264 Iterator<Time> it = asSortedSet(time.getTimeMask()).tailSet(time).iterator();
265 it.next();
266 return it.next();
267 }
268
269 public Time previous(Time time) throws ClassCastException
270 {
271 return asSortedSet(time.getTimeMask()).headSet(time).last();
272 }
273
274
275 public SortedSet<Time> asSortedSet(TimeMask mask)
276 {
277 SortedSet<Time> r = asSortedSet.get(mask);
278 if (r==null) throw new IllegalArgumentException();
279 return r;
280 }
281
282 public List<Time> asList(TimeMask mask)
283 {
284 List<Time> r = asList.get(mask);
285 if (r==null) throw new IllegalArgumentException();
286 return r;
287 }
288
289 public List<Time> asList(Time startingTime, int startingPosition)
290 {
291 return null;
292 }
293
294
295 public SortedSet<Time> split(Time timeToSplit, TimeField... fieldsToAdd)
296 {
297 return null;
298 }
299
300
301 public Instant getInstant(Time time) throws ClassCastException
302 {
303 return null;
304 }
305
306 public Interval getInterval(Time time) throws ClassCastException
307 {
308 testTimeMask(time);
309 TimeInstantBridge bridge = instantBridgeMap.get(time.getTimeMask());
310 if (bridge==null) throw new IllegalArgumentException();
311 Instant i1 = bridge.getStartingInstant(time);
312 Instant i2 = bridge.getEndingInstant(time);
313 return new Interval(i1, i2);
314 }
315
316 private void testTimeMask(Time time) throws ClassCastException
317 {
318 }
319
320
321 public Time getTime(Instant instant)
322 {
323 GregorianCalendar gc = getGc();
324 gc.setTimeInMillis(instant.getMillis());
325
326 int year = gc.get(gc.YEAR);
327 assert(year>0);
328 if (gc.get(gc.ERA)==gc.BC) year = -year+1;
329 int month = gc.get(gc.MONTH);
330 int dayOfYear = gc.get(gc.DAY_OF_YEAR);
331 int dayOfMonth = gc.get(gc.DAY_OF_MONTH);
332 int weekOfYear = gc.get(gc.WEEK_OF_YEAR);
333 int dayOfWeek = gc.get(gc.DAY_OF_WEEK);
334 int hourOfDay = gc.get(gc.HOUR_OF_DAY);
335 int minute = gc.get(gc.MINUTE);
336 int second = gc.get(gc.SECOND);
337 int millisecond = gc.get(gc.MILLISECOND);
338 int dstOffset = gc.get(gc.DST_OFFSET);
339 System.err.println("dstOffset="+gc.get(gc.DST_OFFSET));
340
341 Month month2 = Month.fromValue(month);
342 DayOfWeek dayOfWeek2 = DayOfWeek.fromGcValue(dayOfWeek);
343
344 DstHour dstHour = DstHour.NORMAL;
345 gc.setTimeInMillis(gc.getTimeInMillis()-1000L*3600);
346 if (gc.get(gc.HOUR_OF_DAY)==hourOfDay) dstHour = dstHour.SECOND_TIME_IN_DAY;
347
348 Map<TimeField, Object> map = new HashMap<TimeField, Object>();
349 map.put(DST_HOUR, dstHour);
350 map.put(YEAR, year);
351 map.put(MONTH, month2);
352 map.put(DAY_OF_YEAR, dayOfYear);
353 map.put(DAY_OF_MONTH, dayOfMonth);
354 map.put(WEEK_OF_YEAR, weekOfYear);
355 map.put(DAY_OF_WEEK, dayOfWeek2);
356 map.put(HOUR_OF_DAY, hourOfDay);
357 map.put(MINUTE, minute);
358 map.put(SECOND, second);
359 map.put(MILLISECOND, millisecond);
360
361 return new Time(map);
362
363 }
364
365 public Time getTime(Instant instant, TimeMask mask) throws ClassCastException
366 {
367 return getTime(instant).subMap(mask);
368 }
369
370
371 public Time getTime(Interval interval)
372 {
373 return null;
374 }
375
376
377 public Time getTime(Interval interval, TimeMask mask) throws ClassCastException
378 {
379 return null;
380 }
381
382
383
384 public boolean isConsistentForInstant(Time time)
385 {
386 return false;
387 }
388
389 public boolean isConsistentForInterval(Time time)
390 {
391 return false;
392 }
393
394 public SortedSet<TimeField> knownFields()
395 {
396 return null;
397 }
398
399
400 long getDayDuration(Time day)
401 {
402 Interval inter = getInterval(day);
403 long duration = inter.getDuration();
404 return duration;
405 //long hour = 1000L*3600;
406 /*assert(duration%hour==0) : day + " "+duration+" "+inter+" "+
407 inter.getStart().asImmutableDate().getTime()+" "+
408 inter.getEnd().asImmutableDate().getTime();*/
409 //return (int)(duration / hour);
410 }
411 }
412