1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.format; 63 64 import static java.time.temporal.Adjusters.nextOrSame; 65 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; 66 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; 67 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; 68 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; 69 import static java.time.temporal.ChronoField.AMPM_OF_DAY; 70 import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; 71 import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; 72 import static java.time.temporal.ChronoField.DAY_OF_MONTH; 73 import static java.time.temporal.ChronoField.DAY_OF_WEEK; 74 import static java.time.temporal.ChronoField.DAY_OF_YEAR; 75 import static java.time.temporal.ChronoField.EPOCH_DAY; 76 import static java.time.temporal.ChronoField.EPOCH_MONTH; 77 import static java.time.temporal.ChronoField.ERA; 78 import static java.time.temporal.ChronoField.HOUR_OF_AMPM; 79 import static java.time.temporal.ChronoField.HOUR_OF_DAY; 80 import static java.time.temporal.ChronoField.MICRO_OF_DAY; 81 import static java.time.temporal.ChronoField.MICRO_OF_SECOND; 82 import static java.time.temporal.ChronoField.MILLI_OF_DAY; 83 import static java.time.temporal.ChronoField.MILLI_OF_SECOND; 84 import static java.time.temporal.ChronoField.MINUTE_OF_DAY; 85 import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; 86 import static java.time.temporal.ChronoField.MONTH_OF_YEAR; 87 import static java.time.temporal.ChronoField.NANO_OF_DAY; 88 import static java.time.temporal.ChronoField.NANO_OF_SECOND; 89 import static java.time.temporal.ChronoField.SECOND_OF_DAY; 90 import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; 91 import static java.time.temporal.ChronoField.YEAR; 92 import static java.time.temporal.ChronoField.YEAR_OF_ERA; 93 94 import java.time.DateTimeException; 95 import java.time.DayOfWeek; 96 import java.time.LocalDate; 97 import java.time.LocalTime; 98 import java.time.ZoneId; 99 import java.time.chrono.ChronoLocalDate; 100 import java.time.chrono.Chronology; 101 import java.time.chrono.Era; 102 import java.time.chrono.IsoChronology; 103 import java.time.chrono.JapaneseChronology; 104 import java.time.temporal.ChronoField; 105 import java.time.temporal.ChronoUnit; 106 import java.time.temporal.Queries; 107 import java.time.temporal.TemporalAccessor; 108 import java.time.temporal.TemporalField; 109 import java.time.temporal.TemporalQuery; 110 import java.util.EnumMap; 111 import java.util.HashMap; 112 import java.util.LinkedHashMap; 113 import java.util.List; 114 import java.util.Map; 115 import java.util.Objects; 116 117 /** 118 * Builder that can holds date and time fields and related date and time objects. 119 * <p> 120 * <b>This class still needs major revision before JDK1.8 ships.</b> 121 * <p> 122 * The builder is used to hold onto different elements of date and time. 123 * It holds two kinds of object: 124 * <p><ul> 125 * <li>a {@code Map} from {@link TemporalField} to {@code long} value, where the 126 * value may be outside the valid range for the field 127 * <li>a list of objects, such as {@code Chronology} or {@code ZoneId} 128 * </ul><p> 129 * 130 * <h3>Specification for implementors</h3> 131 * This class is mutable and not thread-safe. 132 * It should only be used from a single thread. 133 * 134 * @since 1.8 135 */ 136 final class DateTimeBuilder 137 implements TemporalAccessor, Cloneable { 138 139 /** 140 * The map of other fields. 141 */ 142 private Map<TemporalField, Long> otherFields; 143 /** 144 * The map of date-time fields. 145 */ 146 private final EnumMap<ChronoField, Long> standardFields = new EnumMap<ChronoField, Long>(ChronoField.class); 147 /** 148 * The chronology. 149 */ 150 private Chronology chrono; 151 /** 152 * The zone. 153 */ 154 private ZoneId zone; 155 /** 156 * The date. 157 */ 158 private LocalDate date; 159 /** 160 * The time. 161 */ 162 private LocalTime time; 163 164 //----------------------------------------------------------------------- 165 /** 166 * Creates an empty instance of the builder. 167 */ 168 public DateTimeBuilder() { 169 } 170 171 //----------------------------------------------------------------------- 172 private Long getFieldValue0(TemporalField field) { 173 if (field instanceof ChronoField) { 174 return standardFields.get(field); 175 } else if (otherFields != null) { 176 return otherFields.get(field); 177 } 178 return null; 179 } 180 181 /** 182 * Adds a field-value pair to the builder. 183 * <p> 184 * This adds a field to the builder. 185 * If the field is not already present, then the field-value pair is added to the map. 186 * If the field is already present and it has the same value as that specified, no action occurs. 187 * If the field is already present and it has a different value to that specified, then 188 * an exception is thrown. 189 * 190 * @param field the field to add, not null 191 * @param value the value to add, not null 192 * @return {@code this}, for method chaining 193 * @throws DateTimeException if the field is already present with a different value 194 */ 195 DateTimeBuilder addFieldValue(TemporalField field, long value) { 196 Objects.requireNonNull(field, "field"); 197 Long old = getFieldValue0(field); // check first for better error message 198 if (old != null && old.longValue() != value) { 199 throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value + ": " + this); 200 } 201 return putFieldValue0(field, value); 202 } 203 204 private DateTimeBuilder putFieldValue0(TemporalField field, long value) { 205 if (field instanceof ChronoField) { 206 standardFields.put((ChronoField) field, value); 207 } else { 208 if (otherFields == null) { 209 otherFields = new LinkedHashMap<TemporalField, Long>(); 210 } 211 otherFields.put(field, value); 212 } 213 return this; 214 } 215 216 //----------------------------------------------------------------------- 217 void addObject(Chronology chrono) { 218 this.chrono = chrono; 219 } 220 221 void addObject(ZoneId zone) { 222 this.zone = zone; 223 } 224 225 void addObject(LocalDate date) { 226 this.date = date; 227 } 228 229 void addObject(LocalTime time) { 230 this.time = time; 231 } 232 233 //----------------------------------------------------------------------- 234 /** 235 * Resolves the builder, evaluating the date and time. 236 * <p> 237 * This examines the contents of the builder and resolves it to produce the best 238 * available date and time, throwing an exception if a problem occurs. 239 * Calling this method changes the state of the builder. 240 * 241 * @return {@code this}, for method chaining 242 */ 243 DateTimeBuilder resolve() { 244 // handle standard fields 245 mergeDate(); 246 mergeTime(); 247 // TODO: cross validate remaining fields? 248 return this; 249 } 250 251 private void mergeDate() { 252 if (standardFields.containsKey(EPOCH_DAY)) { 253 checkDate(LocalDate.ofEpochDay(standardFields.remove(EPOCH_DAY))); 254 return; 255 } 256 257 Era era = null; 258 if (chrono == IsoChronology.INSTANCE) { 259 // normalize fields 260 if (standardFields.containsKey(EPOCH_MONTH)) { 261 long em = standardFields.remove(EPOCH_MONTH); 262 addFieldValue(MONTH_OF_YEAR, (em % 12) + 1); 263 addFieldValue(YEAR, (em / 12) + 1970); 264 } 265 } else { 266 // TODO: revisit EPOCH_MONTH calculation in non-ISO chronology 267 // Handle EPOCH_MONTH here for non-ISO Chronology 268 if (standardFields.containsKey(EPOCH_MONTH)) { 269 long em = standardFields.remove(EPOCH_MONTH); 270 ChronoLocalDate<?> chronoDate = chrono.date(LocalDate.ofEpochDay(0L)); 271 chronoDate = chronoDate.plus(em, ChronoUnit.MONTHS); 272 LocalDate date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 273 checkDate(date); 274 return; 275 } 276 List<Era> eras = chrono.eras(); 277 if (!eras.isEmpty()) { 278 if (standardFields.containsKey(ERA)) { 279 long index = standardFields.remove(ERA); 280 era = chrono.eraOf((int) index); 281 } else { 282 era = eras.get(eras.size() - 1); // current Era 283 } 284 if (standardFields.containsKey(YEAR_OF_ERA)) { 285 Long y = standardFields.remove(YEAR_OF_ERA); 286 putFieldValue0(YEAR, y); 287 } 288 } 289 290 } 291 292 // build date 293 if (standardFields.containsKey(YEAR)) { 294 if (standardFields.containsKey(MONTH_OF_YEAR)) { 295 if (standardFields.containsKey(DAY_OF_MONTH)) { 296 int y = Math.toIntExact(standardFields.remove(YEAR)); 297 int moy = Math.toIntExact(standardFields.remove(MONTH_OF_YEAR)); 298 int dom = Math.toIntExact(standardFields.remove(DAY_OF_MONTH)); 299 LocalDate date; 300 if (chrono == IsoChronology.INSTANCE) { 301 date = LocalDate.of(y, moy, dom); 302 } else { 303 ChronoLocalDate<?> chronoDate; 304 if (era == null) { 305 chronoDate = chrono.date(y, moy, dom); 306 } else { 307 chronoDate = era.date(y, moy, dom); 308 } 309 date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 310 } 311 checkDate(date); 312 return; 313 } 314 if (standardFields.containsKey(ALIGNED_WEEK_OF_MONTH)) { 315 if (standardFields.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) { 316 int y = Math.toIntExact(standardFields.remove(YEAR)); 317 int moy = Math.toIntExact(standardFields.remove(MONTH_OF_YEAR)); 318 int aw = Math.toIntExact(standardFields.remove(ALIGNED_WEEK_OF_MONTH)); 319 int ad = Math.toIntExact(standardFields.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH)); 320 LocalDate date; 321 if (chrono == IsoChronology.INSTANCE) { 322 date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7 + (ad - 1)); 323 } else { 324 ChronoLocalDate<?> chronoDate; 325 if (era == null) { 326 chronoDate = chrono.date(y, moy, 1); 327 } else { 328 chronoDate = era.date(y, moy, 1); 329 } 330 chronoDate = chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS); 331 date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 332 } 333 checkDate(date); 334 return; 335 } 336 if (standardFields.containsKey(DAY_OF_WEEK)) { 337 int y = Math.toIntExact(standardFields.remove(YEAR)); 338 int moy = Math.toIntExact(standardFields.remove(MONTH_OF_YEAR)); 339 int aw = Math.toIntExact(standardFields.remove(ALIGNED_WEEK_OF_MONTH)); 340 int dow = Math.toIntExact(standardFields.remove(DAY_OF_WEEK)); 341 LocalDate date; 342 if (chrono == IsoChronology.INSTANCE) { 343 date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7).with(nextOrSame(DayOfWeek.of(dow))); 344 } else { 345 ChronoLocalDate<?> chronoDate; 346 if (era == null) { 347 chronoDate = chrono.date(y, moy, 1); 348 } else { 349 chronoDate = era.date(y, moy, 1); 350 } 351 chronoDate = chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow))); 352 date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 353 } 354 checkDate(date); 355 return; 356 } 357 } 358 } 359 if (standardFields.containsKey(DAY_OF_YEAR)) { 360 int y = Math.toIntExact(standardFields.remove(YEAR)); 361 int doy = Math.toIntExact(standardFields.remove(DAY_OF_YEAR)); 362 LocalDate date; 363 if (chrono == IsoChronology.INSTANCE) { 364 date = LocalDate.ofYearDay(y, doy); 365 } else { 366 ChronoLocalDate<?> chronoDate; 367 if (era == null) { 368 chronoDate = chrono.dateYearDay(y, doy); 369 } else { 370 chronoDate = era.dateYearDay(y, doy); 371 } 372 date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 373 } 374 checkDate(date); 375 return; 376 } 377 if (standardFields.containsKey(ALIGNED_WEEK_OF_YEAR)) { 378 if (standardFields.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) { 379 int y = Math.toIntExact(standardFields.remove(YEAR)); 380 int aw = Math.toIntExact(standardFields.remove(ALIGNED_WEEK_OF_YEAR)); 381 int ad = Math.toIntExact(standardFields.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR)); 382 LocalDate date; 383 if (chrono == IsoChronology.INSTANCE) { 384 date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7 + (ad - 1)); 385 } else { 386 ChronoLocalDate<?> chronoDate; 387 if (era == null) { 388 chronoDate = chrono.dateYearDay(y, 1); 389 } else { 390 chronoDate = era.dateYearDay(y, 1); 391 } 392 chronoDate = chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS); 393 date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 394 } 395 checkDate(date); 396 return; 397 } 398 if (standardFields.containsKey(DAY_OF_WEEK)) { 399 int y = Math.toIntExact(standardFields.remove(YEAR)); 400 int aw = Math.toIntExact(standardFields.remove(ALIGNED_WEEK_OF_YEAR)); 401 int dow = Math.toIntExact(standardFields.remove(DAY_OF_WEEK)); 402 LocalDate date; 403 if (chrono == IsoChronology.INSTANCE) { 404 date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7).with(nextOrSame(DayOfWeek.of(dow))); 405 } else { 406 ChronoLocalDate<?> chronoDate; 407 if (era == null) { 408 chronoDate = chrono.dateYearDay(y, 1); 409 } else { 410 chronoDate = era.dateYearDay(y, 1); 411 } 412 chronoDate = chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow))); 413 date = LocalDate.ofEpochDay(chronoDate.toEpochDay()); 414 } 415 checkDate(date); 416 return; 417 } 418 } 419 } 420 } 421 422 private void checkDate(LocalDate date) { 423 addObject(date); 424 for (ChronoField field : standardFields.keySet()) { 425 long val1; 426 try { 427 val1 = date.getLong(field); 428 } catch (DateTimeException ex) { 429 continue; 430 } 431 Long val2 = standardFields.get(field); 432 if (val1 != val2) { 433 throw new DateTimeException("Conflict found: Field " + field + " " + val1 + " differs from " + field + " " + val2 + " derived from " + date); 434 } 435 } 436 } 437 438 private void mergeTime() { 439 if (standardFields.containsKey(CLOCK_HOUR_OF_DAY)) { 440 long ch = standardFields.remove(CLOCK_HOUR_OF_DAY); 441 addFieldValue(HOUR_OF_DAY, ch == 24 ? 0 : ch); 442 } 443 if (standardFields.containsKey(CLOCK_HOUR_OF_AMPM)) { 444 long ch = standardFields.remove(CLOCK_HOUR_OF_AMPM); 445 addFieldValue(HOUR_OF_AMPM, ch == 12 ? 0 : ch); 446 } 447 if (standardFields.containsKey(AMPM_OF_DAY) && standardFields.containsKey(HOUR_OF_AMPM)) { 448 long ap = standardFields.remove(AMPM_OF_DAY); 449 long hap = standardFields.remove(HOUR_OF_AMPM); 450 addFieldValue(HOUR_OF_DAY, ap * 12 + hap); 451 } 452 // if (timeFields.containsKey(HOUR_OF_DAY) && timeFields.containsKey(MINUTE_OF_HOUR)) { 453 // long hod = timeFields.remove(HOUR_OF_DAY); 454 // long moh = timeFields.remove(MINUTE_OF_HOUR); 455 // addFieldValue(MINUTE_OF_DAY, hod * 60 + moh); 456 // } 457 // if (timeFields.containsKey(MINUTE_OF_DAY) && timeFields.containsKey(SECOND_OF_MINUTE)) { 458 // long mod = timeFields.remove(MINUTE_OF_DAY); 459 // long som = timeFields.remove(SECOND_OF_MINUTE); 460 // addFieldValue(SECOND_OF_DAY, mod * 60 + som); 461 // } 462 if (standardFields.containsKey(NANO_OF_DAY)) { 463 long nod = standardFields.remove(NANO_OF_DAY); 464 addFieldValue(SECOND_OF_DAY, nod / 1000_000_000L); 465 addFieldValue(NANO_OF_SECOND, nod % 1000_000_000L); 466 } 467 if (standardFields.containsKey(MICRO_OF_DAY)) { 468 long cod = standardFields.remove(MICRO_OF_DAY); 469 addFieldValue(SECOND_OF_DAY, cod / 1000_000L); 470 addFieldValue(MICRO_OF_SECOND, cod % 1000_000L); 471 } 472 if (standardFields.containsKey(MILLI_OF_DAY)) { 473 long lod = standardFields.remove(MILLI_OF_DAY); 474 addFieldValue(SECOND_OF_DAY, lod / 1000); 475 addFieldValue(MILLI_OF_SECOND, lod % 1000); 476 } 477 if (standardFields.containsKey(SECOND_OF_DAY)) { 478 long sod = standardFields.remove(SECOND_OF_DAY); 479 addFieldValue(HOUR_OF_DAY, sod / 3600); 480 addFieldValue(MINUTE_OF_HOUR, (sod / 60) % 60); 481 addFieldValue(SECOND_OF_MINUTE, sod % 60); 482 } 483 if (standardFields.containsKey(MINUTE_OF_DAY)) { 484 long mod = standardFields.remove(MINUTE_OF_DAY); 485 addFieldValue(HOUR_OF_DAY, mod / 60); 486 addFieldValue(MINUTE_OF_HOUR, mod % 60); 487 } 488 489 // long sod = nod / 1000_000_000L; 490 // addFieldValue(HOUR_OF_DAY, sod / 3600); 491 // addFieldValue(MINUTE_OF_HOUR, (sod / 60) % 60); 492 // addFieldValue(SECOND_OF_MINUTE, sod % 60); 493 // addFieldValue(NANO_OF_SECOND, nod % 1000_000_000L); 494 if (standardFields.containsKey(MILLI_OF_SECOND) && standardFields.containsKey(MICRO_OF_SECOND)) { 495 long los = standardFields.remove(MILLI_OF_SECOND); 496 long cos = standardFields.get(MICRO_OF_SECOND); 497 addFieldValue(MICRO_OF_SECOND, los * 1000 + (cos % 1000)); 498 } 499 500 Long hod = standardFields.get(HOUR_OF_DAY); 501 Long moh = standardFields.get(MINUTE_OF_HOUR); 502 Long som = standardFields.get(SECOND_OF_MINUTE); 503 Long nos = standardFields.get(NANO_OF_SECOND); 504 if (hod != null) { 505 int hodVal = Math.toIntExact(hod); 506 if (moh != null) { 507 int mohVal = Math.toIntExact(moh); 508 if (som != null) { 509 int somVal = Math.toIntExact(som); 510 if (nos != null) { 511 int nosVal = Math.toIntExact(nos); 512 addObject(LocalTime.of(hodVal, mohVal, somVal, nosVal)); 513 } else { 514 addObject(LocalTime.of(hodVal, mohVal, somVal)); 515 } 516 } else { 517 addObject(LocalTime.of(hodVal, mohVal)); 518 } 519 } else { 520 addObject(LocalTime.of(hodVal, 0)); 521 } 522 } 523 } 524 525 //----------------------------------------------------------------------- 526 @Override 527 public boolean isSupported(TemporalField field) { 528 if (field == null) { 529 return false; 530 } 531 return standardFields.containsKey(field) || 532 (otherFields != null && otherFields.containsKey(field)) || 533 (date != null && date.isSupported(field)) || 534 (time != null && time.isSupported(field)); 535 } 536 537 @Override 538 public long getLong(TemporalField field) { 539 Objects.requireNonNull(field, "field"); 540 Long value = getFieldValue0(field); 541 if (value == null) { 542 if (date != null && date.isSupported(field)) { 543 return date.getLong(field); 544 } 545 if (time != null && time.isSupported(field)) { 546 return time.getLong(field); 547 } 548 throw new DateTimeException("Field not found: " + field); 549 } 550 return value; 551 } 552 553 @SuppressWarnings("unchecked") 554 @Override 555 public <R> R query(TemporalQuery<R> query) { 556 if (query == Queries.zoneId()) { 557 return (R) zone; 558 } else if (query == Queries.chronology()) { 559 return (R) chrono; 560 } else if (query == Queries.localDate()) { 561 return (R) date; 562 } else if (query == Queries.localTime()) { 563 return (R) time; 564 } else if (query == Queries.zone() || query == Queries.offset()) { 565 return query.queryFrom(this); 566 } else if (query == Queries.precision()) { 567 return null; // not a complete date/time 568 } 569 // inline TemporalAccessor.super.query(query) as an optimization 570 // non-JDK classes are not permitted to make this optimization 571 return query.queryFrom(this); 572 } 573 574 //----------------------------------------------------------------------- 575 @Override 576 public String toString() { 577 StringBuilder buf = new StringBuilder(128); 578 buf.append("DateTimeBuilder["); 579 Map<TemporalField, Long> fields = new HashMap<>(); 580 fields.putAll(standardFields); 581 if (otherFields != null) { 582 fields.putAll(otherFields); 583 } 584 if (fields.size() > 0) { 585 buf.append("fields=").append(fields); 586 } 587 buf.append(", ").append(chrono); 588 buf.append(", ").append(zone); 589 buf.append(", ").append(date); 590 buf.append(", ").append(time); 591 buf.append(']'); 592 return buf.toString(); 593 } 594 595 }