web-dev-qa-db-fra.com

Java: impossible d'obtenir LocalDate de TemporalAccessor

J'essaie de changer le format d'une date String de EEEE MMMM d en MM/d/yyyy en le convertissant d'abord en une LocalDate, puis en appliquant un formateur d'un modèle différent à la LocalDate avant de l'analyser de nouveau en String

Voici mon code:

private String convertDate(String stringDate) 
{
    //from EEEE MMMM d -> MM/dd/yyyy

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
            .toFormatter();

    LocalDate parsedDate = LocalDate.parse(stringDate, formatter);
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");

    String formattedStringDate = parsedDate.format(formatter2);

    return formattedStringDate;
}

Cependant, je reçois ce message d'exception que je ne comprends pas vraiment: 

Exception in thread "main" Java.time.format.DateTimeParseException: Text 'TUESDAY JULY 25' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {DayOfWeek=2, MonthOfYear=7, DayOfMonth=25},ISO of type Java.time.format.Parsed
    at Java.time.format.DateTimeFormatter.createError(DateTimeFormatter.Java:1920)
4
user7049000

Comme les autres réponses l'ont déjà dit, pour créer une LocalDate, vous avez besoin de year, qui ne figure pas dans l'entrée String. Il n'a que jour, mois et jour de la semaine.

Pour obtenir la variable LocalDate complète, vous devez analyser le jour et le mois et rechercher un an dans lequel cette combinaison jour/mois correspond au jour de la semaine.

Bien sûr, vous pouvez ignorer le jour de la semaine et supposer que la date est toujours dans le année en cours; dans ce cas, les autres réponses apportaient déjà la solution. Mais si vous voulez trouver le année qui correspond exactement au jour de la semaine, vous devez effectuer une boucle jusqu'à ce que vous le trouviez.

Je crée également un formateur avec un Java.util.Locale, afin de préciser que je veux les noms mois et jour de la semaine en anglais. Si vous ne spécifiez pas de paramètres régionaux, il utilise les valeurs par défaut du système. Il n'est pas garanti que ce soit toujours l'anglais (il peut être modifié sans préavis, même au moment de l'exécution).

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .parseCaseInsensitive()
    .append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
    // use English Locale to correctly parse month and day of week
    .toFormatter(Locale.ENGLISH);
// parse input
TemporalAccessor parsed = formatter.parse("TUESDAY JULY 25");
// get month and day
MonthDay md = MonthDay.from(parsed);
// get day of week
DayOfWeek dow = DayOfWeek.from(parsed);
LocalDate date;
// start with some arbitrary year, stop at some arbitrary value
for(int year = 2017; year > 1970; year--) {
    // get day and month at the year
    date = md.atYear(year);
    // check if the day of week is the same
    if (date.getDayOfWeek() == dow) {
        // found: 'date' is the correct LocalDate
        break;
    }
}

Dans cet exemple, j'ai commencé l'année 2017 et j'ai essayé de trouver une date jusqu'en 1970, mais vous pouvez vous adapter aux valeurs qui correspondent à vos cas d'utilisation.

Vous pouvez également obtenir l'année en cours (au lieu d'une valeur arbitraire fixe) en utilisant Year.now().getValue().

2
user7605325

La documentation de LocalDate dit que 

LocalDate est un objet date-heure immuable qui représente une date, souvent considéré comme année-mois-jour. Par exemple, la valeur "2 octobre 2007" peut être stockée dans une date locale.

Dans votre cas, il manque dans l’entrée String un élément important de LocalDate, c’est-à-dire l’année. Ce que vous avez essentiellement est le mois et le jour. Donc, vous pouvez utiliser une classe adaptée à cette MonthDay . En utilisant cela votre code peut être modifié pour:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
                .toFormatter();

 MonthDay monthDay = MonthDay.parse(stringDate, formatter);
 LocalDate parsedDate = monthDay.atYear(2017); // or whatever year you want it at
 DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");

 String formattedStringDate = parsedDate.format(formatter2);
 System.out.println(formattedStringDate); //For "TUESDAY JULY 25" input, it gives the output 07/25/2017 
7
Pallavi Sonal

Voici le changement mineur que vous devez implémenter:

private static String convertDate(String stringDate) 
{
    //from EEEE MMMM d -> MM/dd/yyyy

    DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("EEEE MMMM dd")
                                        .parseDefaulting(ChronoField.YEAR, 2017)
                                        .toFormatter();

    LocalDate parsedDate = LocalDate.parse(stringDate, formatter);
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");

    String formattedStringDate = parsedDate.format(formatter2);

    return formattedStringDate;
}
  1. Ajouter l'année chronologique par défaut dans le formateur en utilisant .parseDefaulting(ChronoField.YEAR, 2017)

  2. Appelez la méthode en utilisant l'argument "Tuesday July 25" comme ceci convertDate("Tuesday July 25");

2
KayV

Une autre option consiste à procéder comme suit (tout comme l’autre répond un peu hacky), en supposant bien sûr que vous souhaitiez que la date tombe dans l’année en cours:

LocalDate localDate = LocalDate.parse(stringDate + " " +LocalDate.now().getYear(), DateTimeFormatter.ofPattern("EEEE MMMM d");
0
jwenting