/*
* -----------------------------------------------------------------------
* Copyright © 2013-2015 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (NegativeDayOfMonthPattern.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see <http://www.gnu.org/licenses/>.
* -----------------------------------------------------------------------
*/
package net.time4j.tz.threeten;
import net.time4j.Month;
import net.time4j.PlainDate;
import net.time4j.PlainTime;
import net.time4j.Weekday;
import net.time4j.base.GregorianMath;
import net.time4j.tz.model.GregorianTimezoneRule;
import net.time4j.tz.model.OffsetIndicator;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import static net.time4j.CalendarUnit.DAYS;
/**
* <p>Ein Datumsmuster für DST-Wechsel an einem Wochentag vor oder gleich einem bestimmten Tag im Monat. </p>
*
* <p>Diese Klasse ist wegen einer Anomalie der Definition von <code>DayOfMonthIndicator</code> im JSR-310
* leider notwendig. </p>
*
* @author Meno Hochschild
* @since 4.0
* @serial include
* @doctags.concurrency {immutable}
*/
final class NegativeDayOfMonthPattern
extends GregorianTimezoneRule {
//~ Statische Felder/Initialisierungen --------------------------------
private static final long serialVersionUID = 8126036678681103120L;
//~ Instanzvariablen --------------------------------------------------
private transient final int domIndicator;
private transient final byte dayOfWeek;
//~ Konstruktoren -----------------------------------------------------
NegativeDayOfMonthPattern(
Month month,
int domIndicator,
Weekday dayOfWeek,
PlainTime timeOfDay,
OffsetIndicator indicator,
int savings
) {
super(month, timeOfDay, indicator, savings);
if (domIndicator < -28 || domIndicator > -2) {
throw new IllegalArgumentException("Day-of-month-indicator out of range: " + domIndicator);
}
this.domIndicator = domIndicator;
this.dayOfWeek = (byte) dayOfWeek.getValue();
}
//~ Methoden ----------------------------------------------------------
@Override
public PlainDate getDate(int year) {
int month = this.getMonth().getValue();
int dayOfMonth = GregorianMath.getLengthOfMonth(year, month) + 1 + this.domIndicator;
int ref = GregorianMath.getDayOfWeek(year, month, dayOfMonth);
PlainDate result = PlainDate.of(year, month, dayOfMonth);
if (ref == this.dayOfWeek) {
return result;
}
int delta = (this.dayOfWeek - ref);
if (delta > 0) {
delta -= 7;
}
return result.plus(delta, DAYS);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof NegativeDayOfMonthPattern) {
NegativeDayOfMonthPattern that = (NegativeDayOfMonthPattern) obj;
return (
(this.domIndicator == that.domIndicator)
&& (this.dayOfWeek == that.dayOfWeek)
&& (this.getMonth() == that.getMonth())
&& (this.getIndicator() == that.getIndicator())
&& (this.getSavings() == that.getSavings())
&& this.getTimeOfDay().equals(that.getTimeOfDay())
);
} else {
return false;
}
}
@Override
public int hashCode() {
int h = this.domIndicator;
h += 17 * (this.dayOfWeek + 37 * this.getMonth().getValue());
return h;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append("NegativeDayOfMonthPattern:[month=");
sb.append(this.getMonth());
sb.append(",dom-indicator=");
sb.append(this.domIndicator);
sb.append(",dayOfWeek=");
sb.append(Weekday.valueOf(this.dayOfWeek));
sb.append(",time-of-day=");
sb.append(this.getTimeOfDay());
sb.append(",offset-indicator=");
sb.append(this.getIndicator());
sb.append(",dst-offset=");
sb.append(this.getSavings());
sb.append(']');
return sb.toString();
}
// used in serialization
byte getDayOfWeek() {
return this.dayOfWeek;
}
// used in serialization
int getDomIndicator() {
return this.domIndicator;
}
/**
* @serialData Uses a specialized serialisation form as proxy. The format
* is bit-compressed. The first byte contains the type id of
* the concrete subclass. Then the data bytes for the internal
* state follow. Insight in details see source code.
*
* @return replacement object in serialization graph
*/
private Object writeReplace() {
return new SPX(this, SPX.NEGATIVE_DAY_OF_MONTH_PATTERN_TYPE);
}
/**
* @serialData Blocks because a serialization proxy is required.
* @param in object input stream
* @throws InvalidObjectException (always)
*/
private void readObject(ObjectInputStream in)
throws IOException {
throw new InvalidObjectException("Serialization proxy required.");
}
}