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