| 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 | } |