Czy dobrze pobierasz numer tygodnia? Zapewne odpowiesz tak, ale czy zawsze?

Pakiet java.time zdążył się już dawno zadomowić w naszych projektach i nie ma się co temu dziwić, ponieważ jest to od dawna wyczekiwany następca paskudnego Calendar’a. Na co dzień pewnie każdy z was korzysta z tworzenia dat lub porównywania ich ze sobą. Działa to świetnie oraz jest niesamowicie proste.

Czasami jednak przychodzi moment, że potrzebujemy nieco więcej informacji niż rok, miesiąc, dzień i godzina. Przykładowo tak jak w tytule naszym zadaniem jest pobrać numer aktualnego tygodnia. Wydawałoby się prosta rzecz, w końcu dość często z tego korzystamy w życiu prywatnym. Jednak tutaj następuje delikatne zaskoczenie, bo w klasie LocalDate, ani LocalDateTime, z których zapewne najczęściej korzystacie nie ma metody getCurrentWeek.

LocalDate nie posiada metody do pobierania numeru tygodnia
LocalDate nie posiada metody do pobierania numeru tygodnia

W takich chwilach nie pozostaje nic innego niż sięgnąć do dokumentacji lub szukać rozwiązania w internecie.

WeekFields – w poszukiwaniu numeru tygodnia

Z pomocą przychodzi klasa WeekFields, która udostępnia kilka przydatnych metod związanych z tygodniami. W zależności od naszych potrzeb, możemy skorzystać z definicji ISO-8601, danego Locale lub własnych ustawień obsługi dni tygodnia.

WeekFields localeWeekFields = WeekFields.of(Locale.ENGLISH);
WeekFields customWeekFields = WeekFields.of(DayOfWeek.TUESDAY, 6);
WeekFields isoWeekFields = WeekFields.ISO;
isoWeekFields.dayOfWeek();
isoWeekFields.dayOfWeek();
isoWeekFields.weekOfYear();
isoWeekFields.weekOfMonth();
isoWeekFields.weekBasedYear();
isoWeekFields.weekOfWeekBasedYear();

No i tutaj może nas najść delikatna konsternacja, ponieważ są dwie metody, które można wykorzystać przy pobieraniu numeru tygodnia: weekOfYear i weekOfWeekBasedYear. Dodatkowo mamy też metodę do pobierania roku (weekBasedYear), ale przecież rok możemy łatwo sobie pobrać korzystając z metody getYear w klasie LocalDate. Sprawdźmy jakie wyniki zwrócą te metody dla dzisiejszej daty.

LocalDate localDate = LocalDate.of(2020, 11, 1);
int weekOfYear = localDate.get(weekFields.weekOfYear()); // 44
int weekOfWeekBasedYear = localDate.get(weekFields.weekOfWeekBasedYear()); // 44
int weekBaseYear = localDate.get(weekFields.weekBasedYear()); // 2020
int year = localDate.getYear(); // 2020

Na pierwszy rzut oka wszystko wygląda identycznie, ale gdzieś musi tkwić haczyk. Sprawdźmy wyniki dla innej daty.

LocalDate localDate = LocalDate.of(2021, 1, 1);
int weekOfYear = localDate.get(weekFields.weekOfYear()); // 0
int weekOfWeekBasedYear = localDate.get(weekFields.weekOfWeekBasedYear()); // 53
int weekBaseYear = localDate.get(weekFields.weekBasedYear()); // 2020
int year = localDate.getYear(); // 2021

No i tutaj jest dość duże zaskoczenie, ponieważ okazuje się, że pierwszy stycznia 2021 roku może być zarówno zerowym tygodniem jak i pięćdziesiątym trzecim. A jeszcze dziwniejsza rzecz to ta, że używając metody weekBasedYear jest on w 2020 roku!

O co tutaj chodzi?

W użytym przeze mnie przykładzie skorzystałem z definicji ISO dla WeekFields. W tym wypadku tydzień zaczyna się od poniedziałku i powinien mieć co najmniej 4 dni. Biorąc to pod uwagę, pierwsze trzy dni stycznia 2021 roku nie wpisują się do tej reguły. W takim układzie te dni przypisane są do 53 tygodnia roku 2020. Analogicznie jest w przypadku pobierania roku. Metoda nazywa się weekBasedYear, co oznacza, że rok jest podzielony na tygodnie zgodnie z wybraną definicją. Z uwagi, że pierwszy stycznia 2021 roku należy do 53 tygodnia poprzedniego roku, ten właśnie rok jest zwrócony przy korzystaniu z metody weekBasedYear.

Warto zapamiętać, że numery tygodnia odpowiadają kolejnym czwartkom w roku. Pierwszy czwartek 2021 wypada piątego stycznia, więc jest on w pierwszym tygodniu roku.

W przypadku pobrania numeru tygodnia metodą weekOfYear dostajemy jeszcze jedną istotną informację. W przypadku kiedy pierwszy tydzień nie spełnia reguł zdefiniowanych w WeekFields jako numer dostaniemy zero. Analogicznie przy spełnieniu tych definicji tydzień będzie numerowany od jedynki.

Podsumowując, w zależności jaki rezultat chcemy uzyskać powinniśmy po pierwsze zastanowić się z jakiej definicji tygodnia chcemy skorzystać (od którego dnia ma się on zaczynać i ile minimalnie dni musi zawierać) a po drugie wybrać odpowiednią z dostępnych metod.