当前位置: 首页 > 面试题库 >

将日期字符串(MM-dd)解析为默认年份中的Java日期

林承悦
2023-03-14
问题内容

我想将MM-dd格式的字符串解析为java
date。由于未指定年份,因此解析日期应为当前年份。只能解析有效的日期字符串,因此我应该setLenient(false)在中使用SimpleDateFormat

public static Date parseDate(String ds) throws ParseException {
    SimpleDateFormat df = new SimpleDateFormat("MM-dd");
    df.setLenient(false);
    Date d = df.parse(ds);
    Calendar cal = Calendar.getInstance();
    int year = cal.get(Calendar.YEAR);
    cal.setTime(d);
    cal.set(Calendar.YEAR, year);
    return cal.getTime();
}

在我传递参数“ 02-29”之前,这似乎工作得很好。今年(2012)是leap年,2012-02-29是有效日期,应该成功解析“ 02-29”。

我发现当我未在中指定年份部分时SimpleDateFormat,它将分析为1970年。而1970年不是a年,则“
02-29”将无法分析。因此,解析日期到1970年并在解析策略后设置当前年份并不完美。

用Java解析MM-dd格式的字符串到日期(日期应设置为当年)的最佳方法是什么?

PS1:我搜索了此主题,并在此站点中找到了许多问题和答案,但找不到满意的答案。PS2:df.setLenient(false);之所以重要,是因为只有有效的日期字符串才能成功解析。不应解析无效的日期字符串,例如“
01-32”,“ 02-30”等。

提前致谢。


问题答案:

在当年解析MM-dd格式的字符串…

MonthDay               // Represent a month-day as such, in a class designed for that purpose.
.parse (               // By default parses strings in standard ISO 8601 format.
    "--" + "02-29"     // Prepending a double-hyphen to make this input comply with ISO 8601.
)                      // Returns a `MonthDay` object.
.atYear(               // Get the date of this month-day in a specified year.
    Year.now( ZoneId.of( "Asia/Tokyo" ) ).getValue()  // Getting current year requires a time zone.
)                      // Returns a `LocalDate` object, a year-month-day without time zone and without time-of-day.

看到此代码在IdeOne.com上实时运行。

2019-02-28

java.time

现代解决方案使用Java 8及更高版本内置的行业领先的 java.time 类,并为Java 6&7和早期Android提供了反向端口。

MonthDay

每天的一个月由适当命名的MonthDay类表示。

ISO 8601中定义的月日标准格式是--MM-DD第一个破折号代表年份的占位符。缺省情况下, java.time 类中使用ISO
8601格式来解析/生成字符串。

您的输入几乎符合要求。您可以使用DateTimeFormatter对象定义格式设置模式。但是我只是将a --放在输入之前。

String input = "02-29" ;
String inputModified = "--" + input ;

然后默认解析。

MonthDay md = MonthDay.parse( inputModified ) ;

看到此代码在IdeOne.com上实时运行。

md.toString():–02-29

闰年

请注意,您的leap年问题就消失了。通过使用真正代表一个月而不是一瞬间的适当类型,我们不必担心leap年。

要获取该月日的日期,只需调用MonthDay::atYear即可获取一个LocalDate对象。通过一年数。

LocalDate leapYear2012 = md.atYear( 2012 ) ;

jumpYear2012.toString():2012-02-29

今年

获取当前年份的日期可能会让您感到意外。请注意,获取当前年份需要获取当前日期。获取 当前日期需要一个时区

时区对于确定日期至关重要。在任何给定时刻,日期都会在全球范围内变化。例如,法国巴黎午夜过后的几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天”

如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能会在运行时(!)期间随时更改,因此您的结果可能会有所不同。最好将您的期望/期望时区明确指定为参数。如果要使用JVM的当前默认时区,请通过调用明确意图ZoneId.systemDefault()。如果紧急,请与您的用户确认区域。

指定适当的时区名称,格式Continent/Region,如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4字母的缩写,例如EST或,IST因为它们
不是 真实的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

就我们而言,我们只关心年份。因此,我们可以使用Year类而不是LocalDate。但是对于时区也是如此。如果当前时刻恰好是除夕/日切换,那么该年份将在全球各地按时区变化。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Year y = Year.now( z ) ;
LocalDate currentYear = md.atYear( y.getValue() ) ;

currentYear.toString():2019-02-28

请注意,以上结果将自动处理leap年。2019年没有2月29日,因此 java.time 调整为28日。

解析为 LocalDate

或者,您可以直接解析为LocalDate。您将需要使用DateTimeFormatterBuilder该类来构建DateTimeFormatter默认为某一年的。

像这样:

ZoneId zKolkata = ZoneId.of( "Asia/Kolkata" ) ;
long yearNumber = Year.now( zKolkata ).getValue() ;
DateTimeFormatter formatter = new DateTimeFormatterBuilder().parseDefaulting( ChronoField.YEAR , yearNumber ).appendPattern( "MM-dd").toFormatter() ;
LocalDate ld = LocalDate.parse( "02-28" , formatter ) ;
System.out.println( "ld.toString(): " + ld ) ;

但是我不建议这样做。MonthDay针对您的问题,解决方案和意图,采用对象的方法要清晰得多。另一个好处:如果您得到这样的输入,我怀疑您可能需要像这样工作,而在MonthDay上课时,您手头上有一个对象来完成这项工作。

关于 java.time


java.time 框架是建立在Java
8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

要了解更多信息,请参见
Oracle教程 。并在Stack
Overflow中搜索许多示例和说明。规格为JSR 310。

现在处于维护模式的 Joda-
Time

项目建议迁移到java.time类。

您可以直接与数据库交换 java.time 对象。使用与JDBC
4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要类。java.sql.*

在哪里获取java.time类?

  • Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本-标准Java API的一部分,具有捆绑的实现。
    • Java 9添加了一些次要功能和修复。
  • Java SE 6Java SE 7
    • 大多数 Java.time 功能都在 ThreeTen- Backport 中反向移植到Java 6和7 。
  • 安卓系统
    • 更高版本的Android捆绑了 java.time 类的实现。
    • 对于早期的Android(<26), ThreeTenABP 项目改编了 ThreeTen-Backport (如上所述)。请参阅 如何使用ThreeTenABP…


 类似资料:
  • 当我将格式的字符串对象转换为时,它会给我 我试着这样做:

  • 问题内容: 当我将格式的字符串对象转换为它给我时 我正在尝试这样做: 问题答案: 这里有几个潜在的问题: 您未指定格式 您未指定语言环境 您未指定时区 您正在尝试将返回值(将是引用)转换为-,否则将失败 您想要类似:

  • 问题内容: 我正在尝试使用以下方式将dd-mm-yyyy格式的字符串转换为JavaScript中的日期对象: 包含格式为dd-mm-yyyy的日期。当我执行以下操作时,我得到“无效日期”: 这是因为符号“-”吗?我该如何克服? 问题答案: 分割为“-” 将字符串解析为所需的部分: 使用正则表达式 为什么不使用正则表达式? 因为您知道将要使用由连字符分隔的三部分组成的字符串。 但是,如果您要在另一个

  • 问题内容: 我想在Java中解析这种格式为dd / MM / yyyy [至dd / MM / yyyy]的String的最佳方法是什么。带[]的字符串是可选的,dd代表日期的2位数字表示,MM是月份的2位数字表示,yyyy是年份的4位数字表示。 更新资料 谢谢大家的快速响应,但是我忘了告诉您[]是象征可选的,字符串中没有[]示例字符串可能是 22/01/2010 从22/01/2010至23/0

  • 问题内容: 我有多个具有生日的联系人,并且所有联系人都已与移动设备同步。现在的问题是所有联系人的生日格式都不同,我想以“ dd-MM- yyyy”之类的特定格式显示所有生日日期。 因此,例如,一个同步联系人的生日为“ 1990-02-07”或“ 1988-06-15T22:00:00.000Z”或“ 12-02-1990”等,则应显示所有这些日期以特定格式“ dd-MM-yyyy”。 那我该如何解

  • 我有多个联系人的出生日期,并且都与移动设备同步。现在的问题是所有联系人都有不同的生日格式,我想以特定格式显示所有生日日期,例如“dd-MM-yyyy”。 例如,一个同步联系人的生日是“1990-02-07”或“1988-06-15T22:00:00.000Z”或“12-02-1990”等...那么所有这些日期都应该以特定的格式“dd-MM-yyyy”显示。 那么我该如何解决这个问题呢? 如有任何建