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