Y zwraca 2012, a y zwraca 2011 w SimpleDateFormat

85

Zastanawiam się, dlaczego „Y” zwraca 2012, a „y” zwraca 2011 w SimpleDateFormat:

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

Czy ktoś może wyjaśnić, dlaczego?

Eng. Fouad
źródło
36
Uwaga dla przyszłych czytelników: takie zachowanie będzie miało miejsce tylko w ostatnim tygodniu roku lub w pierwszym tygodniu roku.
ryvantage

Odpowiedzi:

91

tydzień i rok. Z javadoc

Tygodniowy rok jest zsynchronizowany z cyklem WEEK_OF_YEAR. Wszystkie tygodnie między pierwszym a ostatnim tygodniem (włącznie) mają tę samą wartość roku tygodniowego. Dlatego pierwszy i ostatni dzień tygodnia mogą mieć różne wartości roku kalendarzowego.

Na przykład 1 stycznia 1998 to czwartek. Jeśli getFirstDayOfWeek () ma wartość MONDAY, a getMinimalDaysInFirstWeek () ma wartość 4 (ustawienie zgodne ze standardem ISO 8601), to 1. tydzień 1998 rozpoczyna się 29 grudnia 1997 r. I kończy 4 stycznia 1998 r. Rok tygodnia to 1998 dla ostatnich trzech dni. roku kalendarzowego 1997. Jeśli jednak getFirstDayOfWeek () to NIEDZIELA, to 1. tydzień 1998 rozpoczyna się 4 stycznia 1998 r., a kończy 10 stycznia 1998 r .; wtedy pierwsze trzy dni 1998 r. są częścią 53. tygodnia 1997 r., a rok tygodnia 1997 r.

Aravind Yarram
źródło
$ date Wed Dec 30 00:42:51 UTC 2015 $ date +%G 2015 $ date +%Y 2015 Niektóre programy są zdezorientowane: strftimeoblicza dzisiaj (29.12.2015) tydzień 53, a rok 2015.
aks
11

Oto aktualizacja Java 8 z pewnym kodem, ponieważ GregorianCalendar prawdopodobnie zostanie wycofany lub usunięty z przyszłych wersji JDK.

Nowy kod jest obsługiwany w WeekFieldsklasie, a szczególnie w przypadku małych y/ dużych liter Yz weekBasedYear()akcesorium pola.

Zwraca pole umożliwiające dostęp do roku w roku opartym na tygodniu na podstawie tego WeekFields. Stanowi to koncepcję roku, w którym tygodnie rozpoczynają się w ustalonym dniu tygodnia, takim jak poniedziałek, a każdy tydzień należy do dokładnie jednego roku. To pole jest zwykle używane z właściwościami dayOfWeek () i weekOfWeekBasedYear ().

Tydzień pierwszy (1) to tydzień rozpoczynający się w getFirstDayOfWeek (), w którym jest co najmniej getMinimalDaysInFirstWeek () dni w roku. Tak więc pierwszy tydzień może rozpocząć się przed początkiem roku. Jeżeli pierwszy tydzień rozpoczyna się po rozpoczęciu roku, to poprzedni okres przypada w ostatnim tygodniu poprzedniego roku.

To pole może być używane z dowolnym systemem kalendarza.

W fazie rozstrzygania parsowania datę można utworzyć na podstawie tygodnia opartego na roku, tygodniu roku i dniu tygodnia.

W trybie ścisłym wszystkie trzy pola są sprawdzane pod kątem zakresu prawidłowych wartości. Pole tygodnia roku jest sprawdzane w celu upewnienia się, że wynikowy rok tygodniowy jest żądanym rokiem tygodniowym.

W trybie inteligentnym wszystkie trzy pola są sprawdzane pod kątem ich zakresu prawidłowych wartości. Pole roku opartego na tygodniu tygodnia jest sprawdzane w zakresie od 1 do 53, co oznacza, że ​​wynikowa data może przypadać na kolejny rok tygodniowy do podanego.

W trybie łagodnym rok i dzień tygodnia są porównywane z zakresem prawidłowych wartości. Wynikowa data jest obliczana jako odpowiednik następującego trzystopniowego podejścia. Najpierw utwórz datę pierwszego dnia pierwszego tygodnia w żądanym roku tygodniowym. Następnie weź rok oparty na tygodniu tygodnia, odejmij jeden i dodaj kwotę w tygodniach do daty. Na koniec dostosuj się do prawidłowego dnia tygodnia w zlokalizowanym tygodniu.

Konfiguracja tej WeekFieldsinstancji zależy od lokalizacji i może mieć różne ustawienia w zależności od niej, kraje USA i Europy, takie jak Francja, mogą mieć inny dzień jako początek tygodnia.

Na przykład DateFormatterBuilderJava 8, utwórz instancję parsera z ustawieniami narodowymi i użyj tych ustawień narodowych dla Ysymbolu:

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);
        }

        ...
    }

    ...
}

Oto przykład

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()));

A w odniesieniu do lokalizacji i wielkimi literami Y, można grać z opcją wiersza poleceń -Duser.language=( fr, en, es, itd), lub wymusić ustawienia regionalne w czasie wywołania:

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)));
Brice
źródło
5

Format, Yaby uzyskać rok tygodnia, jeśli kalendarz obsługuje rok tygodnia. ( getCalendar().isWeekDateSupported())

adatapost
źródło
1

Dowiedziałem się w przykry sposób z biblioteki znaczników JSTL format:datez shortjak wymaganym formacie wykorzystuje YYYY pod kołdrą. Który rzeczywiście może przesunąć wydrukowaną datę do przodu o rok.

Erica Kane
źródło
0

Konwertuję datę w tę iz powrotem - można się spodziewać tego samego roku, kiedy to zrobisz.

Zwróć uwagę, jak się rozwija!

To jest złe: YYYY! RRRR

Możesz go uruchomić tutaj .

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);
    }
}

To dobrze: yyyy

rrrr

JGFMK
źródło