/** * Obtains an instance defining a transition between two offsets. * * <p>Applications should normally obtain an instance from {@link ZoneRules}. This factory is only * intended for use when creating {@link ZoneRules}. * * @param transition the transition date-time at the transition, which never actually occurs, * expressed local to the before offset, not null * @param offsetBefore the offset before the transition, not null * @param offsetAfter the offset at and after the transition, not null * @return the transition, not null * @throws IllegalArgumentException if {@code offsetBefore} and {@code offsetAfter} are equal, or * {@code transition.getNano()} returns non-zero value */ public static ZoneOffsetTransition of( LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) { Objects.requireNonNull(transition, "transition"); Objects.requireNonNull(offsetBefore, "offsetBefore"); Objects.requireNonNull(offsetAfter, "offsetAfter"); if (offsetBefore.equals(offsetAfter)) { throw new IllegalArgumentException("Offsets must not be equal"); } if (transition.getNano() != 0) { throw new IllegalArgumentException("Nano-of-second must be zero"); } return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter); }
/** * Obtains an instance of {@code ZoneOffset} from a temporal object. * * <p>This obtains an offset based on the specified temporal. A {@code TemporalAccessor} * represents an arbitrary set of date and time information, which this factory converts to an * instance of {@code ZoneOffset}. * * <p>A {@code TemporalAccessor} represents some form of date and time information. This factory * converts the arbitrary temporal object to an instance of {@code ZoneOffset}. * * <p>The conversion uses the {@link TemporalQueries#offset()} query, which relies on extracting * the {@link ChronoField#OFFSET_SECONDS OFFSET_SECONDS} field. * * <p>This method matches the signature of the functional interface {@link TemporalQuery} allowing * it to be used in queries via method reference, {@code ZoneOffset::from}. * * @param temporal the temporal object to convert, not null * @return the zone-offset, not null * @throws DateTimeException if unable to convert to an {@code ZoneOffset} */ public static ZoneOffset from(TemporalAccessor temporal) { Objects.requireNonNull(temporal, "temporal"); ZoneOffset offset = temporal.query(TemporalQueries.offset()); if (offset == null) { throw new DateTimeException( "Unable to obtain ZoneOffset from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName()); } return offset; }
/** * Obtains an instance of {@code ZoneOffset} using the ID. * * <p>This method parses the string ID of a {@code ZoneOffset} to return an instance. The parsing * accepts all the formats generated by {@link #getId()}, plus some additional formats: * * <ul> * <li>{@code Z} - for UTC * <li>{@code +h} * <li>{@code +hh} * <li>{@code +hh:mm} * <li>{@code -hh:mm} * <li>{@code +hhmm} * <li>{@code -hhmm} * <li>{@code +hh:mm:ss} * <li>{@code -hh:mm:ss} * <li>{@code +hhmmss} * <li>{@code -hhmmss} * </ul> * * Note that ± means either the plus or minus symbol. * * <p>The ID of the returned offset will be normalized to one of the formats described by {@link * #getId()}. * * <p>The maximum supported range is from +18:00 to -18:00 inclusive. * * @param offsetId the offset ID, not null * @return the zone-offset, not null * @throws DateTimeException if the offset ID is invalid */ @SuppressWarnings("fallthrough") public static ZoneOffset of(String offsetId) { Objects.requireNonNull(offsetId, "offsetId"); // "Z" is always in the cache ZoneOffset offset = ID_CACHE.get(offsetId); if (offset != null) { return offset; } // parse - +h, +hh, +hhmm, +hh:mm, +hhmmss, +hh:mm:ss final int hours, minutes, seconds; switch (offsetId.length()) { case 2: offsetId = offsetId.charAt(0) + "0" + offsetId.charAt(1); // fallthru case 3: hours = parseNumber(offsetId, 1, false); minutes = 0; seconds = 0; break; case 5: hours = parseNumber(offsetId, 1, false); minutes = parseNumber(offsetId, 3, false); seconds = 0; break; case 6: hours = parseNumber(offsetId, 1, false); minutes = parseNumber(offsetId, 4, true); seconds = 0; break; case 7: hours = parseNumber(offsetId, 1, false); minutes = parseNumber(offsetId, 3, false); seconds = parseNumber(offsetId, 5, false); break; case 9: hours = parseNumber(offsetId, 1, false); minutes = parseNumber(offsetId, 4, true); seconds = parseNumber(offsetId, 7, true); break; default: throw new DateTimeException("Invalid ID for ZoneOffset, invalid format: " + offsetId); } char first = offsetId.charAt(0); if (first != '+' && first != '-') { throw new DateTimeException( "Invalid ID for ZoneOffset, plus/minus not found when expected: " + offsetId); } if (first == '-') { return ofHoursMinutesSeconds(-hours, -minutes, -seconds); } else { return ofHoursMinutesSeconds(hours, minutes, seconds); } }
/** * Loads and processes the Hijrah calendar properties file for this calendarType. The starting * Hijrah date and the corresponding ISO date are extracted and used to calculate the epochDate * offset. The version number is identified and ignored. Everything else is the data for a year * with containing the length of each of 12 months. * * @throws DateTimeException if initialization of the calendar data from the resource fails */ private void loadCalendarData() { try { String resourceName = calendarProperties.getProperty(PROP_PREFIX + typeId); Objects.requireNonNull( resourceName, "Resource missing for calendar: " + PROP_PREFIX + typeId); Properties props = readConfigProperties(resourceName); Map<Integer, int[]> years = new HashMap<>(); int minYear = Integer.MAX_VALUE; int maxYear = Integer.MIN_VALUE; String id = null; String type = null; String version = null; int isoStart = 0; for (Map.Entry<Object, Object> entry : props.entrySet()) { String key = (String) entry.getKey(); switch (key) { case KEY_ID: id = (String) entry.getValue(); break; case KEY_TYPE: type = (String) entry.getValue(); break; case KEY_VERSION: version = (String) entry.getValue(); break; case KEY_ISO_START: { int[] ymd = parseYMD((String) entry.getValue()); isoStart = (int) LocalDate.of(ymd[0], ymd[1], ymd[2]).toEpochDay(); break; } default: try { // Everything else is either a year or invalid int year = Integer.valueOf(key); int[] months = parseMonths((String) entry.getValue()); years.put(year, months); maxYear = Math.max(maxYear, year); minYear = Math.min(minYear, year); } catch (NumberFormatException nfe) { throw new IllegalArgumentException("bad key: " + key); } } } if (!getId().equals(id)) { throw new IllegalArgumentException("Configuration is for a different calendar: " + id); } if (!getCalendarType().equals(type)) { throw new IllegalArgumentException( "Configuration is for a different calendar type: " + type); } if (version == null || version.isEmpty()) { throw new IllegalArgumentException("Configuration does not contain a version"); } if (isoStart == 0) { throw new IllegalArgumentException("Configuration does not contain a ISO start date"); } // Now create and validate the array of epochDays indexed by epochMonth hijrahStartEpochMonth = minYear * 12; minEpochDay = isoStart; hijrahEpochMonthStartDays = createEpochMonths(minEpochDay, minYear, maxYear, years); maxEpochDay = hijrahEpochMonthStartDays[hijrahEpochMonthStartDays.length - 1]; // Compute the min and max year length in days. for (int year = minYear; year < maxYear; year++) { int length = getYearLength(year); minYearLength = Math.min(minYearLength, length); maxYearLength = Math.max(maxYearLength, length); } } catch (Exception ex) { // Log error and throw a DateTimeException PlatformLogger logger = PlatformLogger.getLogger("j86.java.time.chrono"); logger.severe("Unable to initialize Hijrah calendar proxy: " + typeId, ex); throw new DateTimeException("Unable to initialize HijrahCalendar: " + typeId, ex); } }