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