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) 2007-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.chrono; 63 64 import static java.time.temporal.ChronoField.INSTANT_SECONDS; 65 import static java.time.temporal.ChronoField.OFFSET_SECONDS; 66 import static java.time.temporal.ChronoUnit.NANOS; 67 68 import java.time.DateTimeException; 69 import java.time.Instant; 70 import java.time.LocalTime; 71 import java.time.ZoneId; 72 import java.time.ZoneOffset; 73 import java.time.ZonedDateTime; 74 import java.time.format.DateTimeFormatter; 75 import java.time.temporal.ChronoField; 76 import java.time.temporal.Temporal; 77 import java.time.temporal.TemporalAccessor; 78 import java.time.temporal.TemporalAdjuster; 79 import java.time.temporal.TemporalAmount; 80 import java.time.temporal.TemporalField; 81 import java.time.temporal.TemporalQuery; 82 import java.time.temporal.TemporalUnit; 83 import java.time.temporal.UnsupportedTemporalTypeException; 84 import java.time.temporal.ValueRange; 85 import java.util.Comparator; 86 import java.util.Objects; 87 88 /** 89 * A date-time with a time-zone in an arbitrary chronology, 90 * intended for advanced globalization use cases. 91 * <p> 92 * <b>Most applications should declare method signatures, fields and variables 93 * as {@link ZonedDateTime}, not this interface.</b> 94 * <p> 95 * A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time 96 * where the {@code Chronology chronology}, or calendar system, is pluggable. 97 * The date-time is defined in terms of fields expressed by {@link TemporalField}, 98 * where most common implementations are defined in {@link ChronoField}. 99 * The chronology defines how the calendar system operates and the meaning of 100 * the standard fields. 101 * 102 * <h3>When to use this interface</h3> 103 * The design of the API encourages the use of {@code ZonedDateTime} rather than this 104 * interface, even in the case where the application needs to deal with multiple 105 * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}. 106 * <p> 107 * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood 108 * before using this interface. 109 * 110 * <h3>Specification for implementors</h3> 111 * This interface must be implemented with care to ensure other classes operate correctly. 112 * All implementations that can be instantiated must be final, immutable and thread-safe. 113 * Subclasses should be Serializable wherever possible. 114 * 115 * @param <D> the concrete type for the date of this date-time 116 * @since 1.8 117 */ 118 public interface ChronoZonedDateTime<D extends ChronoLocalDate<D>> 119 extends Temporal, Comparable<ChronoZonedDateTime<?>> { 120 121 /** 122 * Gets a comparator that compares {@code ChronoZonedDateTime} in 123 * time-line order ignoring the chronology. 124 * <p> 125 * This comparator differs from the comparison in {@link #compareTo} in that it 126 * only compares the underlying instant and not the chronology. 127 * This allows dates in different calendar systems to be compared based 128 * on the position of the date-time on the instant time-line. 129 * The underlying comparison is equivalent to comparing the epoch-second and nano-of-second. 130 * 131 * @see #isAfter 132 * @see #isBefore 133 * @see #isEqual 134 */ 135 static Comparator<ChronoZonedDateTime<?>> timeLineOrder() { 136 return Chronology.INSTANT_ORDER; 137 } 138 139 //----------------------------------------------------------------------- 140 /** 141 * Obtains an instance of {@code ChronoZonedDateTime} from a temporal object. 142 * <p> 143 * This creates a zoned date-time based on the specified temporal. 144 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 145 * which this factory converts to an instance of {@code ChronoZonedDateTime}. 146 * <p> 147 * The conversion extracts and combines the chronology, date, time and zone 148 * from the temporal object. The behavior is equivalent to using 149 * {@link Chronology#zonedDateTime(TemporalAccessor)} with the extracted chronology. 150 * Implementations are permitted to perform optimizations such as accessing 151 * those fields that are equivalent to the relevant objects. 152 * <p> 153 * This method matches the signature of the functional interface {@link TemporalQuery} 154 * allowing it to be used as a query via method reference, {@code ChronoZonedDateTime::from}. 155 * 156 * @param temporal the temporal objec t to convert, not null 157 * @return the date-time, not null 158 * @throws DateTimeException if unable to convert to a {@code ChronoZonedDateTime} 159 * @see Chronology#zonedDateTime(TemporalAccessor) 160 */ 161 static ChronoZonedDateTime<?> from(TemporalAccessor temporal) { 162 if (temporal instanceof ChronoZonedDateTime) { 163 return (ChronoZonedDateTime<?>) temporal; 164 } 165 Chronology chrono = temporal.query(TemporalQuery.chronology()); 166 if (chrono == null) { 167 throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass()); 168 } 169 return chrono.zonedDateTime(temporal); 170 } 171 172 //----------------------------------------------------------------------- 173 @Override 174 default ValueRange range(TemporalField field) { 175 if (field instanceof ChronoField) { 176 if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) { 177 return field.range(); 178 } 179 return toLocalDateTime().range(field); 180 } 181 return field.rangeRefinedBy(this); 182 } 183 184 @Override 185 default int get(TemporalField field) { 186 if (field instanceof ChronoField) { 187 switch ((ChronoField) field) { 188 case INSTANT_SECONDS: 189 throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead"); 190 case OFFSET_SECONDS: 191 return getOffset().getTotalSeconds(); 192 } 193 return toLocalDateTime().get(field); 194 } 195 return Temporal.super.get(field); 196 } 197 198 @Override 199 default long getLong(TemporalField field) { 200 if (field instanceof ChronoField) { 201 switch ((ChronoField) field) { 202 case INSTANT_SECONDS: return toEpochSecond(); 203 case OFFSET_SECONDS: return getOffset().getTotalSeconds(); 204 } 205 return toLocalDateTime().getLong(field); 206 } 207 return field.getFrom(this); 208 } 209 210 /** 211 * Gets the local date part of this date-time. 212 * <p> 213 * This returns a local date with the same year, month and day 214 * as this date-time. 215 * 216 * @return the date part of this date-time, not null 217 */ 218 default D toLocalDate() { 219 return toLocalDateTime().toLocalDate(); 220 } 221 222 /** 223 * Gets the local time part of this date-time. 224 * <p> 225 * This returns a local time with the same hour, minute, second and 226 * nanosecond as this date-time. 227 * 228 * @return the time part of this date-time, not null 229 */ 230 default LocalTime toLocalTime() { 231 return toLocalDateTime().toLocalTime(); 232 } 233 234 /** 235 * Gets the local date-time part of this date-time. 236 * <p> 237 * This returns a local date with the same year, month and day 238 * as this date-time. 239 * 240 * @return the local date-time part of this date-time, not null 241 */ 242 ChronoLocalDateTime<D> toLocalDateTime(); 243 244 /** 245 * Gets the zone offset, such as '+01:00'. 246 * <p> 247 * This is the offset of the local date-time from UTC/Greenwich. 248 * 249 * @return the zone offset, not null 250 */ 251 ZoneOffset getOffset(); 252 253 /** 254 * Gets the zone ID, such as 'Europe/Paris'. 255 * <p> 256 * This returns the stored time-zone id used to determine the time-zone rules. 257 * 258 * @return the zone ID, not null 259 */ 260 ZoneId getZone(); 261 262 //----------------------------------------------------------------------- 263 /** 264 * Returns a copy of this date-time changing the zone offset to the 265 * earlier of the two valid offsets at a local time-line overlap. 266 * <p> 267 * This method only has any effect when the local time-line overlaps, such as 268 * at an autumn daylight savings cutover. In this scenario, there are two 269 * valid offsets for the local date-time. Calling this method will return 270 * a zoned date-time with the earlier of the two selected. 271 * <p> 272 * If this method is called when it is not an overlap, {@code this} 273 * is returned. 274 * <p> 275 * This instance is immutable and unaffected by this method call. 276 * 277 * @return a {@code ZoneChronoDateTime} based on this date-time with the earlier offset, not null 278 * @throws DateTimeException if no rules can be found for the zone 279 * @throws DateTimeException if no rules are valid for this date-time 280 */ 281 ChronoZonedDateTime<D> withEarlierOffsetAtOverlap(); 282 283 /** 284 * Returns a copy of this date-time changing the zone offset to the 285 * later of the two valid offsets at a local time-line overlap. 286 * <p> 287 * This method only has any effect when the local time-line overlaps, such as 288 * at an autumn daylight savings cutover. In this scenario, there are two 289 * valid offsets for the local date-time. Calling this method will return 290 * a zoned date-time with the later of the two selected. 291 * <p> 292 * If this method is called when it is not an overlap, {@code this} 293 * is returned. 294 * <p> 295 * This instance is immutable and unaffected by this method call. 296 * 297 * @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null 298 * @throws DateTimeException if no rules can be found for the zone 299 * @throws DateTimeException if no rules are valid for this date-time 300 */ 301 ChronoZonedDateTime<D> withLaterOffsetAtOverlap(); 302 303 /** 304 * Returns a copy of this ZonedDateTime with a different time-zone, 305 * retaining the local date-time if possible. 306 * <p> 307 * This method changes the time-zone and retains the local date-time. 308 * The local date-time is only changed if it is invalid for the new zone. 309 * <p> 310 * To change the zone and adjust the local date-time, 311 * use {@link #withZoneSameInstant(ZoneId)}. 312 * <p> 313 * This instance is immutable and unaffected by this method call. 314 * 315 * @param zone the time-zone to change to, not null 316 * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null 317 */ 318 ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone); 319 320 /** 321 * Returns a copy of this date-time with a different time-zone, 322 * retaining the instant. 323 * <p> 324 * This method changes the time-zone and retains the instant. 325 * This normally results in a change to the local date-time. 326 * <p> 327 * This method is based on retaining the same instant, thus gaps and overlaps 328 * in the local time-line have no effect on the result. 329 * <p> 330 * To change the offset while keeping the local time, 331 * use {@link #withZoneSameLocal(ZoneId)}. 332 * 333 * @param zone the time-zone to change to, not null 334 * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null 335 * @throws DateTimeException if the result exceeds the supported date range 336 */ 337 ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone); 338 339 @Override // Override to provide javadoc 340 boolean isSupported(TemporalField field); 341 342 //----------------------------------------------------------------------- 343 // override for covariant return type 344 /** 345 * {@inheritDoc} 346 * @throws DateTimeException {@inheritDoc} 347 * @throws ArithmeticException {@inheritDoc} 348 */ 349 @Override 350 default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) { 351 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.with(adjuster))); 352 } 353 354 /** 355 * {@inheritDoc} 356 * @throws DateTimeException {@inheritDoc} 357 * @throws ArithmeticException {@inheritDoc} 358 */ 359 @Override 360 ChronoZonedDateTime<D> with(TemporalField field, long newValue); 361 362 /** 363 * {@inheritDoc} 364 * @throws DateTimeException {@inheritDoc} 365 * @throws ArithmeticException {@inheritDoc} 366 */ 367 @Override 368 default ChronoZonedDateTime<D> plus(TemporalAmount amount) { 369 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.plus(amount))); 370 } 371 372 /** 373 * {@inheritDoc} 374 * @throws DateTimeException {@inheritDoc} 375 * @throws ArithmeticException {@inheritDoc} 376 */ 377 @Override 378 ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit); 379 380 /** 381 * {@inheritDoc} 382 * @throws DateTimeException {@inheritDoc} 383 * @throws ArithmeticException {@inheritDoc} 384 */ 385 @Override 386 default ChronoZonedDateTime<D> minus(TemporalAmount amount) { 387 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amount))); 388 } 389 390 /** 391 * {@inheritDoc} 392 * @throws DateTimeException {@inheritDoc} 393 * @throws ArithmeticException {@inheritDoc} 394 */ 395 @Override 396 default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) { 397 return (ChronoZonedDateTime<D>)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amountToSubtract, unit))); 398 } 399 400 //----------------------------------------------------------------------- 401 /** 402 * Queries this date-time using the specified query. 403 * <p> 404 * This queries this date-time using the specified query strategy object. 405 * The {@code TemporalQuery} object defines the logic to be used to 406 * obtain the result. Read the documentation of the query to understand 407 * what the result of this method will be. 408 * <p> 409 * The result of this method is obtained by invoking the 410 * {@link java.time.temporal.TemporalQuery#queryFrom(TemporalAccessor)} method on the 411 * specified query passing {@code this} as the argument. 412 * 413 * @param <R> the type of the result 414 * @param query the query to invoke, not null 415 * @return the query result, null may be returned (defined by the query) 416 * @throws DateTimeException if unable to query (defined by the query) 417 * @throws ArithmeticException if numeric overflow occurs (defined by the query) 418 */ 419 @SuppressWarnings("unchecked") 420 @Override 421 default <R> R query(TemporalQuery<R> query) { 422 if (query == TemporalQuery.zone() || query == TemporalQuery.zoneId()) { 423 return (R) getZone(); 424 } else if (query == TemporalQuery.offset()) { 425 return (R) getOffset(); 426 } else if (query == TemporalQuery.localTime()) { 427 return (R) toLocalTime(); 428 } else if (query == TemporalQuery.chronology()) { 429 return (R) toLocalDate().getChronology(); 430 } else if (query == TemporalQuery.precision()) { 431 return (R) NANOS; 432 } 433 // inline TemporalAccessor.super.query(query) as an optimization 434 // non-JDK classes are not permitted to make this optimization 435 return query.queryFrom(this); 436 } 437 438 /** 439 * Formats this date-time using the specified formatter. 440 * <p> 441 * This date-time will be passed to the formatter to produce a string. 442 * <p> 443 * The default implementation must behave as follows: 444 * <pre> 445 * return formatter.format(this); 446 * </pre> 447 * 448 * @param formatter the formatter to use, not null 449 * @return the formatted date-time string, not null 450 * @throws DateTimeException if an error occurs during printing 451 */ 452 default String format(DateTimeFormatter formatter) { 453 Objects.requireNonNull(formatter, "formatter"); 454 return formatter.format(this); 455 } 456 457 //----------------------------------------------------------------------- 458 /** 459 * Converts this date-time to an {@code Instant}. 460 * <p> 461 * This returns an {@code Instant} representing the same point on the 462 * time-line as this date-time. The calculation combines the 463 * {@linkplain #toLocalDateTime() local date-time} and 464 * {@linkplain #getOffset() offset}. 465 * 466 * @return an {@code Instant} representing the same instant, not null 467 */ 468 default Instant toInstant() { 469 return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano()); 470 } 471 472 /** 473 * Converts this date-time to the number of seconds from the epoch 474 * of 1970-01-01T00:00:00Z. 475 * <p> 476 * This uses the {@linkplain #toLocalDateTime() local date-time} and 477 * {@linkplain #getOffset() offset} to calculate the epoch-second value, 478 * which is the number of elapsed seconds from 1970-01-01T00:00:00Z. 479 * Instants on the time-line after the epoch are positive, earlier are negative. 480 * 481 * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z 482 */ 483 default long toEpochSecond() { 484 long epochDay = toLocalDate().toEpochDay(); 485 long secs = epochDay * 86400 + toLocalTime().toSecondOfDay(); 486 secs -= getOffset().getTotalSeconds(); 487 return secs; 488 } 489 490 //----------------------------------------------------------------------- 491 /** 492 * Compares this date-time to another date-time, including the chronology. 493 * <p> 494 * The comparison is based first on the instant, then on the local date-time, 495 * then on the zone ID, then on the chronology. 496 * It is "consistent with equals", as defined by {@link Comparable}. 497 * <p> 498 * If all the date-time objects being compared are in the same chronology, then the 499 * additional chronology stage is not required. 500 * <p> 501 * This default implementation performs the comparison defined above. 502 * 503 * @param other the other date-time to compare to, not null 504 * @return the comparator value, negative if less, positive if greater 505 */ 506 @Override 507 default int compareTo(ChronoZonedDateTime<?> other) { 508 int cmp = Long.compare(toEpochSecond(), other.toEpochSecond()); 509 if (cmp == 0) { 510 cmp = toLocalTime().getNano() - other.toLocalTime().getNano(); 511 if (cmp == 0) { 512 cmp = toLocalDateTime().compareTo(other.toLocalDateTime()); 513 if (cmp == 0) { 514 cmp = getZone().getId().compareTo(other.getZone().getId()); 515 if (cmp == 0) { 516 cmp = toLocalDate().getChronology().compareTo(other.toLocalDate().getChronology()); 517 } 518 } 519 } 520 } 521 return cmp; 522 } 523 524 /** 525 * Checks if the instant of this date-time is before that of the specified date-time. 526 * <p> 527 * This method differs from the comparison in {@link #compareTo} in that it 528 * only compares the instant of the date-time. This is equivalent to using 529 * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}. 530 * <p> 531 * This default implementation performs the comparison based on the epoch-second 532 * and nano-of-second. 533 * 534 * @param other the other date-time to compare to, not null 535 * @return true if this point is before the specified date-time 536 */ 537 default boolean isBefore(ChronoZonedDateTime<?> other) { 538 long thisEpochSec = toEpochSecond(); 539 long otherEpochSec = other.toEpochSecond(); 540 return thisEpochSec < otherEpochSec || 541 (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano()); 542 } 543 544 /** 545 * Checks if the instant of this date-time is after that of the specified date-time. 546 * <p> 547 * This method differs from the comparison in {@link #compareTo} in that it 548 * only compares the instant of the date-time. This is equivalent to using 549 * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}. 550 * <p> 551 * This default implementation performs the comparison based on the epoch-second 552 * and nano-of-second. 553 * 554 * @param other the other date-time to compare to, not null 555 * @return true if this is after the specified date-time 556 */ 557 default boolean isAfter(ChronoZonedDateTime<?> other) { 558 long thisEpochSec = toEpochSecond(); 559 long otherEpochSec = other.toEpochSecond(); 560 return thisEpochSec > otherEpochSec || 561 (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano()); 562 } 563 564 /** 565 * Checks if the instant of this date-time is equal to that of the specified date-time. 566 * <p> 567 * This method differs from the comparison in {@link #compareTo} and {@link #equals} 568 * in that it only compares the instant of the date-time. This is equivalent to using 569 * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}. 570 * <p> 571 * This default implementation performs the comparison based on the epoch-second 572 * and nano-of-second. 573 * 574 * @param other the other date-time to compare to, not null 575 * @return true if the instant equals the instant of the specified date-time 576 */ 577 default boolean isEqual(ChronoZonedDateTime<?> other) { 578 return toEpochSecond() == other.toEpochSecond() && 579 toLocalTime().getNano() == other.toLocalTime().getNano(); 580 } 581 582 //----------------------------------------------------------------------- 583 /** 584 * Checks if this date-time is equal to another date-time. 585 * <p> 586 * The comparison is based on the offset date-time and the zone. 587 * To compare for the same instant on the time-line, use {@link #compareTo}. 588 * Only objects of type {@code ChronoZonedDateTime} are compared, other types return false. 589 * 590 * @param obj the object to check, null returns false 591 * @return true if this is equal to the other date-time 592 */ 593 @Override 594 boolean equals(Object obj); 595 596 /** 597 * A hash code for this date-time. 598 * 599 * @return a suitable hash code 600 */ 601 @Override 602 int hashCode(); 603 604 //----------------------------------------------------------------------- 605 /** 606 * Outputs this date-time as a {@code String}. 607 * <p> 608 * The output will include the full zoned date-time. 609 * 610 * @return a string representation of this date-time, not null 611 */ 612 @Override 613 String toString(); 614 615 }