在没有固有 WithDayOfMonth 方法的情况下使用 DateTime 将日期设置为每月第一天的最佳方法

Best Way to Set Day to the First of the Month Using DateTime Without Inherent WithDayOfMonth Method

所以,我主要尝试获取 2 个 DateTime 对象并将它们设置为各自月份的第一天,以便我最终可以计算出两个日期之间的月份。

代码示例:

DateTime dt = new DateTime();
DateTime newDT = dt.withDayOfMonth(1);

在任何人问之前,实际代码将 Date 对象转换为 DateTime 对象,该对象在代码的另一部分中使用。

问题是,当我在单元测试中这样做时,它似乎工作得很好。但是,当我尝试使用 SOAP UI 对此进行测试时,我可以在调试过程中看到由于以下原因出现运行时异常:

method lookup failed for selector "withDayOfMonth" with signature "(I)Lorg/joda/time/DateTime;"

在相应的 server.txt 日志文件中,我可以看到一个堆栈跟踪,它表明 没有这样的方法 已经发生。

经过进一步研究,我发现我们的应用服务器目前使用的是过时版本的 JodaTime jar (1.2.1),而我的 eclipse 库包含正确的 jar (1.6.2)。

但是,现在的问题是什么是实现我的目标的最佳方法(创建一个新的 DateTime 对象并将每月的第一天设置为 0),因为我无法访问提供的 withDayOfMonth 方法来自 JodaTime?

根据两个月值的差值计算月数。例如,如果 newDate 是 2016 年 7 月 1 日,oldDate 是 2016 年 5 月 31 日,则 newDate.getMonth() 将 return 7,oldDate.getMonth() 将 return 5,并且差额将按要求四舍五入。

int months = newDate.getMonth() - oldDate.getMonth();    // 7 - 5 = 2

一种可能的方法,使用 Joda-Time API 的另一种方法,它存在于 1.2.1版本如下:

DateTime dateTime = new DateTime().dayOfMonth().withMinimumValue();

另一种方法是使用 jdk Calendar 设置日期的月份的第一天。然后使用 DateTime(Calendar cal) 构造函数获取 joda DateTime

Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH, 1); 
DateTime dateTime = new DateTime(cal);

然而,可能正如其他答案所建议的那样,你能做的最好的事情就是更新你的 Joda-Time 版本。

tl;博士

LocalDate firstOfThisMonth = 
    LocalDate.now( ZoneId.of( "America/Montreal" ) )
             .with( TemporalAdjusters.firstDayOfMonth() ) ;

详情

其他答案解决了您的 Joda-Time 问题。但是,Joda-Time 项目现在处于维护模式,团队建议迁移到 java.time classes。所以这是 java.time 代码中的解决方案。

LocalDate

LocalDate class 表示没有时间和时区的仅日期值。

时区对于确定日期至关重要。对于任何给定时刻,日期在全球范围内因地区而异。例如,Paris France is a new day while still “yesterday” in Montréal Québec.

午夜后几分钟
ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

TemporalAdjuster

TemporalAdjuster interface in java.time provides for classes to manipulate a value. The TemporalAdjusters class (note the plural s) provides several handy implementations of adjusters. One is firstDayOfMonth.

LocalDate firstOfThisMonth = today.with( TemporalAdjusters.firstDayOfMonth() ) ;

firstOfThisMonth.toString(): 2016-03-01

Period

Period class 跟踪未附加到时间轴的时间跨度。它保留了若干年、月和日。

LocalDate start = LocalDate.of ( 2016 , 1 , 1 ) ;
LocalDate stop = LocalDate.of ( 2016 , 3 , 1 ) ;
Period p = Period.between ( start , stop ) ;

Period 上调用 toString 生成标准 ISO 8601 格式的字符串。

P2M

您可以要求其中一部分作为数字,例如月数。

int months = p.getMonths();

2

请注意,此处显示的经过时间明智地使用了半开方法,其中开始是 包含 而结束是 不包含

关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

Joda-Time project, now in maintenance mode,建议迁移到java.time。

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

从哪里获得java.time classes?

  • Java SE 8 and SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and SE 7
  • Android
    • ThreeTenABP项目专门为Android改编了ThreeTen-Backport(上面提到的)。
    • 参见

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.