web-dev-qa-db-fra.com

Comment fonctionne vraiment Java "semaine année"?

Cela a commencé comme une simple erreur: j'avais YYYY au lieu de yyyy dans ma chaîne de format pour un objet SimpleDateFormat. Mais je suis totalement dérouté par les résultats de mes tests avec la chaîne de format incorrecte.

Ce code:

@Test
public void whatTheHell() {
        try {
                SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/YYYY");

                Date d1 = sdf.parse("01/07/2016");
                Date d2 = sdf.parse("02/08/2016");
                Date d3 = sdf.parse("11/29/2027");

                System.out.println(d1.toString());
                System.out.println(d2.toString());
                System.out.println(d3.toString());
        } catch (ParseException pe) {
                fail("ParseException: " + pe.getMessage());
        }
}

produit cette sortie:

Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2026

J'ai lu la documentation sur le paramètre 'Y' ici: https://docs.Oracle.com/javase/7/docs/api/Java/util/GregorianCalendar.html , mais je continue ne peut pas voir la logique qui fonctionne ici. En particulier, le dernier cas: je peux en quelque sorte comprendre comment les dates de janvier (et peut-être de février) peuvent être traduites en décembre de l'année précédente, mais déplacer la date du 29 novembre de 11 mois en arrière me déroute. Et quelle est la particularité du 27 décembre?

Quelqu'un peut-il expliquer?

PLUS D'INFORMATIONS

@Jan a suggéré que le recours à la méthode toString () pourrait être un problème, j'ai donc défini un format de date pour imprimer YYYY MM dd '-' yyyy MM dd dans le même code que ci-dessus. Voici la sortie supplémentaire:

2016 12 27 - 2015 12 27
2016 12 27 - 2015 12 27
2027 12 27 - 2026 12 27
22
Dave Mulligan

C'est simple: le 27 décembre 2015 est le jour 1 de la semaine 1 de la semaine de l'année 2016 (et le 27 décembre 2026 est le jour 1 de la semaine 1 de la semaine de l'année 2027). Cela peut être vérifié en ajoutant ces lignes:

SimpleDateFormat odf = new SimpleDateFormat("YYYY-ww-u");
System.out.println(odf.format(d1));
System.out.println(odf.format(d2));
System.out.println(odf.format(d3));

Si un SimpleDateFormat affiche une date, il peut utiliser tous les champs: année, mois, jour, jour de la semaine, semaine du mois, semaine de l'année, semaine-année, etc.

Lors de l'analyse, SimpleDateFormat attend un ensemble de valeurs correspondant: jour, mois, année ou jour de la semaine, semaine dans l'année, semaine -an. Puisque vous avez fourni une semaine-année mais n'avez pas fourni de jour de semaine et de semaine dans l'année, les valeurs de ces valeurs ont été supposées égales à 1.


Les valeurs réelles dépendent de votre environnement local:

  • quelle semaine de l'année est la semaine 1
  • quel jour est le premier jour de la semaine

(voir https://docs.Oracle.com/javase/7/docs/api/Java/util/GregorianCalendar.html#week_and_year )

Sur mon système (en utilisant les paramètres régionaux de-ch, avec le format "EEE MMM dd HH: mm: ss zzz yyyy - YYYY-ww-u"), j'obtiens

Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1 
 Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1 
 Mo Jan 04 00:00:00 MEZ 2027 - 2027-01-1
10
Thomas Kläger

Les définitions varient

Une semaine peut être définie de différentes manières. Par exemple, aux États-Unis, le premier jour de la semaine est considéré comme le dimanche le plus souvent tandis qu'en Europe et dans de nombreux autres endroits, le premier jour est le lundi.

De même, la semaine d'une année hebdomadaire peut également être définie de différentes manières.

  • La semaine # 1 contient le 1er janvier.
  • La semaine # 1 est la première semaine à contenir un jour de semaine particulier tel que le dimanche.
  • La semaine n ° 1 est la première semaine à contenir uniquement les jours de la nouvelle année, sans date de l'année précédente.
  • … et plus

Les classes héritées que vous utilisez utilisent implicitement les définitions spécifiées par un Locale. Les paramètres régionaux appliqués sont également implicites, en utilisant la valeur par défaut actuelle de la JVM Locale si vous ne spécifiez pas autrement.

Pour des détails encore plus intéressants sur la difficulté de définir une semaine, voir cette question, Comportement différent de WeekFields sur JVM 8 et JVM 10

ISO 8601

Il existe une norme internationale pratique pour la gestion de la date et de l'heure, ISO 8601 .

définition ISO 8601 d'une semaine est:

  • Les semaines commencent un lundi
  • La semaine # 1 a le premier jeudi de l'année
  • Une année comprend 52 ou 53 semaines.
  • Une année peut avoir un ou plusieurs jours à partir de l'année civile précédente et également de l'année civile suivante.

Je suggère d'utiliser la définition de la norme ISO 8601 autant que possible. Cette définition standard est simple et logique, avec une adoption croissante dans tous les secteurs.

Java.time

Les classes Java.time offrent un support pour la semaine de l'année en semaine dans la classe WeekFields .

LocalDate ld = LocalDate.of( 2019 , Month.JANUARY , 1 ) ;
long week = ld.get( WeekFields.ISO.weekOfWeekBasedYear() ) ;

Voir ceci code exécuté en direct sur IdeOne.com .

ld.toString (): 2019-01-01

semaine 1

org.threeten.extra.YearWeek

Mais si vous effectuez une grande partie de ce travail, je suggère d'ajouter la bibliothèque ThreeTen-Extra à votre projet. Vous trouverez la classe YearWeek utile. Cette classe propose plusieurs méthodes pratiques telles que la génération d'un LocalDate pour n'importe quel jour de la semaine.

LocalDate ld = LocalDate.of ( 2019 , Month.JANUARY , 1 );
YearWeek yw = YearWeek.from ( ld );
LocalDate startOfWeek = yw.atDay ( DayOfWeek.MONDAY );

ld.toString (): 2019-01-01

yw.toString (): 2019-W01

startOfWeek.toString (): 2018-12-31

Remarquez comment le premier jour de l'année de l'année hebdomadaire de 2019 est une date de l'année civile précédente, 2018 plutôt que 2019.

calendar for month of January 2019, with week number, showing first week starts in the previous calendar year on 2018-12-31


À propos de Java.time

Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent l'ancien hérité) gênant classes date-heure telles que Java.util.Date , Calendar , & SimpleDateFormat .

Pour en savoir plus, consultez le Tutoriel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 31 .

Le projet Joda-Time , maintenant en mode maintenance , conseille la migration vers les classes Java.time .

Vous pouvez échanger des objets Java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de chaînes, pas besoin de Java.sql.* Des classes.

Où obtenir les classes Java.time?

5
Basil Bourque