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 * Counterpart remainder datetime field to {@link DividedDateTimeField}. The 024 * field's unit duration is unchanged, but the range duration is scaled 025 * accordingly. 026 * <p> 027 * RemainderDateTimeField is thread-safe and immutable. 028 * 029 * @see DividedDateTimeField 030 * 031 * @author Brian S O'Neill 032 * @since 1.0 033 */ 034 public class RemainderDateTimeField extends DecoratedDateTimeField { 035 036 private static final long serialVersionUID = 5708241235177666790L; 037 038 // Shared with DividedDateTimeField. 039 final int iDivisor; 040 final DurationField iRangeField; 041 042 /** 043 * Constructor. 044 * 045 * @param field the field to wrap, like "year()". 046 * @param type the field type this field actually uses 047 * @param divisor divisor, such as 100 years in a century 048 * @throws IllegalArgumentException if divisor is less than two 049 */ 050 public RemainderDateTimeField(DateTimeField field, 051 DateTimeFieldType type, int divisor) { 052 super(field, type); 053 054 if (divisor < 2) { 055 throw new IllegalArgumentException("The divisor must be at least 2"); 056 } 057 058 DurationField rangeField = field.getDurationField(); 059 if (rangeField == null) { 060 iRangeField = null; 061 } else { 062 iRangeField = new ScaledDurationField( 063 rangeField, type.getRangeDurationType(), divisor); 064 } 065 066 iDivisor = divisor; 067 } 068 069 /** 070 * Construct a RemainderDateTimeField that compliments the given 071 * DividedDateTimeField. 072 * 073 * @param dividedField complimentary divided field, like "century()". 074 */ 075 public RemainderDateTimeField(DividedDateTimeField dividedField) { 076 this(dividedField, dividedField.getType()); 077 } 078 079 /** 080 * Construct a RemainderDateTimeField that compliments the given 081 * DividedDateTimeField. 082 * 083 * @param dividedField complimentary divided field, like "century()". 084 * @param type the field type this field actually uses 085 */ 086 public RemainderDateTimeField(DividedDateTimeField dividedField, DateTimeFieldType type) { 087 super(dividedField.getWrappedField(), type); 088 iDivisor = dividedField.iDivisor; 089 iRangeField = dividedField.iDurationField; 090 } 091 092 //----------------------------------------------------------------------- 093 /** 094 * Get the remainder from the specified time instant. 095 * 096 * @param instant the time instant in millis to query. 097 * @return the remainder extracted from the input. 098 */ 099 public int get(long instant) { 100 int value = getWrappedField().get(instant); 101 if (value >= 0) { 102 return value % iDivisor; 103 } else { 104 return (iDivisor - 1) + ((value + 1) % iDivisor); 105 } 106 } 107 108 /** 109 * Add the specified amount to the specified time instant, wrapping around 110 * within the remainder range if necessary. The amount added may be 111 * negative. 112 * 113 * @param instant the time instant in millis to update. 114 * @param amount the amount to add (can be negative). 115 * @return the updated time instant. 116 */ 117 public long addWrapField(long instant, int amount) { 118 return set(instant, FieldUtils.getWrappedValue(get(instant), amount, 0, iDivisor - 1)); 119 } 120 121 /** 122 * Set the specified amount of remainder units to the specified time instant. 123 * 124 * @param instant the time instant in millis to update. 125 * @param value value of remainder units to set. 126 * @return the updated time instant. 127 * @throws IllegalArgumentException if value is too large or too small. 128 */ 129 public long set(long instant, int value) { 130 FieldUtils.verifyValueBounds(this, value, 0, iDivisor - 1); 131 int divided = getDivided(getWrappedField().get(instant)); 132 return getWrappedField().set(instant, divided * iDivisor + value); 133 } 134 135 /** 136 * Returns a scaled version of the wrapped field's unit duration field. 137 */ 138 public DurationField getRangeDurationField() { 139 return iRangeField; 140 } 141 142 /** 143 * Get the minimum value for the field, which is always zero. 144 * 145 * @return the minimum value of zero. 146 */ 147 public int getMinimumValue() { 148 return 0; 149 } 150 151 /** 152 * Get the maximum value for the field, which is always one less than the 153 * divisor. 154 * 155 * @return the maximum value 156 */ 157 public int getMaximumValue() { 158 return iDivisor - 1; 159 } 160 161 public long roundFloor(long instant) { 162 return getWrappedField().roundFloor(instant); 163 } 164 165 public long roundCeiling(long instant) { 166 return getWrappedField().roundCeiling(instant); 167 } 168 169 public long roundHalfFloor(long instant) { 170 return getWrappedField().roundHalfFloor(instant); 171 } 172 173 public long roundHalfCeiling(long instant) { 174 return getWrappedField().roundHalfCeiling(instant); 175 } 176 177 public long roundHalfEven(long instant) { 178 return getWrappedField().roundHalfEven(instant); 179 } 180 181 public long remainder(long instant) { 182 return getWrappedField().remainder(instant); 183 } 184 185 /** 186 * Returns the divisor applied, in the field's units. 187 * 188 * @return the divisor 189 */ 190 public int getDivisor() { 191 return iDivisor; 192 } 193 194 private int getDivided(int value) { 195 if (value >= 0) { 196 return value / iDivisor; 197 } else { 198 return ((value + 1) / iDivisor) - 1; 199 } 200 } 201 202 }