Y restituisce 2012 mentre y restituisce 2011 in SimpleDateFormat


85

Mi chiedo perché "Y" restituisce 2012 mentre "y" restituisce 2011 in SimpleDateFormat:

System.out.println(new SimpleDateFormat("Y").format(new Date())); // prints 2012
System.out.println(new SimpleDateFormat("y").format(new Date())); // prints 2011

Qualcuno può spiegare perché?


40
Proprio come una nota per i futuri lettori: questo comportamento avverrà solo durante l'ultima settimana dell'anno o la prima settimana dell'anno.
ryvantage

Risposte:


94

settimana anno e anno. Da javadoc

Un anno settimanale è sincronizzato con un ciclo WEEK_OF_YEAR. Tutte le settimane comprese tra la prima e l'ultima settimana (incluse) hanno lo stesso valore settimana anno. Pertanto, il primo e l'ultimo giorno dell'anno della settimana possono avere valori di anno di calendario diversi.

Ad esempio, il 1 gennaio 1998 è un giovedì. Se getFirstDayOfWeek () è LUNEDÌ e getMinimalDaysInFirstWeek () è 4 (impostazione compatibile con lo standard ISO 8601), la settimana 1 del 1998 inizia il 29 dicembre 1997 e termina il 4 gennaio 1998. La settimana dell'anno è 1998 per gli ultimi tre giorni dell'anno civile 1997. Se, tuttavia, getFirstDayOfWeek () è SUNDAY, la settimana 1 del 1998 inizia il 4 gennaio 1998 e termina il 10 gennaio 1998; i primi tre giorni del 1998 fanno poi parte della 53a settimana del 1997 e la loro settimana è il 1997.


$ date Wed Dec 30 00:42:51 UTC 2015 $ date +%G 2015 $ date +%Y 2015 Alcuni software sono confusi: strftimecalcola oggi (29/12/2015) come avente la settimana 53 e la settimana-anno come 2015.
risponde il

11

Ecco un aggiornamento di Java 8 con del codice, poiché GregorianCalendar sarà probabilmente deprecato o rimosso dalle future versioni di JDK.

Il nuovo codice viene gestito nella WeekFieldsclasse e specificamente per le lettere minuscole y/ maiuscole Ycon la funzione di accesso al weekBasedYear()campo.

Restituisce un campo per accedere all'anno di un anno basato sulla settimana in base a questo WeekFields. Questo rappresenta il concetto dell'anno in cui le settimane iniziano in un giorno della settimana fisso, come il lunedì e ogni settimana appartiene esattamente a un anno. Questo campo viene in genere utilizzato con dayOfWeek () e weekOfWeekBasedYear ().

La prima settimana (1) è la settimana che inizia su getFirstDayOfWeek () dove ci sono almeno getMinimalDaysInFirstWeek () giorni nell'anno. Pertanto, la prima settimana può iniziare prima dell'inizio dell'anno. Se la prima settimana inizia dopo l'inizio dell'anno, il periodo precedente è nell'ultima settimana dell'anno precedente.

Questo campo può essere utilizzato con qualsiasi sistema di calendario.

Nella fase di risoluzione dell'analisi, è possibile creare una data da un anno basato sulla settimana, una settimana dell'anno e un giorno della settimana.

In modalità rigorosa, tutti e tre i campi vengono convalidati rispetto al loro intervallo di valori validi. Il campo della settimana dell'anno viene convalidato per garantire che l'anno basato sulla settimana risultante sia l'anno basato sulla settimana richiesto.

In modalità intelligente, tutti e tre i campi vengono convalidati rispetto al loro intervallo di valori validi. Il campo dell'anno basato sulla settimana viene convalidato da 1 a 53, il che significa che la data risultante può essere nell'anno basato sulla settimana successivo a quello specificato.

In modalità clemente, l'anno e il giorno della settimana vengono convalidati rispetto all'intervallo di valori validi. La data risultante è calcolata equivalente al seguente approccio in tre fasi. Innanzitutto, crea una data nel primo giorno della prima settimana nell'anno basato sulla settimana richiesto. Quindi prendi l'anno basato sulla settimana della settimana, sottrai uno e aggiungi l'importo in settimane alla data. Infine, regola il giorno della settimana corretto all'interno della settimana localizzata.

La configurazione di questa WeekFieldsistanza dipende dalle impostazioni locali e potrebbe avere impostazioni diverse a seconda di essa, gli Stati Uniti e i paesi europei come la Francia potrebbero avere un giorno diverso come inizio della settimana.

Ad esempio, DateFormatterBuilderdi Java 8, istanzia il parser con la locale e usa questa locale per il Ysimbolo:

public final class DateTimeFormatterBuilder {
    ...

    private void parsePattern(String pattern) {
        ...
                } else if (cur == 'Y') {
                    // Fields defined by Locale
                    appendInternal(new WeekBasedFieldPrinterParser(cur, count));
                } else {
        ...


    static final class WeekBasedFieldPrinterParser implements DateTimePrinterParser {
        ...

        /**
         * Gets the printerParser to use based on the field and the locale.
         *
         * @param locale  the locale to use, not null
         * @return the formatter, not null
         * @throws IllegalArgumentException if the formatter cannot be found
         */
        private DateTimePrinterParser printerParser(Locale locale) {
            WeekFields weekDef = WeekFields.of(locale);
            TemporalField field = null;
            switch (chr) {
                case 'Y':
                    field = weekDef.weekBasedYear();
                    if (count == 2) {
                        return new ReducedPrinterParser(field, 2, 2, 0, ReducedPrinterParser.BASE_DATE, 0);
                    } else {
                        return new NumberPrinterParser(field, count, 19,
                                                       (count < 4) ? SignStyle.NORMAL : SignStyle.EXCEEDS_PAD, -1);
                    }
                case 'e':
                case 'c':
                    field = weekDef.dayOfWeek();
                    break;
                case 'w':
                    field = weekDef.weekOfWeekBasedYear();
                    break;
                case 'W':
                    field = weekDef.weekOfMonth();
                    break;
                default:
                    throw new IllegalStateException("unreachable");
            }
            return new NumberPrinterParser(field, (count == 2 ? 2 : 1), 2, SignStyle.NOT_NEGATIVE);
        }

        ...
    }

    ...
}

Ecco qualche esempio

System.out.format("Conundrum                         : %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                               .format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'")));
System.out.format("Solution                          : %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                               .format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmms'S'")));


System.out.format("JVM Locale first day of week      : %s%n",
                  WeekFields.of(Locale.getDefault()).getFirstDayOfWeek());
System.out.format("US first day of week              : %s%n",
                  WeekFields.of(Locale.US).getFirstDayOfWeek());
System.out.format("France first day of week          : %s%n",
                  WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());
System.out.format("JVM Locale min days in 1st week   : %s%n",
                  WeekFields.of(Locale.getDefault()).getMinimalDaysInFirstWeek());
System.out.format("US min days in 1st week           : %s%n",
                  WeekFields.of(Locale.US).getMinimalDaysInFirstWeek());
System.out.format("JVM Locale min days in 1st week   : %s%n",
                  WeekFields.of(Locale.FRANCE).getMinimalDaysInFirstWeek());

System.out.format("JVM Locale week based year (big Y): %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.FRANCE).weekBasedYear()));
System.out.format("France week based year (big Y)    : %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.FRANCE).weekBasedYear()));
System.out.format("US week based year (big Y)        : %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.US).weekBasedYear()));

E per quanto riguarda la localizzazione e la maiuscola Y, è possibile giocare sia con l'opzione della riga di comando -Duser.language=( fr, en, es, etc.), o forzare il locale in fase di invocazione:

System.out.format("English localized                 : %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                               .format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'", Locale.ENGLISH)));
System.out.format("French localized                  : %s%n",
                  ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
                               .format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'", Locale.FRENCH)));

5

Formato Yper ottenere la settimana dell'anno se il calendario supporta la settimana dell'anno. ( getCalendar().isWeekDateSupported())


0

Ho imparato a mie spese la libreria di tag JSTL format:datecon shortcome il formato richiesto utilizza YYYY sotto le coperte. Che può effettivamente portare la data stampata avanti di un anno.


0

Converto una data avanti e indietro: ti aspetteresti lo stesso anno quando lo fai.

Notate come ne avanza uno!

Questo è un male: YYYY! AAAA

Puoi eseguirlo qui .

import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import static java.lang.System.out;
class Playground {
    public static Date convertYYYYMMDDStr(String s) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date result = null;
        try {
            result = sdf.parse(s);
        } catch(ParseException e) {
            e.printStackTrace();
        }
        return result;
    }
    public static String formatDateToStrWithSDF(Date d, SimpleDateFormat s) {
        return s.format(d);
    }
    public static void main(String[ ] args) {
        // DON'T DO. Use yyyy instead of YYYY
        SimpleDateFormat sdfdmy = new SimpleDateFormat("dd-MM-YYYY"); 
        String jan1st2020sb = "2020-01-01";
        Date jan1st2020d = convertYYYYMMDDStr(jan1st2020sb);
        String jan1st2020sa = formatDateToStrWithSDF(jan1st2020d, sdfdmy);
        out.println(jan1st2020sb);
        out.println(jan1st2020d);
        out.println(jan1st2020sa);
        String dec31st2020sb = "2020-12-31";
        Date dec31st2020d = convertYYYYMMDDStr(dec31st2020sb);
        String dec31st2020sa = formatDateToStrWithSDF(dec31st2020d, sdfdmy);
        out.println(dec31st2020sb);
        out.println(dec31st2020d);
        out.println(dec31st2020sa);
    }
}

Questo va bene: yyyy

yyyy

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.