如何用日期格式yyyy-MM-dd hh:mm a解决Java 8中不可解析的日期异常

问题描述

我有一个文本文件,正在读取和设置事务POJO类数据,为了获取开始时间和结束时间之间的差额,我需要解析日期对象中的时间信息。

App.js

运行此代码时出现异常

例外是

export default function App() {
  const [inputValue,setInputValue] = React.useState("");
  const [checkedItems,setCheckedItems] = React.useState([]);

  return (
    <div className="App">
      <Input onChange={setInputValue} value={inputValue} />
      <List
        name="inputNumberOfCheckBoxes"
        inputValue={inputValue}
        checkedItems={checkedItems}
        setCheckedItems={setCheckedItems}
      />
      <Summary inputValue={inputValue} checkedItems={checkedItems} />
    </div>
  );
}

yyyy-MM-dd hh:mm a是我要传递给SimleDateFormat构造函数的日期格式。我无法理解和调试为什么收到此错误

解决方法

java.time

我建议您使用java.time(现代的Java日期和时间API)进行日期和时间工作。

    DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu\u2013MM\u2013dd");
    DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a",Locale.ENGLISH);
    
    String dateString = "2020–03–01";
    String timeString = "03:15 PM";
    
    LocalDate date = LocalDate.parse(dateString,dateFormatter);
    LocalTime time = LocalTime.parse(timeString,timeFormatter);
    LocalDateTime dateTime = date.atTime(time);
    
    System.out.println(dateTime);

输出:

2020-03-01T15:15

使用java.time可以在解析后合并日期和时间,因此我更喜欢单独解析它们。

您的代码出了什么问题?

信用会转到Ajeetkumar以进行注释并在评论中进行报告:日期字符串中的连字符不是通常的负号或字符值为2D十六进制(45十进制)的连字符,而是带有破折号的字符字符值2013十六进制(十进制8211)。因此,当您在格式模式字符串中指定常用的连字符时,它们将不匹配,并且解析会失败。相反,我使用Unicode转义符来将破折号放入格式模式字符串中。只需将其粘贴到那里也可以(只要您使用支持它的字符编码保存.java文件),但是我想让读者知道这里发生了一些特殊的事情,因此我更喜欢使用Unicode转义\u

您的代码还有另一个问题:您没有为格式化程序提供任何语言环境。因此,它使用JVM的默认语言环境。只要该语言环境期望PM,解析就会起作用。如果有一天您更改语言环境设置或在具有不同默认语言环境的计算机或JVM上运行代码,则解析将突然失败,这可能会导致您难以理解原因。我已指定英语语言环境来解析时间。即使从技术上讲是不必要的,有些人也希望在日期上这样做。

链接

,

我始终坚持这一口头禅:使用与您的日期时间字符串完全相同的格式。

在以下给出的解决方案中,我已将您的日期时间字符串复制到为SimpleDateFormatDateTimeFormatter指定的模式中,并用相应的字母替换了数字,例如与2020一起yyyy,同时保持其余所有内容(符号,空格等)完整。

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class Main {
    public static void main(String[] args) throws ParseException {
        DateFormat sdf = new SimpleDateFormat("yyyy–MM–dd hh:mm a",Locale.ENGLISH);// Make sure to use Locale
        String dateTimeString = "2020–03–01 03:15 PM";// The string copied from the exception you have got
        Date date = sdf.parse(dateTimeString);
        System.out.println(sdf.format(date));
    }
}

输出:

2020–03–01 03:15 PM

注意java.util的日期时间API及其格式API SimpleDateFormat已过时且容易出错。我建议您应该完全停止使用它们,并切换到modern date-time API

使用现代的日期时间API:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy–MM–dd hh:mm a",Locale.ENGLISH);// Make sure to use Locale
        String dateTimeString = "2020–03–01 03:15 PM";// The string copied from the exception you have got
        System.out.println(LocalDateTime.parse(dateTimeString,dtf));
    }
}

输出:

2020-03-01T15:15

通过 Trail: Date Time 了解有关现代日期时间API的更多信息。

如果您正在为自己的Android项目执行此操作,并且您的Android API级别仍不符合Java-8,请选中Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project