453 }
454 ZoneOffset[] savOffsets = new ZoneOffset[savSize + 1];
455 for (int i = 0; i < savOffsets.length; i++) {
456 savOffsets[i] = Ser.readOffset(in);
457 }
458 int ruleSize = in.readByte();
459 ZoneOffsetTransitionRule[] rules = (ruleSize == 0) ?
460 EMPTY_LASTRULES : new ZoneOffsetTransitionRule[ruleSize];
461 for (int i = 0; i < ruleSize; i++) {
462 rules[i] = ZoneOffsetTransitionRule.readExternal(in);
463 }
464 return new ZoneRules(stdTrans, stdOffsets, savTrans, savOffsets, rules);
465 }
466
467 /**
468 * Checks of the zone rules are fixed, such that the offset never varies.
469 *
470 * @return true if the time-zone is fixed and the offset never changes
471 */
472 public boolean isFixedOffset() {
473 return savingsInstantTransitions.length == 0;
474 }
475
476 /**
477 * Gets the offset applicable at the specified instant in these rules.
478 * <p>
479 * The mapping from an instant to an offset is simple, there is only
480 * one valid offset for each instant.
481 * This method returns that offset.
482 *
483 * @param instant the instant to find the offset for, not null, but null
484 * may be ignored if the rules have a single offset for all instants
485 * @return the offset, not null
486 */
487 public ZoneOffset getOffset(Instant instant) {
488 if (savingsInstantTransitions.length == 0) {
489 return standardOffsets[0];
490 }
491 long epochSec = instant.getEpochSecond();
492 // check if using last rules
493 if (lastRules.length > 0 &&
494 epochSec > savingsInstantTransitions[savingsInstantTransitions.length - 1]) {
495 int year = findYear(epochSec, wallOffsets[wallOffsets.length - 1]);
496 ZoneOffsetTransition[] transArray = findTransitionArray(year);
497 ZoneOffsetTransition trans = null;
498 for (int i = 0; i < transArray.length; i++) {
499 trans = transArray[i];
500 if (epochSec < trans.toEpochSecond()) {
501 return trans.getOffsetBefore();
502 }
503 }
504 return trans.getOffsetAfter();
505 }
506
507 // using historic rules
508 int index = Arrays.binarySearch(savingsInstantTransitions, epochSec);
509 if (index < 0) {
623 * <pre>
624 * ZoneOffsetTransition trans = rules.getTransition(localDT);
625 * if (trans != null) {
626 * // Gap or Overlap: determine what to do from transition
627 * } else {
628 * // Normal case: only one valid offset
629 * zoneOffset = rule.getOffset(localDT);
630 * }
631 * </pre>
632 *
633 * @param localDateTime the local date-time to query for offset transition, not null, but null
634 * may be ignored if the rules have a single offset for all instants
635 * @return the offset transition, null if the local date-time is not in transition
636 */
637 public ZoneOffsetTransition getTransition(LocalDateTime localDateTime) {
638 Object info = getOffsetInfo(localDateTime);
639 return (info instanceof ZoneOffsetTransition ? (ZoneOffsetTransition) info : null);
640 }
641
642 private Object getOffsetInfo(LocalDateTime dt) {
643 if (savingsInstantTransitions.length == 0) {
644 return standardOffsets[0];
645 }
646 // check if using last rules
647 if (lastRules.length > 0 &&
648 dt.isAfter(savingsLocalTransitions[savingsLocalTransitions.length - 1])) {
649 ZoneOffsetTransition[] transArray = findTransitionArray(dt.getYear());
650 Object info = null;
651 for (ZoneOffsetTransition trans : transArray) {
652 info = findOffsetInfo(dt, trans);
653 if (info instanceof ZoneOffsetTransition || info.equals(trans.getOffsetBefore())) {
654 return info;
655 }
656 }
657 return info;
658 }
659
660 // using historic rules
661 int index = Arrays.binarySearch(savingsLocalTransitions, dt);
662 if (index == -1) {
663 // before first transition
664 return wallOffsets[0];
739 }
740 if (year < LAST_CACHED_YEAR) {
741 lastRulesCache.putIfAbsent(yearObj, transArray);
742 }
743 return transArray;
744 }
745
746 /**
747 * Gets the standard offset for the specified instant in this zone.
748 * <p>
749 * This provides access to historic information on how the standard offset
750 * has changed over time.
751 * The standard offset is the offset before any daylight saving time is applied.
752 * This is typically the offset applicable during winter.
753 *
754 * @param instant the instant to find the offset information for, not null, but null
755 * may be ignored if the rules have a single offset for all instants
756 * @return the standard offset, not null
757 */
758 public ZoneOffset getStandardOffset(Instant instant) {
759 if (savingsInstantTransitions.length == 0) {
760 return standardOffsets[0];
761 }
762 long epochSec = instant.getEpochSecond();
763 int index = Arrays.binarySearch(standardTransitions, epochSec);
764 if (index < 0) {
765 // switch negative insert position to start of matched range
766 index = -index - 2;
767 }
768 return standardOffsets[index + 1];
769 }
770
771 /**
772 * Gets the amount of daylight savings in use for the specified instant in this zone.
773 * <p>
774 * This provides access to historic information on how the amount of daylight
775 * savings has changed over time.
776 * This is the difference between the standard offset and the actual offset.
777 * Typically the amount is zero during winter and one hour during summer.
778 * Time-zones are second-based, so the nanosecond part of the duration will be zero.
779 * <p>
780 * This default implementation calculates the duration from the
781 * {@link #getOffset(java.time.Instant) actual} and
782 * {@link #getStandardOffset(java.time.Instant) standard} offsets.
783 *
784 * @param instant the instant to find the daylight savings for, not null, but null
785 * may be ignored if the rules have a single offset for all instants
786 * @return the difference between the standard and actual offset, not null
787 */
788 public Duration getDaylightSavings(Instant instant) {
789 if (savingsInstantTransitions.length == 0) {
790 return Duration.ZERO;
791 }
792 ZoneOffset standardOffset = getStandardOffset(instant);
793 ZoneOffset actualOffset = getOffset(instant);
794 return Duration.ofSeconds(actualOffset.getTotalSeconds() - standardOffset.getTotalSeconds());
795 }
796
797 /**
798 * Checks if the specified instant is in daylight savings.
799 * <p>
800 * This checks if the standard offset and the actual offset are the same
801 * for the specified instant.
802 * If they are not, it is assumed that daylight savings is in operation.
803 * <p>
804 * This default implementation compares the {@link #getOffset(java.time.Instant) actual}
805 * and {@link #getStandardOffset(java.time.Instant) standard} offsets.
806 *
807 * @param instant the instant to find the offset information for, not null, but null
808 * may be ignored if the rules have a single offset for all instants
809 * @return the standard offset, not null
|
453 }
454 ZoneOffset[] savOffsets = new ZoneOffset[savSize + 1];
455 for (int i = 0; i < savOffsets.length; i++) {
456 savOffsets[i] = Ser.readOffset(in);
457 }
458 int ruleSize = in.readByte();
459 ZoneOffsetTransitionRule[] rules = (ruleSize == 0) ?
460 EMPTY_LASTRULES : new ZoneOffsetTransitionRule[ruleSize];
461 for (int i = 0; i < ruleSize; i++) {
462 rules[i] = ZoneOffsetTransitionRule.readExternal(in);
463 }
464 return new ZoneRules(stdTrans, stdOffsets, savTrans, savOffsets, rules);
465 }
466
467 /**
468 * Checks of the zone rules are fixed, such that the offset never varies.
469 *
470 * @return true if the time-zone is fixed and the offset never changes
471 */
472 public boolean isFixedOffset() {
473 return standardOffsets[0] == wallOffsets[0] &&
474 standardTransitions.length == 0 &&
475 savingsInstantTransitions.length == 0 &&
476 lastRules.length == 0;
477 }
478
479 /**
480 * Gets the offset applicable at the specified instant in these rules.
481 * <p>
482 * The mapping from an instant to an offset is simple, there is only
483 * one valid offset for each instant.
484 * This method returns that offset.
485 *
486 * @param instant the instant to find the offset for, not null, but null
487 * may be ignored if the rules have a single offset for all instants
488 * @return the offset, not null
489 */
490 public ZoneOffset getOffset(Instant instant) {
491 if (savingsInstantTransitions.length == 0) {
492 return wallOffsets[0];
493 }
494 long epochSec = instant.getEpochSecond();
495 // check if using last rules
496 if (lastRules.length > 0 &&
497 epochSec > savingsInstantTransitions[savingsInstantTransitions.length - 1]) {
498 int year = findYear(epochSec, wallOffsets[wallOffsets.length - 1]);
499 ZoneOffsetTransition[] transArray = findTransitionArray(year);
500 ZoneOffsetTransition trans = null;
501 for (int i = 0; i < transArray.length; i++) {
502 trans = transArray[i];
503 if (epochSec < trans.toEpochSecond()) {
504 return trans.getOffsetBefore();
505 }
506 }
507 return trans.getOffsetAfter();
508 }
509
510 // using historic rules
511 int index = Arrays.binarySearch(savingsInstantTransitions, epochSec);
512 if (index < 0) {
626 * <pre>
627 * ZoneOffsetTransition trans = rules.getTransition(localDT);
628 * if (trans != null) {
629 * // Gap or Overlap: determine what to do from transition
630 * } else {
631 * // Normal case: only one valid offset
632 * zoneOffset = rule.getOffset(localDT);
633 * }
634 * </pre>
635 *
636 * @param localDateTime the local date-time to query for offset transition, not null, but null
637 * may be ignored if the rules have a single offset for all instants
638 * @return the offset transition, null if the local date-time is not in transition
639 */
640 public ZoneOffsetTransition getTransition(LocalDateTime localDateTime) {
641 Object info = getOffsetInfo(localDateTime);
642 return (info instanceof ZoneOffsetTransition ? (ZoneOffsetTransition) info : null);
643 }
644
645 private Object getOffsetInfo(LocalDateTime dt) {
646 if (savingsLocalTransitions.length == 0) {
647 return wallOffsets[0];
648 }
649 // check if using last rules
650 if (lastRules.length > 0 &&
651 dt.isAfter(savingsLocalTransitions[savingsLocalTransitions.length - 1])) {
652 ZoneOffsetTransition[] transArray = findTransitionArray(dt.getYear());
653 Object info = null;
654 for (ZoneOffsetTransition trans : transArray) {
655 info = findOffsetInfo(dt, trans);
656 if (info instanceof ZoneOffsetTransition || info.equals(trans.getOffsetBefore())) {
657 return info;
658 }
659 }
660 return info;
661 }
662
663 // using historic rules
664 int index = Arrays.binarySearch(savingsLocalTransitions, dt);
665 if (index == -1) {
666 // before first transition
667 return wallOffsets[0];
742 }
743 if (year < LAST_CACHED_YEAR) {
744 lastRulesCache.putIfAbsent(yearObj, transArray);
745 }
746 return transArray;
747 }
748
749 /**
750 * Gets the standard offset for the specified instant in this zone.
751 * <p>
752 * This provides access to historic information on how the standard offset
753 * has changed over time.
754 * The standard offset is the offset before any daylight saving time is applied.
755 * This is typically the offset applicable during winter.
756 *
757 * @param instant the instant to find the offset information for, not null, but null
758 * may be ignored if the rules have a single offset for all instants
759 * @return the standard offset, not null
760 */
761 public ZoneOffset getStandardOffset(Instant instant) {
762 if (standardTransitions.length == 0) {
763 return standardOffsets[0];
764 }
765 long epochSec = instant.getEpochSecond();
766 int index = Arrays.binarySearch(standardTransitions, epochSec);
767 if (index < 0) {
768 // switch negative insert position to start of matched range
769 index = -index - 2;
770 }
771 return standardOffsets[index + 1];
772 }
773
774 /**
775 * Gets the amount of daylight savings in use for the specified instant in this zone.
776 * <p>
777 * This provides access to historic information on how the amount of daylight
778 * savings has changed over time.
779 * This is the difference between the standard offset and the actual offset.
780 * Typically the amount is zero during winter and one hour during summer.
781 * Time-zones are second-based, so the nanosecond part of the duration will be zero.
782 * <p>
783 * This default implementation calculates the duration from the
784 * {@link #getOffset(java.time.Instant) actual} and
785 * {@link #getStandardOffset(java.time.Instant) standard} offsets.
786 *
787 * @param instant the instant to find the daylight savings for, not null, but null
788 * may be ignored if the rules have a single offset for all instants
789 * @return the difference between the standard and actual offset, not null
790 */
791 public Duration getDaylightSavings(Instant instant) {
792 if (isFixedOffset()) {
793 return Duration.ZERO;
794 }
795 ZoneOffset standardOffset = getStandardOffset(instant);
796 ZoneOffset actualOffset = getOffset(instant);
797 return Duration.ofSeconds(actualOffset.getTotalSeconds() - standardOffset.getTotalSeconds());
798 }
799
800 /**
801 * Checks if the specified instant is in daylight savings.
802 * <p>
803 * This checks if the standard offset and the actual offset are the same
804 * for the specified instant.
805 * If they are not, it is assumed that daylight savings is in operation.
806 * <p>
807 * This default implementation compares the {@link #getOffset(java.time.Instant) actual}
808 * and {@link #getStandardOffset(java.time.Instant) standard} offsets.
809 *
810 * @param instant the instant to find the offset information for, not null, but null
811 * may be ignored if the rules have a single offset for all instants
812 * @return the standard offset, not null
|