Como encontrar o total de semanas do ano em Java?

11

Estou trabalhando em um projeto. Lá eu deveria encontrar o total de semanas de um ano. Tentei com o código a seguir, mas recebo a resposta errada: 2020 tem 53 semanas, mas esse código fornece 52 semanas.

Onde eu errei neste código?

package com.hib.mapping;

import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.joda.time.DateTime;

public class TestWeek {

    public static void main(String args[]) {
        System.out.println(getWeeks());
    }

    public static int getWeeks() {

        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2020);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        GregorianCalendar gregorianCalendar = new GregorianCalendar();

        int weekDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (gregorianCalendar.isLeapYear(2020)) {
            if (weekDay == Calendar.THURSDAY || weekDay == Calendar.WEDNESDAY)
                return 53;
            else
                return 52;
        } else {
            if (weekDay == Calendar.THURSDAY)
                return 53;
            else
                return 52;
        }

    }

}

Resultado:

52

Kumaresan Perumal
fonte
11
Não é exatamente uma duplicata, mas a leitura sugerida: stackoverflow.com/questions/44197872/…
Federico klez Culloca
11
Sugira fornecer sua definição de uma semana.
Andy
4
É 2020. Evite usar a api antiga e problemática da data de legado em torno de Datee Calendar. Use java.time, em vez disso, é muito melhor e mais simples.
Zabuzard 27/01
Se (o ano é bissexto) e (o 1º de janeiro é o domingo), mais 54 são 53.
Akina
você pode me dar algum código?
Kumaresan Perumal 27/01

Respostas:

7

Usando a definição da Wikipedia aqui . Um ano tem 53 semanas se 1º de janeiro é quinta-feira ou 31 de dezembro é quinta-feira, caso contrário, tem 52 semanas. Essa definição é equivalente à que você usou. Acho que essa é uma condição muito mais fácil de verificar, pois você não precisa verificar se há anos bissextos.

Usando as java.timeAPIs do Java 8 :

int year = 2020;
boolean is53weekYear = LocalDate.of(year, 1, 1).getDayOfWeek() == DayOfWeek.THURSDAY ||
        LocalDate.of(year, 12, 31).getDayOfWeek() == DayOfWeek.THURSDAY;
int weekCount = is53weekYear ? 53 : 52;
Vassoura
fonte
A semana máxima do ano 53. Acho que é bom @Naman, não há 54 semanas, acho que a solução correta é LocalDate.of (i, 1, 1) .range (WeekFields.ISO.weekOfWeekBasedYear ()). GetMaximum ()
Kumaresan Perumal 27/01
11
dá 53 semanas para 2021. Acho que está errado. por favor testá-lo
Kumaresan Perumal
11
@KumaresanPerumal Você tem certeza?
Vassoura
11
Sim, está funcionando. Vou testar com 100 anos e depois digo. obrigado pelo seu esforço
Kumaresan Perumal 28/01
11
Eu testei no JDK 1.8.0_101 e 1.8.0_121. Ainda recebendo 52 semanas em 2021 como deveríamos.
Ole VV
7

tl; dr

Para uma semana ISO 8601 padrão, use a YearWeekclasse da biblioteca ThreeTen-Extra com uma declaração ternária.

YearWeek          // Represents an entire week of a week-based-year.
.of( 2020 , 1 )   // Pass the number of the week-based-year (*not* calendar year), and a week number ranging from 1 to 52 or 1 to 53.
.is53WeekYear()   // Every standard week-based-year has either 52 or 52 complete weeks.
? 53              // Ternary statement returns 53 if the predicate returns True, …
: 52              // … otherwise returns 52. 

Isso é, YearWeek.of( 2020 , 1 ).is53WeekYear() ? 53 : 52

Definir "semana"

Você precisa definir uma semana. No seu exemplo de código, a definição de semana varia de acordo com o padrão atual da JVM Locale. Portanto, seus resultados podem variar em tempo de execução.

Seu código também usa classes de data e hora terríveis que foram substituídas anos atrás pelas modernas classes java.time . Pare de usar o GregorianCalendar& Calendar; eles foram substituídos por boas razões.

Semana ISO 8601

A norma ISO 8601 padrão define uma semana como:

  • As semanas começam na segunda-feira e terminam no domingo.
  • A semana 1 tem a primeira quinta-feira do ano civil.

Essa definição significa:

  • O primeiro e os últimos dias de um ano com base na semana podem ser os dias finais / iniciais do ano civil anterior / seguinte.
  • O ano baseado na semana tem 52 ou 53 semanas completas.

Se sua definição for diferente, consulte a resposta por Ole VV .

YearWeek:is53WeekYear

Se isso corresponder à sua definição, adicione a biblioteca ThreeTen-Extra ao seu projeto para estender a funcionalidade java.time incorporada ao Java 8 e posterior. Você então tem acesso à YearWeekclasse.

ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearWeek yearWeekNow = YearWeek.now( z ) ;
boolean is53WeekYear = yearWeekNow.is53WeekYear() ;

int weeksLong = yearWeekNow.is53WeekYear() ? 53 : 52 ;

Para perguntar sobre um determinado ano com base na semana, escolha arbitrariamente qualquer semana do ano. Por exemplo, para o ano 2020 baseado em semana, solicitamos a semana nº 1.

int weeksLong = YearWeek.of( 2020 , 1 ).is53WeekYear() ? 53 : 52 ;
LocalDate weekStart = YearWeek.of( 2020 , 1 ).atDay( DayOfWeek.MONDAY ) ;

weeksLong = 53

weekStart = 30-12-2019

Observe como o primeiro dia do ano semanal de 2020 é a partir do ano civil de 2019.

Basil Bourque
fonte
Eu estou na India. Devo especificar o nome do país?
Kumaresan Perumal 28/01
@KumaresanPerumal Não, o fuso horário existe now. Como você deseja obter a contagem da semana para um ano específico, em vez do ano atual, basta usar YearWeek.of(yourYear, 1).
Vassoura
ok obrigado @sweeper eu vou usá-lo
Kumaresan Perumal
A @KumaresanPerumal Wikipedia mantém esta lista de nomes de fuso horário. Pode estar um pouco desatualizado. Até onde eu sei, toda a Índia usa o fuso horário Asia/Kolkata, atualmente com um deslocamento de +05: 30. No código Java: ZoneId.of( "Asia/Kolkata" ). Para mais informações e histórico, consulte Wikipedia, Hora na Índia .
Basil Bourque
4

A solução flexível

Isso deve funcionar para qualquer esquema de numeração semanal que possa ser representado em um WeekFieldsobjeto.

public static int noOfWeeks(WeekFields wf, int year) {
    LocalDate lastDayOfYear = YearMonth.of(year, Month.DECEMBER).atEndOfMonth();
    if (lastDayOfYear.get(wf.weekBasedYear()) > year) { // belongs to following week year
        return lastDayOfYear.minusWeeks(1).get(wf.weekOfWeekBasedYear());
    }
    else {
        return lastDayOfYear.get(wf.weekOfWeekBasedYear());
    }
}

A idéia é encontrar o número da semana da última semana do ano com base na semana. Tento primeiro com 31 de dezembro, mas isso pode ocorrer na primeira semana do ano seguinte. Se assim for, eu vou uma semana atrás.

Eu testei bastante com WeekFields.ISO, não tanto com outros WeekFieldsobjetos, mas como eu disse, acredito que funciona.

Se você sabe que sempre precisará da ISO 8601 semanas, acho que deve seguir uma das boas respostas de Sweeper e Basil Bourque . Postei isso caso você precisasse de uma solução mais flexível que também funcionasse com outros esquemas de numeração semanal.

Use java.time

O código da sua pergunta é engraçado, pois importa classes do Joda-Time e do java.time, mas usa o antigo Calendare o GregorianCalendarJava 1.1. Essas classes foram mal projetadas e agora estão desatualizadas há muito tempo, você não deve usá-las. O Joda-Time está no modo de manutenção, o java.time assumiu o controle. Qual é o que eu uso e recomendo que você use.

Ole VV
fonte
1

Eu acho que isso deve funcionar muito bem também:

int year = 2020;
long numOfWeeks = LocalDate.of(year, 1, 1).datesUntil(LocalDate.of(year, 12, 31), Period.ofDays(7)).count();
System.out.println("Weeks: " + numOfWeeks);
Tim Hunter
fonte
Belo pensamento. Ele fornece o resultado correto para 2020 (53 semanas), além de 2019 e 2021 (52 semanas cada). Pode ser um pouco difícil de se convencer, no entanto.
Ole VV
Parece dar 53 semanas para todos os anos bissextos, o que não está correto.
Ole VV
@ OleV.V. Hmm, justo o suficiente. Estou curioso para saber como funcionam os cálculos do LocalDate. Terra estúpida e seu ciclo de tempo ligeiramente imperfeito, tornando as coisas mais complicadas do que precisam.
Tim Hunter
LocalDate.datesUntil é Java 9+
ZhekaKozlov
0

Depois de tentar muito no java 8. Não consegui encontrar uma solução. então preparei a dependência de data e hora de Joda. Deu-me uma boa resposta como eu esperava

código:

for (int i = 2020; i < 2100; i++) {
  int weeks = new DateTime().withYear(i).weekOfWeekyear().getMaximumValue();
  System.out.println(i + " years : " + weeks); 
}

Dependência do Maven:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.5</version>
</dependency>
Kumaresan Perumal
fonte