private static ZoneOffsetTransitionRule toZoneOffsetTransitionRule(
            AnnualTimeZoneRule rule, int dstSavingMillisBefore) {
        DateTimeRule dateTimeRule = rule.getRule();
        // Calendar.JANUARY is 0, transform it into a proper Month.
        Month month = Month.JANUARY.plus(dateTimeRule.getRuleMonth());
        int dayOfMonthIndicator;
        // Calendar.SUNDAY is 1, transform it into a proper DayOfWeek.
        DayOfWeek dayOfWeek = DayOfWeek.SATURDAY.plus(dateTimeRule.getRuleDayOfWeek());
        switch (dateTimeRule.getDateRuleType()) {
            case DateTimeRule.DOM:
                // Transition always on a specific day of the month.
                dayOfMonthIndicator = dateTimeRule.getRuleDayOfMonth();
                dayOfWeek = null;
                break;
            case DateTimeRule.DOW_GEQ_DOM:
                // ICU representation matches java.time representation.
                dayOfMonthIndicator = dateTimeRule.getRuleDayOfMonth();
                break;
            case DateTimeRule.DOW_LEQ_DOM:
                // java.time uses a negative dayOfMonthIndicator to represent "Sun<=X" or "lastSun"
                // rules. ICU uses this constant and the normal day. So "lastSun" in January would
                // ruleDayOfMonth = 31 in ICU and dayOfMonthIndicator = -1 in java.time.
                dayOfMonthIndicator = -month.maxLength() + dateTimeRule.getRuleDayOfMonth() - 1;
                break;
            case DateTimeRule.DOW:
                // DOW is unspecified in the documentation and seems to never be used.
                throw new ZoneRulesException("Date rule type DOW is unsupported");
            default:
                throw new ZoneRulesException(
                        "Unexpected date rule type: " + dateTimeRule.getDateRuleType());
        }
        // Cast to int is save, as input is int.
        int secondOfDay = (int) TimeUnit.MILLISECONDS.toSeconds(dateTimeRule.getRuleMillisInDay());
        LocalTime time;
        boolean timeEndOfDay;
        if (secondOfDay == SECONDS_IN_DAY) {
            time = LocalTime.MIDNIGHT;
            timeEndOfDay = true;
        } else {
            time = LocalTime.ofSecondOfDay(secondOfDay);
            timeEndOfDay = false;
        }
        ZoneOffsetTransitionRule.TimeDefinition timeDefinition;
        switch (dateTimeRule.getTimeRuleType()) {
            case DateTimeRule.WALL_TIME:
                timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.WALL;
                break;
            case DateTimeRule.STANDARD_TIME:
                timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.STANDARD;
                break;
            case DateTimeRule.UTC_TIME:
                timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.UTC;
                break;
            default:
                throw new ZoneRulesException(
                        "Unexpected time rule type " + dateTimeRule.getTimeRuleType());
        }
        ZoneOffset standardOffset = millisToOffset(rule.getRawOffset());
        ZoneOffset offsetBefore = millisToOffset(rule.getRawOffset() + dstSavingMillisBefore);
        ZoneOffset offsetAfter = millisToOffset(
                rule.getRawOffset() + rule.getDSTSavings());
        return ZoneOffsetTransitionRule.of(
                month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay, timeDefinition,
                standardOffset, offsetBefore, offsetAfter);
    }
private static ZoneOffsetTransitionRule toZoneOffsetTransitionRule ( AnnualTimeZoneRule rule , int dstSavingMillisBefore ) { DateTimeRule dateTimeRule = rule . getRule ( ) ; Month month = Month . JANUARY . plus ( dateTimeRule . getRuleMonth ( ) ) ; int dayOfMonthIndicator ; DayOfWeek dayOfWeek = DayOfWeek . SATURDAY . plus ( dateTimeRule . getRuleDayOfWeek ( ) ) ; switch ( dateTimeRule . getDateRuleType ( ) ) { case DateTimeRule . DOM : dayOfMonthIndicator = dateTimeRule . getRuleDayOfMonth ( ) ; dayOfWeek = null ; break ; case DateTimeRule . DOW_GEQ_DOM : dayOfMonthIndicator = dateTimeRule . getRuleDayOfMonth ( ) ; break ; case DateTimeRule . DOW_LEQ_DOM : dayOfMonthIndicator = - month . maxLength ( ) + dateTimeRule . getRuleDayOfMonth ( ) - 1 ; break ; case DateTimeRule . DOW : throw new ZoneRulesException ( "Date rule type DOW is unsupported" ) ; default : throw new ZoneRulesException ( "Unexpected date rule type: " + dateTimeRule . getDateRuleType ( ) ) ; } int secondOfDay = ( int ) TimeUnit . MILLISECONDS . toSeconds ( dateTimeRule . getRuleMillisInDay ( ) ) ; LocalTime time ; boolean timeEndOfDay ; if ( secondOfDay == SECONDS_IN_DAY ) { time = LocalTime . MIDNIGHT ; timeEndOfDay = true ; } else { time = LocalTime . ofSecondOfDay ( secondOfDay ) ; timeEndOfDay = false ; } ZoneOffsetTransitionRule . TimeDefinition timeDefinition ; switch ( dateTimeRule . getTimeRuleType ( ) ) { case DateTimeRule . WALL_TIME : timeDefinition = ZoneOffsetTransitionRule . TimeDefinition . WALL ; break ; case DateTimeRule . STANDARD_TIME : timeDefinition = ZoneOffsetTransitionRule . TimeDefinition . STANDARD ; break ; case DateTimeRule . UTC_TIME : timeDefinition = ZoneOffsetTransitionRule . TimeDefinition . UTC ; break ; default : throw new ZoneRulesException ( "Unexpected time rule type " + dateTimeRule . getTimeRuleType ( ) ) ; } ZoneOffset standardOffset = millisToOffset ( rule . getRawOffset ( ) ) ; ZoneOffset offsetBefore = millisToOffset ( rule . getRawOffset ( ) + dstSavingMillisBefore ) ; ZoneOffset offsetAfter = millisToOffset ( rule . getRawOffset ( ) + rule . getDSTSavings ( ) ) ; return ZoneOffsetTransitionRule . of ( month , dayOfMonthIndicator , dayOfWeek , time , timeEndOfDay , timeDefinition , standardOffset , offsetBefore , offsetAfter ) ; }
Transform an {@link AnnualTimeZoneRule} into an equivalent {@link ZoneOffsetTransitionRule}.
This is only used for the "final rules".

@param rule
The rule to transform.
@param dstSavingMillisBefore
The DST offset before the first transition in milliseconds.