View Javadoc

1   /*
2    *  Copyright 2001-2005 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.joda.time.chrono;
17  
18  import org.joda.time.DateTimeConstants;
19  import org.joda.time.DateTimeFieldType;
20  import org.joda.time.DurationField;
21  import org.joda.time.field.FieldUtils;
22  import org.joda.time.field.ImpreciseDateTimeField;
23  
24  /**
25   * Provides time calculations for the week of the weekyear component of time.
26   *
27   * @author Guy Allard
28   * @author Stephen Colebourne
29   * @author Brian S O'Neill
30   * @since 1.1, refactored from GJWeekyearDateTimeField
31   */
32  final class BasicWeekyearDateTimeField extends ImpreciseDateTimeField {
33      
34      private static final long serialVersionUID = 6215066916806820644L;
35  
36      private static final long WEEK_53 = (53L - 1) * DateTimeConstants.MILLIS_PER_WEEK;
37  
38      private final BasicChronology iChronology;
39  
40      /**
41       * Restricted constructor
42       */
43      BasicWeekyearDateTimeField(BasicChronology chronology) {
44          super(DateTimeFieldType.weekyear(), chronology.getAverageMillisPerYear());
45          iChronology = chronology;
46      }
47  
48      public boolean isLenient() {
49          return false;
50      }
51  
52      /**
53       * Get the Year of a week based year component of the specified time instant.
54       * 
55       * @see org.joda.time.DateTimeField#get
56       * @param instant  the time instant in millis to query.
57       * @return the year extracted from the input.
58       */
59      public int get(long instant) {
60          return iChronology.getWeekyear(instant);
61      }
62  
63      /**
64       * Add the specified years to the specified time instant.
65       * 
66       * @see org.joda.time.DateTimeField#add
67       * @param instant  the time instant in millis to update.
68       * @param years  the years to add (can be negative).
69       * @return the updated time instant.
70       */
71      public long add(long instant, int years) {
72          if (years == 0) {
73              return instant;
74          }
75          return set(instant, get(instant) + years);
76      }
77  
78      public long add(long instant, long value) {
79          return add(instant, FieldUtils.safeToInt(value));
80      }
81  
82      /**
83       * Add to the year component of the specified time instant
84       * wrapping around within that component if necessary.
85       * 
86       * @see org.joda.time.DateTimeField#addWrapField
87       * @param instant  the time instant in millis to update.
88       * @param years  the years to add (can be negative).
89       * @return the updated time instant.
90       */
91      public long addWrapField(long instant, int years) {
92          return add(instant, years);
93      }
94  
95      public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) {
96          if (minuendInstant < subtrahendInstant) {
97              return -getDifference(subtrahendInstant, minuendInstant);
98          }
99  
100         int minuendWeekyear = get(minuendInstant);
101         int subtrahendWeekyear = get(subtrahendInstant);
102 
103         long minuendRem = remainder(minuendInstant);
104         long subtrahendRem = remainder(subtrahendInstant);
105 
106         // Balance leap weekyear differences on remainders.
107         if (subtrahendRem >= WEEK_53 && iChronology.getWeeksInYear(minuendWeekyear) <= 52) {
108             subtrahendRem -= DateTimeConstants.MILLIS_PER_WEEK;
109         }
110 
111         int difference = minuendWeekyear - subtrahendWeekyear;
112         if (minuendRem < subtrahendRem) {
113             difference--;
114         }
115         return difference;
116     }
117 
118     /**
119      * Set the Year of a week based year component of the specified time instant.
120      *
121      * @see org.joda.time.DateTimeField#set
122      * @param instant  the time instant in millis to update.
123      * @param year  the year (-9999,9999) to set the date to.
124      * @return the updated DateTime.
125      * @throws IllegalArgumentException  if year is invalid.
126      */
127     public long set(long instant, int year) {
128         FieldUtils.verifyValueBounds(this, Math.abs(year),
129                                      iChronology.getMinYear(), iChronology.getMaxYear());
130         //
131         // Do nothing if no real change is requested.
132         //
133         int thisWeekyear = get( instant );
134         if ( thisWeekyear == year ) {
135             return instant;
136         }
137         //
138         // Calculate the DayOfWeek (to be preserved).
139         //
140         int thisDow = iChronology.getDayOfWeek(instant);
141         //
142         // Calculate the maximum weeks in the target year.
143         //
144         int weeksInFromYear = iChronology.getWeeksInYear( thisWeekyear );
145         int weeksInToYear = iChronology.getWeeksInYear( year );
146         int maxOutWeeks = (weeksInToYear < weeksInFromYear) ?
147             weeksInToYear : weeksInFromYear;
148         //
149         // Get the current week of the year. This will be preserved in
150         // the output unless it is greater than the maximum possible
151         // for the target weekyear.  In that case it is adjusted
152         // to the maximum possible.
153         //
154         int setToWeek = iChronology.getWeekOfWeekyear(instant);
155         if ( setToWeek > maxOutWeeks ) {
156             setToWeek = maxOutWeeks;
157         }
158         //
159         // Get a wroking copy of the current date-time.
160         // This can be a convenience for debugging.
161         //
162         long workInstant = instant; // Get a copy
163         //
164         // Attempt to get close to the proper weekyear.
165         // Note - we cannot currently call ourself, so we just call
166         // set for the year.  This at least gets us close.
167         //
168         workInstant = iChronology.setYear( workInstant, year );
169         //
170         // Calculate the weekyear number for the get close to value
171         // (which might not be equal to the year just set).
172         //
173         int workWoyYear = get( workInstant );
174 
175         //
176         // At most we are off by one year, which can be "fixed" by
177         // adding/subtracting a week.
178         //
179         if ( workWoyYear < year ) {
180             workInstant += DateTimeConstants.MILLIS_PER_WEEK;
181         } else if ( workWoyYear > year ) {
182             workInstant -= DateTimeConstants.MILLIS_PER_WEEK;
183         }
184         //
185         // Set the proper week in the current weekyear.
186         //
187 
188         // BEGIN: possible set WeekOfWeekyear logic.
189         int currentWoyWeek = iChronology.getWeekOfWeekyear(workInstant);
190         // No range check required (we already know it is OK).
191         workInstant = workInstant + (setToWeek - currentWoyWeek)
192             * (long)DateTimeConstants.MILLIS_PER_WEEK;
193         // END: possible set WeekOfWeekyear logic.
194 
195         //
196         // Reset DayOfWeek to previous value.
197         //
198         // Note: This works fine, but it ideally shouldn't invoke other
199         // fields from within a field.
200         workInstant = iChronology.dayOfWeek().set( workInstant, thisDow );
201         //
202         // Return result.
203         //
204         return workInstant;
205     }
206 
207     public DurationField getRangeDurationField() {
208         return null;
209     }
210 
211     public boolean isLeap(long instant) {
212         return iChronology.getWeeksInYear(iChronology.getWeekyear(instant)) > 52;
213     }
214 
215     public int getLeapAmount(long instant) {
216         return iChronology.getWeeksInYear(iChronology.getWeekyear(instant)) - 52;
217     }
218 
219     public DurationField getLeapDurationField() {
220         return iChronology.weeks();
221     }
222 
223     public int getMinimumValue() {
224         return iChronology.getMinYear();
225     }
226 
227     public int getMaximumValue() {
228         return iChronology.getMaxYear();
229     }
230 
231     public long roundFloor(long instant) {
232         // Note: This works fine, but it ideally shouldn't invoke other
233         // fields from within a field.
234         instant = iChronology.weekOfWeekyear().roundFloor(instant);
235         int wow = iChronology.getWeekOfWeekyear(instant);
236         if (wow > 1) {
237             instant -= ((long) DateTimeConstants.MILLIS_PER_WEEK) * (wow - 1);
238         }
239         return instant;
240     }
241 
242     public long remainder(long instant) {
243         return instant - roundFloor(instant);
244     }
245 
246     /**
247      * Serialization singleton
248      */
249     private Object readResolve() {
250         return iChronology.weekyear();
251     }
252 }