it.scoppelletti.mobilepower.types.SimpleDate.java Source code

Java tutorial

Introduction

Here is the source code for it.scoppelletti.mobilepower.types.SimpleDate.java

Source

/*
 * 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&rsquo;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} &egrave; {@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} &egrave; 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&rsquo;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&rsquo;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 &egrave; 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 &egrave; 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&rsquo;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&rsquo;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&rsquo;anno precedente.<BR>
     * L&rsquo;anno 0 &egrave;, per definizione, bisestile e il suo codice anno
     * &egrave;, 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;
    }
}