Java tutorial
/* * Copyright (C) 2012-2013 Dario Scoppelletti, <http://www.scoppelletti.it/>. * * 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 it.scoppelletti.mobilepower.types; import java.text.*; import java.util.*; import android.os.*; import org.apache.commons.lang3.*; /** * Data. * * <P>A differenza delle classi {@code Calendar} e {@code Date} di * <ACRONYM TITLE="Java Development Kit">JDK</ACRONYM>, la classe * {@code SimpleDate} include le sole componenti (anno, mese, giorno) e le * istanze sono immutabili.</P> * * @since 1.0 */ public final class SimpleDate implements Comparable<SimpleDate>, ValueType { /** * Data nulla. * * @see #isEmpty */ public static final SimpleDate NIL = new SimpleDate(); /** * Servizio di creazione delle istanze. */ public static final Parcelable.Creator<SimpleDate> CREATOR = new Parcelable.Creator<SimpleDate>() { /** * Legge un’istanza. * * @param in Flusso di lettura. * @return Oggetto. */ public SimpleDate createFromParcel(Parcel in) { return new SimpleDate(in); } /** * Crea un vettore di istanze. * * @param size Dimensione. * @return Vettore. */ public SimpleDate[] newArray(int size) { return new SimpleDate[size]; } }; private final int myYear; private final int myMonth; private final int myDay; /** * Costruttore. */ private SimpleDate() { myYear = TimeTools.NA; myMonth = TimeTools.NA; myDay = TimeTools.NA; } /** * Costruttore. * * @param year Anno. * @param month Mese. * @param day Giorno. */ public SimpleDate(int year, int month, int day) { int min, max; Calendar cal = Calendar.getInstance(); min = cal.getMinimum(Calendar.YEAR); max = cal.getMaximum(Calendar.YEAR); if (year < min || year > max) { throw new IllegalArgumentException( String.format("Argument year %1$d is ouf of range [%2$d, %3$d].", year, min, max)); } cal.set(Calendar.YEAR, year); min = cal.getActualMinimum(Calendar.MONTH); max = cal.getActualMaximum(Calendar.MONTH); if (month < min || month > max) { throw new IllegalArgumentException( String.format("Argument month %1$d is ouf of range [%2$d, %3$d].", month, min, max)); } cal.set(Calendar.MONTH, month); min = cal.getActualMinimum(Calendar.DAY_OF_MONTH); max = cal.getActualMaximum(Calendar.DAY_OF_MONTH); if (day < min || day > max) { throw new IllegalArgumentException( String.format("Argument day %1$d is ouf of range [%2$d, %3$d].", day, min, max)); } myYear = year; myMonth = month; myDay = day; } /** * Costruttore. * * @param value Calendario. */ private SimpleDate(Calendar value) { myYear = value.get(Calendar.YEAR); myMonth = value.get(Calendar.MONTH); myDay = value.get(Calendar.DAY_OF_MONTH); } /** * Costruttore. * * @param in Flusso di lettura. */ private SimpleDate(Parcel in) { myYear = in.readInt(); myMonth = in.readInt(); myDay = in.readInt(); } /** * Restituisce la data corrente. * * @return Valore. */ public static SimpleDate getToday() { return new SimpleDate(Calendar.getInstance()); } /** * Restituisce la data corrispondente ad un calendario. * * @param value Calendario. * @return Data. Se il calendario {@code value} è {@code null}, * restituisce la data nulla. * @see #NIL */ public static SimpleDate toSimpleDate(Calendar value) { if (value == null) { return SimpleDate.NIL; } return new SimpleDate(value); } /** * Restituisce la data corrispondente ad un codice giorno. * * @param dayCode Codice giorno. * @return Data. * @see #calcDayCode */ public static SimpleDate fromDayCode(long dayCode) { int year, month, day, delta, monthDays; Calendar cal; if (dayCode < 0) { throw new IllegalArgumentException("Argument dayCode is less than 0."); } if (dayCode == 0) { return SimpleDate.NIL; } // Calcolo l'anno year al quale appartiene la data ricercata per // approssimazioni successive basate sul concetto di codice anno: dato // infatti un numero di giorni, il minimo numero di anni che // intercorrono in quel periodo e' il rapporto tra quel numero di giorni // e 366 (durata massima in giorni di un anno). cal = Calendar.getInstance(); year = (int) (dayCode / 366l); while (true) { delta = (int) (dayCode - SimpleDate.calcYearCode(year)); if (delta < 366) { break; } year = year + delta / 366; } if (delta == 0) { // Definizione di codice anno year--; cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, Calendar.DECEMBER); day = cal.getActualMaximum(Calendar.DAY_OF_MONTH); return new SimpleDate(year, Calendar.DECEMBER, day); } // delta corrisponde al numero progressivo del giorno ricercato // nell'anno: // Sottraendo progressivamente la durata in giorni dei mesi, si // determina il numero progressivo del mese nell'anno; il residuo finale // corrisponde al numero progressivo del giorno nel mese. cal.set(Calendar.YEAR, year); day = 0; // avoid warning for (month = 0; delta > 0; month++) { cal.set(Calendar.MONTH, month); monthDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH); if (delta <= monthDays) { day = delta; break; } delta = delta - monthDays; } return new SimpleDate(year, month, day); } /** * Restituisce la data rappresentata da una stringa in formato ISO 8601 * ({@code yyyy-MM-dd}). * * @param s Stringa. * @return Data. Se la stringa {@code s} è nulla, restituisce la * data nulla. * @see #NIL * @see #isEmpty */ public static SimpleDate fromISOString(String s) throws ParseException { Date value; SimpleDate date; SimpleDateFormat fmt; if (StringUtils.isBlank(s)) { return SimpleDate.NIL; } fmt = new SimpleDateFormat("yyyy-MM-dd"); value = fmt.parse(s); date = new SimpleDate(1900 + value.getYear(), value.getMonth(), value.getDate()); return date; } /** * Restituisce l’anno. * * @return Valore. * @see #isEmpty * @see it.scoppelletti.mobilepower.types.TimeTools#NA */ public int getYear() { return myYear; } /** * Restituisce il mese. * * @return Valore. * @see #isEmpty * @see it.scoppelletti.mobilepower.types.TimeTools#NA */ public int getMonth() { return myMonth; } /** * Restituisce il giorno. * * @return Valore. * @see #isEmpty * @see it.scoppelletti.mobilepower.types.TimeTools#NA */ public int getDay() { return myDay; } /** * Rappresenta l’oggetto con una stringa. * * <P>Questa versione del metodo {@code toString} restituisce la data in * formato ISO 8601 ({@code yyyy-MM-dd}).</P> * * @return Stringa. Se la data è nulla, restituisce la stringa vuota * {@code ""}. * @see #NIL * @see #isEmpty */ @Override public String toString() { if (isEmpty()) { return StringUtils.EMPTY; } return String.format("%1$4d-%2$02d-%3$02d", myYear, myMonth + 1, myDay); } public boolean isEmpty() { return (myDay == TimeTools.NA); } public void writeToParcel(Parcel out, int flags) { out.writeInt(myYear); out.writeInt(myMonth); out.writeInt(myDay); } public int describeContents() { return 0; } /** * Converte la data in un calendario. * * @return Valore. Se la data è nulla, restituisce {@code null}. * @see #NIL * @see #isEmpty */ public Calendar toCalendar() { if (isEmpty()) { return null; } Calendar value = TimeTools.getUTCNow(); value.set(Calendar.YEAR, myYear); value.set(Calendar.MONTH, myMonth); value.set(Calendar.DAY_OF_MONTH, myDay); value.set(Calendar.HOUR_OF_DAY, 0); value.set(Calendar.MINUTE, 0); value.set(Calendar.SECOND, 0); value.set(Calendar.MILLISECOND, 0); return value; } /** * Verifica l’uguaglianza con un altro oggetto. * * @param obj Oggetto di confronto. * @return Esito della verifica. */ @Override public boolean equals(Object obj) { SimpleDate op; if (!(obj instanceof SimpleDate)) { return false; } op = (SimpleDate) obj; if (myYear != op.getYear() || myMonth != op.getMonth() || myDay != op.getDay()) { return false; } return true; } /** * Calcola il codice hash dell’oggetto. * * @return Valore. */ @Override public int hashCode() { int value = 17; value = 37 * value + myYear; value = 37 * value + myMonth; value = 37 * value + myDay; return value; } /** * Confronto con un altro oggetto. * * @param obj Oggetto di confronto. * @return Risultato del confronto. */ public int compareTo(SimpleDate obj) { if (obj == null) { throw new NullPointerException("Argument obj is null."); } if (isEmpty() && obj.isEmpty()) { return 0; } else if (isEmpty()) { return -1; } else if (obj.isEmpty()) { return 1; } if (myYear < obj.getYear()) { return -1; } if (myYear > obj.getYear()) { return 1; } if (myMonth < obj.getMonth()) { return -1; } if (myMonth > obj.getMonth()) { return 1; } if (myDay < obj.getDay()) { return -1; } if (myDay > obj.getDay()) { return 1; } return 0; } /** * Calcola il codice giorno della data. * * <P>Il <DFN>codice giorno</DFN> corrisponde al numero di giorni che * intercorrono da un ipotetico anno 0 alla data presa in esame.</P> * * @return Valore. */ public long calcDayCode() { int month; long dayCode; Calendar cal; if (isEmpty()) { return 0; } cal = Calendar.getInstance(); dayCode = SimpleDate.calcYearCode(myYear); cal.set(Calendar.YEAR, myYear); for (month = 0; month < myMonth; month++) { cal.set(Calendar.MONTH, month); dayCode += cal.getActualMaximum(Calendar.DAY_OF_MONTH); } return dayCode + myDay; } /** * Calcola il codice anno di un anno. * * <P>Il <DFN>codice anno</DFN> di un anno coincide con il codice giorno del * 31 dicembre dell’anno precedente.<BR> * L’anno 0 è, per definizione, bisestile e il suo codice anno * è, per convenzione, 0.</P> * * @param year Anno. * @return Codice anno. */ private static long calcYearCode(long year) { long year4, year100, year400, year365, year366; // Somma ponderata del numero di anni bisestili e del numero di anni non // bisestili che intercorrono tra l'anno 0 e lanno considerato. year4 = (year - (year % 4)) / 4; year100 = (year - year % 100) / 100; year400 = (year - year % 400) / 400; year365 = year - year4 + year100 - year400; year366 = year - year365; return year365 * 365 + year366 * 366; } }