Java tutorial
/** * Copyright 2011 Caleb Richardson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.outerspacecat.icalendar; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import java.io.Serializable; import java.time.ZoneOffset; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; /** * A representation of an iCalendar utc-offset type defined by <a * href="http://tools.ietf.org/html/rfc5545">RFC 5545</a>. * <p> * This class adds the additional restriction of requiring the offset to be * between -180000 and +180000, inclusive, to comply with * {@link java.time.ZoneOffset}. * * @author Caleb Richardson */ @Immutable @ThreadSafe public final class UtcOffsetType implements HasICalendar, Serializable { private final static long serialVersionUID = 1L; private final static Pattern OFFSET_PATTERN = Pattern.compile("(\\-|\\+)(\\d\\d)(\\d\\d)(\\d\\d)?"); public final static UtcOffsetType ZERO = new UtcOffsetType(false, 0, 0, 0); private final boolean negative; private final int hours; private final int minutes; private final int seconds; /** * Creates a new utc-offset. A negative zero utc-offset is not allowed * (-000000). The allowable range is -180000 to +180000, inclusive. * * @param negative whether or not the utc-offset is negative * @param hours the hours of the utc-offset. Must be >= 0 and <= 18. * @param minutes the minutes of the utc-offset. Must be >= 0 and <= 59. * @param seconds the seconds of the utc-offset. Must be >= 0 and <= 59. */ public UtcOffsetType(final boolean negative, final int hours, final int minutes, final int seconds) { Preconditions.checkArgument(hours >= 0 && hours <= 18, "invalid hours: " + hours); Preconditions.checkArgument(minutes >= 0 && minutes <= 59, "invalid minutes: " + minutes); Preconditions.checkArgument(seconds >= 0 && seconds <= 59, "invalid seconds: " + seconds); if (negative && hours == 0 && minutes == 0 && seconds == 0) throw new IllegalArgumentException("cannot have negative zero utc-offset"); int totalSeconds = hours * 3600 + minutes * 60 + seconds; if (totalSeconds > 64800) throw new IllegalArgumentException("offset must be within -180000 and +180000, inclusive"); this.negative = negative; this.hours = hours; this.minutes = minutes; this.seconds = seconds; } /** * Parses a utc-offset. * * @param seq the value to parse. Must be non {@code null}. * @return an utc-offset. Never {@code null}. * @throws CalendarParseException if {@code seq} does not represent a valid * utc-offset */ public static UtcOffsetType parse(final CharSequence seq) throws CalendarParseException { Preconditions.checkNotNull(seq, "value required"); Matcher m = OFFSET_PATTERN.matcher(seq); if (!m.matches()) throw new CalendarParseException("invalid utc-offset: " + seq); try { int hours = Integer.parseInt(m.group(2)); int minutes = Integer.parseInt(m.group(3)); int seconds = m.group(4) != null ? Integer.parseInt(m.group(4)) : 0; if (minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) throw new CalendarParseException("invalid utc-offset: " + seq); int total = hours * 3600 + minutes * 60 + seconds; if (total > 64800) throw new CalendarParseException("invalid utc-offset: " + seq); return new UtcOffsetType(m.group(1).equals("-"), hours, minutes, seconds); } catch (NumberFormatException e) { throw new CalendarParseException("invalid utc-offset: " + seq); } } /** * Returns whether or not this utc-offset is negative. * * @return whether or not this utc-offset is negative */ public boolean isNegative() { return negative; } /** * Returns the number of hours in this utc-offset. * * @return the number of hours in this utc-offset. >= 0 and <= 18. */ public int getHours() { return hours; } /** * Returns the number of minutes in this utc-offset. * * @return the number of minutes in this utc-offset. >= 0 and <= 59. */ public int getMinutes() { return minutes; } /** * Returns the number of seconds in this utc-offset. * * @return the number of seconds in this utc-offset. >= 0 and <= 59. */ public int getSeconds() { return seconds; } /** * Returns the number of milliseconds in this utc-offset. Assumes 60 minute * hours and 60 second minutes. * * @return the number of milliseconds in this utc-offset. >= 0. */ public int getMillis() { return getHours() * 3600000 + getMinutes() * 60000 + getSeconds() * 1000; } /** * Returns a {@link ZoneOffset}. * * @return a {@link ZoneOffset}. Never {@code null}. */ public ZoneOffset getZoneOffset() { return ZoneOffset.ofHoursMinutesSeconds(getHours() * (isNegative() ? -1 : 1), getMinutes(), getSeconds()); } @Override public int hashCode() { return Objects.hashCode(isNegative(), getHours(), getMinutes(), getSeconds()); } @Override public boolean equals(final Object obj) { return obj instanceof UtcOffsetType && isNegative() == ((UtcOffsetType) obj).isNegative() && getHours() == ((UtcOffsetType) obj).getHours() && getMinutes() == ((UtcOffsetType) obj).getMinutes() && getSeconds() == ((UtcOffsetType) obj).getSeconds(); } @Override public String toString() { return toICalendar(); } @Override public String toICalendar() { return (isNegative() ? "-" : "+") + Strings.padStart(String.valueOf(getHours()), 2, '0') + Strings.padStart(String.valueOf(getMinutes()), 2, '0') + Strings.padStart(String.valueOf(getSeconds()), 2, '0'); } }