001 /* 002 * Copyright 2001-2005 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.joda.time.field; 017 018 import org.joda.time.DateTimeField; 019 import org.joda.time.DateTimeFieldType; 020 import org.joda.time.DurationField; 021 022 /** 023 * Divides a DateTimeField such that the retrieved values are reduced by a 024 * fixed divisor. The field's unit duration is scaled accordingly, but the 025 * range duration is unchanged. 026 * <p> 027 * DividedDateTimeField is thread-safe and immutable. 028 * 029 * @see RemainderDateTimeField 030 * 031 * @author Stephen Colebourne 032 * @author Brian S O'Neill 033 * @since 1.0 034 */ 035 public class DividedDateTimeField extends DecoratedDateTimeField { 036 037 private static final long serialVersionUID = 8318475124230605365L; 038 039 // Shared with RemainderDateTimeField. 040 final int iDivisor; 041 final DurationField iDurationField; 042 043 private final int iMin; 044 private final int iMax; 045 046 /** 047 * Constructor. 048 * 049 * @param field the field to wrap, like "year()". 050 * @param type the field type this field will actually use 051 * @param divisor divisor, such as 100 years in a century 052 * @throws IllegalArgumentException if divisor is less than two 053 */ 054 public DividedDateTimeField(DateTimeField field, 055 DateTimeFieldType type, int divisor) { 056 super(field, type); 057 058 if (divisor < 2) { 059 throw new IllegalArgumentException("The divisor must be at least 2"); 060 } 061 062 DurationField unitField = field.getDurationField(); 063 if (unitField == null) { 064 iDurationField = null; 065 } else { 066 iDurationField = new ScaledDurationField( 067 unitField, type.getDurationType(), divisor); 068 } 069 070 iDivisor = divisor; 071 072 int i = field.getMinimumValue(); 073 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1); 074 075 int j = field.getMaximumValue(); 076 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1); 077 078 iMin = min; 079 iMax = max; 080 } 081 082 /** 083 * Construct a DividedDateTimeField that compliments the given 084 * RemainderDateTimeField. 085 * 086 * @param remainderField complimentary remainder field, like "yearOfCentury()". 087 * @param type the field type this field will actually use 088 */ 089 public DividedDateTimeField(RemainderDateTimeField remainderField, DateTimeFieldType type) { 090 super(remainderField.getWrappedField(), type); 091 int divisor = iDivisor = remainderField.iDivisor; 092 iDurationField = remainderField.iRangeField; 093 094 DateTimeField field = getWrappedField(); 095 int i = field.getMinimumValue(); 096 int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1); 097 098 int j = field.getMaximumValue(); 099 int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1); 100 101 iMin = min; 102 iMax = max; 103 } 104 105 /** 106 * Get the amount of scaled units from the specified time instant. 107 * 108 * @param instant the time instant in millis to query. 109 * @return the amount of scaled units extracted from the input. 110 */ 111 public int get(long instant) { 112 int value = getWrappedField().get(instant); 113 if (value >= 0) { 114 return value / iDivisor; 115 } else { 116 return ((value + 1) / iDivisor) - 1; 117 } 118 } 119 120 /** 121 * Add the specified amount of scaled units to the specified time 122 * instant. The amount added may be negative. 123 * 124 * @param instant the time instant in millis to update. 125 * @param amount the amount of scaled units to add (can be negative). 126 * @return the updated time instant. 127 */ 128 public long add(long instant, int amount) { 129 return getWrappedField().add(instant, amount * iDivisor); 130 } 131 132 /** 133 * Add the specified amount of scaled units to the specified time 134 * instant. The amount added may be negative. 135 * 136 * @param instant the time instant in millis to update. 137 * @param amount the amount of scaled units to add (can be negative). 138 * @return the updated time instant. 139 */ 140 public long add(long instant, long amount) { 141 return getWrappedField().add(instant, amount * iDivisor); 142 } 143 144 /** 145 * Add to the scaled component of the specified time instant, 146 * wrapping around within that component if necessary. 147 * 148 * @param instant the time instant in millis to update. 149 * @param amount the amount of scaled units to add (can be negative). 150 * @return the updated time instant. 151 */ 152 public long addWrapField(long instant, int amount) { 153 return set(instant, FieldUtils.getWrappedValue(get(instant), amount, iMin, iMax)); 154 } 155 156 public int getDifference(long minuendInstant, long subtrahendInstant) { 157 return getWrappedField().getDifference(minuendInstant, subtrahendInstant) / iDivisor; 158 } 159 160 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 161 return getWrappedField().getDifferenceAsLong(minuendInstant, subtrahendInstant) / iDivisor; 162 } 163 164 /** 165 * Set the specified amount of scaled units to the specified time instant. 166 * 167 * @param instant the time instant in millis to update. 168 * @param value value of scaled units to set. 169 * @return the updated time instant. 170 * @throws IllegalArgumentException if value is too large or too small. 171 */ 172 public long set(long instant, int value) { 173 FieldUtils.verifyValueBounds(this, value, iMin, iMax); 174 int remainder = getRemainder(getWrappedField().get(instant)); 175 return getWrappedField().set(instant, value * iDivisor + remainder); 176 } 177 178 /** 179 * Returns a scaled version of the wrapped field's unit duration field. 180 */ 181 public DurationField getDurationField() { 182 return iDurationField; 183 } 184 185 /** 186 * Get the minimum value for the field. 187 * 188 * @return the minimum value 189 */ 190 public int getMinimumValue() { 191 return iMin; 192 } 193 194 /** 195 * Get the maximum value for the field. 196 * 197 * @return the maximum value 198 */ 199 public int getMaximumValue() { 200 return iMax; 201 } 202 203 public long roundFloor(long instant) { 204 DateTimeField field = getWrappedField(); 205 return field.roundFloor(field.set(instant, get(instant) * iDivisor)); 206 } 207 208 public long remainder(long instant) { 209 return set(instant, get(getWrappedField().remainder(instant))); 210 } 211 212 /** 213 * Returns the divisor applied, in the field's units. 214 * 215 * @return the divisor 216 */ 217 public int getDivisor() { 218 return iDivisor; 219 } 220 221 private int getRemainder(int value) { 222 if (value >= 0) { 223 return value % iDivisor; 224 } else { 225 return (iDivisor - 1) + ((value + 1) % iDivisor); 226 } 227 } 228 229 }