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 java.util.Locale; 019 020 import org.joda.time.DateTimeField; 021 import org.joda.time.DateTimeFieldType; 022 import org.joda.time.DurationField; 023 import org.joda.time.IllegalFieldValueException; 024 import org.joda.time.ReadablePartial; 025 026 /** 027 * BaseDateTimeField provides the common behaviour for DateTimeField 028 * implementations. 029 * <p> 030 * This class should generally not be used directly by API users. The 031 * DateTimeField class should be used when different kinds of DateTimeField 032 * objects are to be referenced. 033 * <p> 034 * BaseDateTimeField is thread-safe and immutable, and its subclasses must 035 * be as well. 036 * 037 * @author Brian S O'Neill 038 * @since 1.0 039 * @see DecoratedDateTimeField 040 */ 041 public abstract class BaseDateTimeField extends DateTimeField { 042 043 /** The field type. */ 044 private final DateTimeFieldType iType; 045 046 /** 047 * Constructor. 048 */ 049 protected BaseDateTimeField(DateTimeFieldType type) { 050 super(); 051 if (type == null) { 052 throw new IllegalArgumentException("The type must not be null"); 053 } 054 iType = type; 055 } 056 057 public final DateTimeFieldType getType() { 058 return iType; 059 } 060 061 public final String getName() { 062 return iType.getName(); 063 } 064 065 /** 066 * @return true always 067 */ 068 public final boolean isSupported() { 069 return true; 070 } 071 072 // Main access API 073 //------------------------------------------------------------------------ 074 /** 075 * Get the value of this field from the milliseconds. 076 * 077 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 078 * @return the value of the field, in the units of the field 079 */ 080 public abstract int get(long instant); 081 082 //----------------------------------------------------------------------- 083 /** 084 * Get the human-readable, text value of this field from the milliseconds. 085 * If the specified locale is null, the default locale is used. 086 * <p> 087 * The default implementation returns getAsText(get(instant), locale). 088 * 089 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 090 * @param locale the locale to use for selecting a text symbol, null means default 091 * @return the text value of the field 092 */ 093 public String getAsText(long instant, Locale locale) { 094 return getAsText(get(instant), locale); 095 } 096 097 /** 098 * Get the human-readable, text value of this field from the milliseconds. 099 * <p> 100 * The default implementation calls {@link #getAsText(long, Locale)}. 101 * 102 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 103 * @return the text value of the field 104 */ 105 public final String getAsText(long instant) { 106 return getAsText(instant, null); 107 } 108 109 /** 110 * Get the human-readable, text value of this field from a partial instant. 111 * If the specified locale is null, the default locale is used. 112 * <p> 113 * The default implementation returns getAsText(fieldValue, locale). 114 * 115 * @param partial the partial instant to query 116 * @param fieldValue the field value of this field, provided for performance 117 * @param locale the locale to use for selecting a text symbol, null for default 118 * @return the text value of the field 119 */ 120 public String getAsText(ReadablePartial partial, int fieldValue, Locale locale) { 121 return getAsText(fieldValue, locale); 122 } 123 124 /** 125 * Get the human-readable, text value of this field from a partial instant. 126 * If the specified locale is null, the default locale is used. 127 * <p> 128 * The default implementation calls {@link ReadablePartial#get(DateTimeFieldType)} 129 * and {@link #getAsText(ReadablePartial, int, Locale)}. 130 * 131 * @param partial the partial instant to query 132 * @param locale the locale to use for selecting a text symbol, null for default 133 * @return the text value of the field 134 */ 135 public final String getAsText(ReadablePartial partial, Locale locale) { 136 return getAsText(partial, partial.get(getType()), locale); 137 } 138 139 /** 140 * Get the human-readable, text value of this field from the field value. 141 * If the specified locale is null, the default locale is used. 142 * <p> 143 * The default implementation returns Integer.toString(get(instant)). 144 * <p> 145 * Note: subclasses that override this method should also override 146 * getMaximumTextLength. 147 * 148 * @param fieldValue the numeric value to convert to text 149 * @param locale the locale to use for selecting a text symbol, null for default 150 * @return the text value of the field 151 */ 152 public String getAsText(int fieldValue, Locale locale) { 153 return Integer.toString(fieldValue); 154 } 155 156 //----------------------------------------------------------------------- 157 /** 158 * Get the human-readable, short text value of this field from the milliseconds. 159 * If the specified locale is null, the default locale is used. 160 * <p> 161 * The default implementation returns getAsShortText(get(instant), locale). 162 * 163 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 164 * @param locale the locale to use for selecting a text symbol, null means default 165 * @return the text value of the field 166 */ 167 public String getAsShortText(long instant, Locale locale) { 168 return getAsShortText(get(instant), locale); 169 } 170 171 /** 172 * Get the human-readable, short text value of this field from the milliseconds. 173 * <p> 174 * The default implementation calls {@link #getAsShortText(long, Locale)}. 175 * 176 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 177 * @return the text value of the field 178 */ 179 public final String getAsShortText(long instant) { 180 return getAsShortText(instant, null); 181 } 182 183 /** 184 * Get the human-readable, short text value of this field from a partial instant. 185 * If the specified locale is null, the default locale is used. 186 * <p> 187 * The default implementation returns getAsShortText(fieldValue, locale). 188 * 189 * @param partial the partial instant to query 190 * @param fieldValue the field value of this field, provided for performance 191 * @param locale the locale to use for selecting a text symbol, null for default 192 * @return the text value of the field 193 */ 194 public String getAsShortText(ReadablePartial partial, int fieldValue, Locale locale) { 195 return getAsShortText(fieldValue, locale); 196 } 197 198 /** 199 * Get the human-readable, short text value of this field from a partial instant. 200 * If the specified locale is null, the default locale is used. 201 * <p> 202 * The default implementation calls {@link ReadablePartial#get(DateTimeFieldType)} 203 * and {@link #getAsText(ReadablePartial, int, Locale)}. 204 * 205 * @param partial the partial instant to query 206 * @param locale the locale to use for selecting a text symbol, null for default 207 * @return the text value of the field 208 */ 209 public final String getAsShortText(ReadablePartial partial, Locale locale) { 210 return getAsShortText(partial, partial.get(getType()), locale); 211 } 212 213 /** 214 * Get the human-readable, short text value of this field from the field value. 215 * If the specified locale is null, the default locale is used. 216 * <p> 217 * The default implementation returns getAsText(fieldValue, locale). 218 * <p> 219 * Note: subclasses that override this method should also override 220 * getMaximumShortTextLength. 221 * 222 * @param fieldValue the numeric value to convert to text 223 * @param locale the locale to use for selecting a text symbol, null for default 224 * @return the text value of the field 225 */ 226 public String getAsShortText(int fieldValue, Locale locale) { 227 return getAsText(fieldValue, locale); 228 } 229 230 //----------------------------------------------------------------------- 231 /** 232 * Adds a value (which may be negative) to the instant value, 233 * overflowing into larger fields if necessary. 234 * <p> 235 * The value will be added to this field. If the value is too large to be 236 * added solely to this field, larger fields will increase as required. 237 * Smaller fields should be unaffected, except where the result would be 238 * an invalid value for a smaller field. In this case the smaller field is 239 * adjusted to be in range. 240 * <p> 241 * For example, in the ISO chronology:<br> 242 * 2000-08-20 add six months is 2001-02-20<br> 243 * 2000-08-20 add twenty months is 2002-04-20<br> 244 * 2000-08-20 add minus nine months is 1999-11-20<br> 245 * 2001-01-31 add one month is 2001-02-28<br> 246 * 2001-01-31 add two months is 2001-03-31<br> 247 * 248 * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to 249 * @param value the value to add, in the units of the field 250 * @return the updated milliseconds 251 */ 252 public long add(long instant, int value) { 253 return getDurationField().add(instant, value); 254 } 255 256 /** 257 * Adds a value (which may be negative) to the instant value, 258 * overflowing into larger fields if necessary. 259 * 260 * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to 261 * @param value the long value to add, in the units of the field 262 * @return the updated milliseconds 263 * @throws IllegalArgumentException if value is too large 264 * @see #add(long,int) 265 */ 266 public long add(long instant, long value) { 267 return getDurationField().add(instant, value); 268 } 269 270 /** 271 * Adds a value (which may be negative) to the partial instant, 272 * throwing an exception if the maximum size of the instant is reached. 273 * <p> 274 * The value will be added to this field, overflowing into larger fields 275 * if necessary. Smaller fields should be unaffected, except where the 276 * result would be an invalid value for a smaller field. In this case the 277 * smaller field is adjusted to be in range. 278 * <p> 279 * Partial instants only contain some fields. This may result in a maximum 280 * possible value, such as TimeOfDay being limited to 23:59:59:999. If this 281 * limit is breached by the add an exception is thrown. 282 * <p> 283 * For example, in the ISO chronology:<br> 284 * 2000-08-20 add six months is 2000-02-20<br> 285 * 2000-08-20 add twenty months is 2000-04-20<br> 286 * 2000-08-20 add minus nine months is 2000-11-20<br> 287 * 2001-01-31 add one month is 2001-02-28<br> 288 * 2001-01-31 add two months is 2001-03-31<br> 289 * 290 * @param instant the partial instant 291 * @param fieldIndex the index of this field in the partial 292 * @param values the values of the partial instant which should be updated 293 * @param valueToAdd the value to add, in the units of the field 294 * @return the passed in values 295 * @throws IllegalArgumentException if the value is invalid or the maximum instant is reached 296 */ 297 public int[] add(ReadablePartial instant, int fieldIndex, int[] values, int valueToAdd) { 298 if (valueToAdd == 0) { 299 return values; 300 } 301 // there are more efficient algorithms than this (especially for time only fields) 302 // trouble is when dealing with days and months, so we use this technique of 303 // adding/removing one from the larger field at a time 304 DateTimeField nextField = null; 305 306 while (valueToAdd > 0) { 307 int max = getMaximumValue(instant, values); 308 long proposed = values[fieldIndex] + valueToAdd; 309 if (proposed <= max) { 310 values[fieldIndex] = (int) proposed; 311 break; 312 } 313 if (nextField == null) { 314 if (fieldIndex == 0) { 315 throw new IllegalArgumentException("Maximum value exceeded for add"); 316 } 317 nextField = instant.getField(fieldIndex - 1); 318 // test only works if this field is UTC (ie. local) 319 if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { 320 throw new IllegalArgumentException("Fields invalid for add"); 321 } 322 } 323 valueToAdd -= (max + 1) - values[fieldIndex]; // reduce the amount to add 324 values = nextField.add(instant, fieldIndex - 1, values, 1); // add 1 to next bigger field 325 values[fieldIndex] = getMinimumValue(instant, values); // reset this field to zero 326 } 327 while (valueToAdd < 0) { 328 int min = getMinimumValue(instant, values); 329 long proposed = values[fieldIndex] + valueToAdd; 330 if (proposed >= min) { 331 values[fieldIndex] = (int) proposed; 332 break; 333 } 334 if (nextField == null) { 335 if (fieldIndex == 0) { 336 throw new IllegalArgumentException("Maximum value exceeded for add"); 337 } 338 nextField = instant.getField(fieldIndex - 1); 339 if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { 340 throw new IllegalArgumentException("Fields invalid for add"); 341 } 342 } 343 valueToAdd -= (min - 1) - values[fieldIndex]; // reduce the amount to add 344 values = nextField.add(instant, fieldIndex - 1, values, -1); // subtract 1 from next bigger field 345 values[fieldIndex] = getMaximumValue(instant, values); // reset this field to max value 346 } 347 348 return set(instant, fieldIndex, values, values[fieldIndex]); // adjusts smaller fields 349 } 350 351 /** 352 * Adds a value (which may be negative) to the partial instant, 353 * wrapping the whole partial if the maximum size of the partial is reached. 354 * <p> 355 * The value will be added to this field, overflowing into larger fields 356 * if necessary. Smaller fields should be unaffected, except where the 357 * result would be an invalid value for a smaller field. In this case the 358 * smaller field is adjusted to be in range. 359 * <p> 360 * Partial instants only contain some fields. This may result in a maximum 361 * possible value, such as TimeOfDay normally being limited to 23:59:59:999. 362 * If ths limit is reached by the addition, this method will wrap back to 363 * 00:00:00.000. In fact, you would generally only use this method for 364 * classes that have a limitation such as this. 365 * <p> 366 * For example, in the ISO chronology:<br> 367 * 10:20:30 add 20 minutes is 10:40:30<br> 368 * 10:20:30 add 45 minutes is 11:05:30<br> 369 * 10:20:30 add 16 hours is 02:20:30<br> 370 * 371 * @param instant the partial instant 372 * @param fieldIndex the index of this field in the partial 373 * @param values the values of the partial instant which should be updated 374 * @param valueToAdd the value to add, in the units of the field 375 * @return the passed in values 376 * @throws IllegalArgumentException if the value is invalid or the maximum instant is reached 377 */ 378 public int[] addWrapPartial(ReadablePartial instant, int fieldIndex, int[] values, int valueToAdd) { 379 if (valueToAdd == 0) { 380 return values; 381 } 382 // there are more efficient algorithms than this (especially for time only fields) 383 // trouble is when dealing with days and months, so we use this technique of 384 // adding/removing one from the larger field at a time 385 DateTimeField nextField = null; 386 387 while (valueToAdd > 0) { 388 int max = getMaximumValue(instant, values); 389 long proposed = values[fieldIndex] + valueToAdd; 390 if (proposed <= max) { 391 values[fieldIndex] = (int) proposed; 392 break; 393 } 394 if (nextField == null) { 395 if (fieldIndex == 0) { 396 valueToAdd -= (max + 1) - values[fieldIndex]; 397 values[fieldIndex] = getMinimumValue(instant, values); 398 continue; 399 } 400 nextField = instant.getField(fieldIndex - 1); 401 // test only works if this field is UTC (ie. local) 402 if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { 403 throw new IllegalArgumentException("Fields invalid for add"); 404 } 405 } 406 valueToAdd -= (max + 1) - values[fieldIndex]; // reduce the amount to add 407 values = nextField.addWrapPartial(instant, fieldIndex - 1, values, 1); // add 1 to next bigger field 408 values[fieldIndex] = getMinimumValue(instant, values); // reset this field to zero 409 } 410 while (valueToAdd < 0) { 411 int min = getMinimumValue(instant, values); 412 long proposed = values[fieldIndex] + valueToAdd; 413 if (proposed >= min) { 414 values[fieldIndex] = (int) proposed; 415 break; 416 } 417 if (nextField == null) { 418 if (fieldIndex == 0) { 419 valueToAdd -= (min - 1) - values[fieldIndex]; 420 values[fieldIndex] = getMaximumValue(instant, values); 421 continue; 422 } 423 nextField = instant.getField(fieldIndex - 1); 424 if (getRangeDurationField().getType() != nextField.getDurationField().getType()) { 425 throw new IllegalArgumentException("Fields invalid for add"); 426 } 427 } 428 valueToAdd -= (min - 1) - values[fieldIndex]; // reduce the amount to add 429 values = nextField.addWrapPartial(instant, fieldIndex - 1, values, -1); // subtract 1 from next bigger field 430 values[fieldIndex] = getMaximumValue(instant, values); // reset this field to max value 431 } 432 433 return set(instant, fieldIndex, values, values[fieldIndex]); // adjusts smaller fields 434 } 435 436 /** 437 * Adds a value (which may be negative) to the instant value, 438 * wrapping within this field. 439 * <p> 440 * The value will be added to this field. If the value is too large to be 441 * added solely to this field then it wraps. Larger fields are always 442 * unaffected. Smaller fields should be unaffected, except where the 443 * result would be an invalid value for a smaller field. In this case the 444 * smaller field is adjusted to be in range. 445 * <p> 446 * For example, in the ISO chronology:<br> 447 * 2000-08-20 addWrapField six months is 2000-02-20<br> 448 * 2000-08-20 addWrapField twenty months is 2000-04-20<br> 449 * 2000-08-20 addWrapField minus nine months is 2000-11-20<br> 450 * 2001-01-31 addWrapField one month is 2001-02-28<br> 451 * 2001-01-31 addWrapField two months is 2001-03-31<br> 452 * <p> 453 * The default implementation internally calls set. Subclasses are 454 * encouraged to provide a more efficient implementation. 455 * 456 * @param instant the milliseconds from 1970-01-01T00:00:00Z to add to 457 * @param value the value to add, in the units of the field 458 * @return the updated milliseconds 459 */ 460 public long addWrapField(long instant, int value) { 461 int current = get(instant); 462 int wrapped = FieldUtils.getWrappedValue 463 (current, value, getMinimumValue(instant), getMaximumValue(instant)); 464 return set(instant, wrapped); 465 } 466 467 /** 468 * Adds a value (which may be negative) to the partial instant, 469 * wrapping within this field. 470 * <p> 471 * The value will be added to this field. If the value is too large to be 472 * added solely to this field then it wraps. Larger fields are always 473 * unaffected. Smaller fields should be unaffected, except where the 474 * result would be an invalid value for a smaller field. In this case the 475 * smaller field is adjusted to be in range. 476 * <p> 477 * For example, in the ISO chronology:<br> 478 * 2000-08-20 addWrapField six months is 2000-02-20<br> 479 * 2000-08-20 addWrapField twenty months is 2000-04-20<br> 480 * 2000-08-20 addWrapField minus nine months is 2000-11-20<br> 481 * 2001-01-31 addWrapField one month is 2001-02-28<br> 482 * 2001-01-31 addWrapField two months is 2001-03-31<br> 483 * <p> 484 * The default implementation internally calls set. Subclasses are 485 * encouraged to provide a more efficient implementation. 486 * 487 * @param instant the partial instant 488 * @param fieldIndex the index of this field in the instant 489 * @param values the values of the partial instant which should be updated 490 * @param valueToAdd the value to add, in the units of the field 491 * @return the passed in values 492 * @throws IllegalArgumentException if the value is invalid 493 */ 494 public int[] addWrapField(ReadablePartial instant, int fieldIndex, int[] values, int valueToAdd) { 495 int current = values[fieldIndex]; 496 int wrapped = FieldUtils.getWrappedValue 497 (current, valueToAdd, getMinimumValue(instant), getMaximumValue(instant)); 498 return set(instant, fieldIndex, values, wrapped); // adjusts smaller fields 499 } 500 501 //----------------------------------------------------------------------- 502 /** 503 * Computes the difference between two instants, as measured in the units 504 * of this field. Any fractional units are dropped from the result. Calling 505 * getDifference reverses the effect of calling add. In the following code: 506 * 507 * <pre> 508 * long instant = ... 509 * int v = ... 510 * int age = getDifference(add(instant, v), instant); 511 * </pre> 512 * 513 * The value 'age' is the same as the value 'v'. 514 * 515 * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to 516 * subtract from 517 * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to 518 * subtract off the minuend 519 * @return the difference in the units of this field 520 */ 521 public int getDifference(long minuendInstant, long subtrahendInstant) { 522 return getDurationField().getDifference(minuendInstant, subtrahendInstant); 523 } 524 525 /** 526 * Computes the difference between two instants, as measured in the units 527 * of this field. Any fractional units are dropped from the result. Calling 528 * getDifference reverses the effect of calling add. In the following code: 529 * 530 * <pre> 531 * long instant = ... 532 * long v = ... 533 * long age = getDifferenceAsLong(add(instant, v), instant); 534 * </pre> 535 * 536 * The value 'age' is the same as the value 'v'. 537 * 538 * @param minuendInstant the milliseconds from 1970-01-01T00:00:00Z to 539 * subtract from 540 * @param subtrahendInstant the milliseconds from 1970-01-01T00:00:00Z to 541 * subtract off the minuend 542 * @return the difference in the units of this field 543 */ 544 public long getDifferenceAsLong(long minuendInstant, long subtrahendInstant) { 545 return getDurationField().getDifferenceAsLong(minuendInstant, subtrahendInstant); 546 } 547 548 /** 549 * Sets a value in the milliseconds supplied. 550 * <p> 551 * The value of this field will be set. 552 * If the value is invalid, an exception if thrown. 553 * <p> 554 * If setting this field would make other fields invalid, then those fields 555 * may be changed. For example if the current date is the 31st January, and 556 * the month is set to February, the day would be invalid. Instead, the day 557 * would be changed to the closest value - the 28th/29th February as appropriate. 558 * 559 * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in 560 * @param value the value to set, in the units of the field 561 * @return the updated milliseconds 562 * @throws IllegalArgumentException if the value is invalid 563 */ 564 public abstract long set(long instant, int value); 565 566 /** 567 * Sets a value using the specified partial instant. 568 * <p> 569 * The value of this field (specified by the index) will be set. 570 * If the value is invalid, an exception if thrown. 571 * <p> 572 * If setting this field would make other fields invalid, then those fields 573 * may be changed. For example if the current date is the 31st January, and 574 * the month is set to February, the day would be invalid. Instead, the day 575 * would be changed to the closest value - the 28th/29th February as appropriate. 576 * 577 * @param partial the partial instant 578 * @param fieldIndex the index of this field in the instant 579 * @param values the values to update 580 * @param newValue the value to set, in the units of the field 581 * @return the updated values 582 * @throws IllegalArgumentException if the value is invalid 583 */ 584 public int[] set(ReadablePartial partial, int fieldIndex, int[] values, int newValue) { 585 FieldUtils.verifyValueBounds(this, newValue, getMinimumValue(partial, values), getMaximumValue(partial, values)); 586 values[fieldIndex] = newValue; 587 588 // may need to adjust smaller fields 589 for (int i = fieldIndex + 1; i < partial.size(); i++) { 590 DateTimeField field = partial.getField(i); 591 if (values[i] > field.getMaximumValue(partial, values)) { 592 values[i] = field.getMaximumValue(partial, values); 593 } 594 if (values[i] < field.getMinimumValue(partial, values)) { 595 values[i] = field.getMinimumValue(partial, values); 596 } 597 } 598 return values; 599 } 600 601 /** 602 * Sets a value in the milliseconds supplied from a human-readable, text value. 603 * If the specified locale is null, the default locale is used. 604 * <p> 605 * This implementation uses <code>convertText(String, Locale)</code> and 606 * {@link #set(long, int)}. 607 * <p> 608 * Note: subclasses that override this method should also override 609 * getAsText. 610 * 611 * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in 612 * @param text the text value to set 613 * @param locale the locale to use for selecting a text symbol, null for default 614 * @return the updated milliseconds 615 * @throws IllegalArgumentException if the text value is invalid 616 */ 617 public long set(long instant, String text, Locale locale) { 618 int value = convertText(text, locale); 619 return set(instant, value); 620 } 621 622 /** 623 * Sets a value in the milliseconds supplied from a human-readable, text value. 624 * <p> 625 * This implementation uses {@link #set(long, String, Locale)}. 626 * <p> 627 * Note: subclasses that override this method should also override getAsText. 628 * 629 * @param instant the milliseconds from 1970-01-01T00:00:00Z to set in 630 * @param text the text value to set 631 * @return the updated milliseconds 632 * @throws IllegalArgumentException if the text value is invalid 633 */ 634 public final long set(long instant, String text) { 635 return set(instant, text, null); 636 } 637 638 /** 639 * Sets a value in the milliseconds supplied from a human-readable, text value. 640 * If the specified locale is null, the default locale is used. 641 * <p> 642 * This implementation uses <code>convertText(String, Locale)</code> and 643 * {@link #set(ReadablePartial, int, int[], int)}. 644 * 645 * @param instant the partial instant 646 * @param fieldIndex the index of this field in the instant 647 * @param values the values of the partial instant which should be updated 648 * @param text the text value to set 649 * @param locale the locale to use for selecting a text symbol, null for default 650 * @return the passed in values 651 * @throws IllegalArgumentException if the text value is invalid 652 */ 653 public int[] set(ReadablePartial instant, int fieldIndex, int[] values, String text, Locale locale) { 654 int value = convertText(text, locale); 655 return set(instant, fieldIndex, values, value); 656 } 657 658 /** 659 * Convert the specified text and locale into a value. 660 * 661 * @param text the text to convert 662 * @param locale the locale to convert using 663 * @return the value extracted from the text 664 * @throws IllegalArgumentException if the text is invalid 665 */ 666 protected int convertText(String text, Locale locale) { 667 try { 668 return Integer.parseInt(text); 669 } catch (NumberFormatException ex) { 670 throw new IllegalFieldValueException(getType(), text); 671 } 672 } 673 674 // Extra information API 675 //------------------------------------------------------------------------ 676 /** 677 * Returns the duration per unit value of this field. For example, if this 678 * field represents "hour of day", then the unit duration is an hour. 679 * 680 * @return the duration of this field, or UnsupportedDurationField if field 681 * has no duration 682 */ 683 public abstract DurationField getDurationField(); 684 685 /** 686 * Returns the range duration of this field. For example, if this field 687 * represents "hour of day", then the range duration is a day. 688 * 689 * @return the range duration of this field, or null if field has no range 690 */ 691 public abstract DurationField getRangeDurationField(); 692 693 /** 694 * Returns whether this field is 'leap' for the specified instant. 695 * <p> 696 * For example, a leap year would return true, a non leap year would return 697 * false. 698 * <p> 699 * This implementation returns false. 700 * 701 * @return true if the field is 'leap' 702 */ 703 public boolean isLeap(long instant) { 704 return false; 705 } 706 707 /** 708 * Gets the amount by which this field is 'leap' for the specified instant. 709 * <p> 710 * For example, a leap year would return one, a non leap year would return 711 * zero. 712 * <p> 713 * This implementation returns zero. 714 */ 715 public int getLeapAmount(long instant) { 716 return 0; 717 } 718 719 /** 720 * If this field were to leap, then it would be in units described by the 721 * returned duration. If this field doesn't ever leap, null is returned. 722 * <p> 723 * This implementation returns null. 724 */ 725 public DurationField getLeapDurationField() { 726 return null; 727 } 728 729 /** 730 * Get the minimum allowable value for this field. 731 * 732 * @return the minimum valid value for this field, in the units of the 733 * field 734 */ 735 public abstract int getMinimumValue(); 736 737 /** 738 * Get the minimum value for this field evaluated at the specified time. 739 * <p> 740 * This implementation returns the same as {@link #getMinimumValue()}. 741 * 742 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 743 * @return the minimum value for this field, in the units of the field 744 */ 745 public int getMinimumValue(long instant) { 746 return getMinimumValue(); 747 } 748 749 /** 750 * Get the minimum value for this field evaluated at the specified instant. 751 * <p> 752 * This implementation returns the same as {@link #getMinimumValue()}. 753 * 754 * @param instant the partial instant to query 755 * @return the minimum value for this field, in the units of the field 756 */ 757 public int getMinimumValue(ReadablePartial instant) { 758 return getMinimumValue(); 759 } 760 761 /** 762 * Get the minimum value for this field using the partial instant and 763 * the specified values. 764 * <p> 765 * This implementation returns the same as {@link #getMinimumValue(ReadablePartial)}. 766 * 767 * @param instant the partial instant to query 768 * @param values the values to use 769 * @return the minimum value for this field, in the units of the field 770 */ 771 public int getMinimumValue(ReadablePartial instant, int[] values) { 772 return getMinimumValue(instant); 773 } 774 775 /** 776 * Get the maximum allowable value for this field. 777 * 778 * @return the maximum valid value for this field, in the units of the 779 * field 780 */ 781 public abstract int getMaximumValue(); 782 783 /** 784 * Get the maximum value for this field evaluated at the specified time. 785 * <p> 786 * This implementation returns the same as {@link #getMaximumValue()}. 787 * 788 * @param instant the milliseconds from 1970-01-01T00:00:00Z to query 789 * @return the maximum value for this field, in the units of the field 790 */ 791 public int getMaximumValue(long instant) { 792 return getMaximumValue(); 793 } 794 795 /** 796 * Get the maximum value for this field evaluated at the specified instant. 797 * <p> 798 * This implementation returns the same as {@link #getMaximumValue()}. 799 * 800 * @param instant the partial instant to query 801 * @return the maximum value for this field, in the units of the field 802 */ 803 public int getMaximumValue(ReadablePartial instant) { 804 return getMaximumValue(); 805 } 806 807 /** 808 * Get the maximum value for this field using the partial instant and 809 * the specified values. 810 * <p> 811 * This implementation returns the same as {@link #getMaximumValue(ReadablePartial)}. 812 * 813 * @param instant the partial instant to query 814 * @param values the values to use 815 * @return the maximum value for this field, in the units of the field 816 */ 817 public int getMaximumValue(ReadablePartial instant, int[] values) { 818 return getMaximumValue(instant); 819 } 820 821 /** 822 * Get the maximum text value for this field. The default implementation 823 * returns the equivalent of Integer.toString(getMaximumValue()).length(). 824 * 825 * @param locale the locale to use for selecting a text symbol 826 * @return the maximum text length 827 */ 828 public int getMaximumTextLength(Locale locale) { 829 int max = getMaximumValue(); 830 if (max >= 0) { 831 if (max < 10) { 832 return 1; 833 } else if (max < 100) { 834 return 2; 835 } else if (max < 1000) { 836 return 3; 837 } 838 } 839 return Integer.toString(max).length(); 840 } 841 842 /** 843 * Get the maximum short text value for this field. The default 844 * implementation returns getMaximumTextLength(). 845 * 846 * @param locale the locale to use for selecting a text symbol 847 * @return the maximum short text length 848 */ 849 public int getMaximumShortTextLength(Locale locale) { 850 return getMaximumTextLength(locale); 851 } 852 853 // Calculation API 854 //------------------------------------------------------------------------ 855 /** 856 * Round to the lowest whole unit of this field. After rounding, the value 857 * of this field and all fields of a higher magnitude are retained. The 858 * fractional millis that cannot be expressed in whole increments of this 859 * field are set to minimum. 860 * <p> 861 * For example, a datetime of 2002-11-02T23:34:56.789, rounded to the 862 * lowest whole hour is 2002-11-02T23:00:00.000. 863 * 864 * @param instant the milliseconds from 1970-01-01T00:00:00Z to round 865 * @return rounded milliseconds 866 */ 867 public abstract long roundFloor(long instant); 868 869 /** 870 * Round to the highest whole unit of this field. The value of this field 871 * and all fields of a higher magnitude may be incremented in order to 872 * achieve this result. The fractional millis that cannot be expressed in 873 * whole increments of this field are set to minimum. 874 * <p> 875 * For example, a datetime of 2002-11-02T23:34:56.789, rounded to the 876 * highest whole hour is 2002-11-03T00:00:00.000. 877 * <p> 878 * The default implementation calls roundFloor, and if the instant is 879 * modified as a result, adds one field unit. Subclasses are encouraged to 880 * provide a more efficient implementation. 881 * 882 * @param instant the milliseconds from 1970-01-01T00:00:00Z to round 883 * @return rounded milliseconds 884 */ 885 public long roundCeiling(long instant) { 886 long newInstant = roundFloor(instant); 887 if (newInstant != instant) { 888 instant = add(newInstant, 1); 889 } 890 return instant; 891 } 892 893 /** 894 * Round to the nearest whole unit of this field. If the given millisecond 895 * value is closer to the floor or is exactly halfway, this function 896 * behaves like roundFloor. If the millisecond value is closer to the 897 * ceiling, this function behaves like roundCeiling. 898 * 899 * @param instant the milliseconds from 1970-01-01T00:00:00Z to round 900 * @return rounded milliseconds 901 */ 902 public long roundHalfFloor(long instant) { 903 long floor = roundFloor(instant); 904 long ceiling = roundCeiling(instant); 905 906 long diffFromFloor = instant - floor; 907 long diffToCeiling = ceiling - instant; 908 909 if (diffFromFloor <= diffToCeiling) { 910 // Closer to the floor, or halfway - round floor 911 return floor; 912 } else { 913 return ceiling; 914 } 915 } 916 917 /** 918 * Round to the nearest whole unit of this field. If the given millisecond 919 * value is closer to the floor, this function behaves like roundFloor. If 920 * the millisecond value is closer to the ceiling or is exactly halfway, 921 * this function behaves like roundCeiling. 922 * 923 * @param instant the milliseconds from 1970-01-01T00:00:00Z to round 924 * @return rounded milliseconds 925 */ 926 public long roundHalfCeiling(long instant) { 927 long floor = roundFloor(instant); 928 long ceiling = roundCeiling(instant); 929 930 long diffFromFloor = instant - floor; 931 long diffToCeiling = ceiling - instant; 932 933 if (diffToCeiling <= diffFromFloor) { 934 // Closer to the ceiling, or halfway - round ceiling 935 return ceiling; 936 } else { 937 return floor; 938 } 939 } 940 941 /** 942 * Round to the nearest whole unit of this field. If the given millisecond 943 * value is closer to the floor, this function behaves like roundFloor. If 944 * the millisecond value is closer to the ceiling, this function behaves 945 * like roundCeiling. 946 * <p> 947 * If the millisecond value is exactly halfway between the floor and 948 * ceiling, the ceiling is chosen over the floor only if it makes this 949 * field's value even. 950 * 951 * @param instant the milliseconds from 1970-01-01T00:00:00Z to round 952 * @return rounded milliseconds 953 */ 954 public long roundHalfEven(long instant) { 955 long floor = roundFloor(instant); 956 long ceiling = roundCeiling(instant); 957 958 long diffFromFloor = instant - floor; 959 long diffToCeiling = ceiling - instant; 960 961 if (diffFromFloor < diffToCeiling) { 962 // Closer to the floor - round floor 963 return floor; 964 } else if (diffToCeiling < diffFromFloor) { 965 // Closer to the ceiling - round ceiling 966 return ceiling; 967 } else { 968 // Round to the instant that makes this field even. If both values 969 // make this field even (unlikely), favor the ceiling. 970 if ((get(ceiling) & 1) == 0) { 971 return ceiling; 972 } 973 return floor; 974 } 975 } 976 977 /** 978 * Returns the fractional duration milliseconds of this field. In other 979 * words, calling remainder returns the duration that roundFloor would 980 * subtract. 981 * <p> 982 * For example, on a datetime of 2002-11-02T23:34:56.789, the remainder by 983 * hour is 34 minutes and 56.789 seconds. 984 * <p> 985 * The default implementation computes 986 * <code>instant - roundFloor(instant)</code>. Subclasses are encouraged to 987 * provide a more efficient implementation. 988 * 989 * @param instant the milliseconds from 1970-01-01T00:00:00Z to get the 990 * remainder 991 * @return remainder duration, in milliseconds 992 */ 993 public long remainder(long instant) { 994 return instant - roundFloor(instant); 995 } 996 997 /** 998 * Get a suitable debug string. 999 * 1000 * @return debug string 1001 */ 1002 public String toString() { 1003 return "DateTimeField[" + getName() + ']'; 1004 } 1005 1006 }