在Java中创建一个日历对象的临时副本

9
我需要找出如何创建一个临时的Calendar对象(已经存在的“永久”日历的副本),以便我可以操作这个副本:tempCal.add(unit, value)。我需要保持原始的日历对象不变,所以我真的不想直接在它上面调用add(unit, value)
由于我尝试创建副本的所有尝试都没有成功,我的当前丑陋的解决方法是调用permanentCal.add(unit, value),显示所需的结果,然后调用permanentCal.add(unit, -value) - 这似乎不太好。

1
FYI,诸如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat等存在严重缺陷的日期时间类现已成为遗留系统,被内置于Java 8及更高版本中的java.time类所取代。 - Basil Bourque
5个回答

20

我真的不敢相信我错过了那个,在使用 Eclipse 的情况下——这些该死的方法就在我的眼前列出来了...谢谢提供信息。 - Rich

3

如果您想在计算中使用时间、日期、月份、星期几、年份等内容,可以按以下方式复制并使用timeInMillis

Calendar calendarCopy = Calendar.getInstance()
calendarCopy.setTimeInMillis(calendarOriginal.getTimeInMillis)
doSomethingWith(calendarCopy)

这样你就可以在副本中包含时间、日期、月份、星期、年份等信息。

不建议进一步使用 Calendar。那个糟糕的类多年前就被 JSR 310 定义的 java.time 类所取代了。 - Basil Bourque
@BasilBourque 您说得对,但我们正在使用它来支持旧版本的Android而不使用外部库。 ZonedDateTime 仅从Android Oreo开始受支持。 谷歌仍在其新的Android Architecture Components示例中使用 Calendar - Yogesh Umesh Vaity
1
请参阅我的答案,了解如何在早期的Android中使用java.time功能的后移版本。再也不需要使用那些可怕的CalendarGregorianCalendar类了。 - Basil Bourque
值得一提的是,这个后移版本包括了方便地在传统类和现代类之间进行转换的方法。因此,在使用现代类处理所有业务逻辑的同时,您可以继续与尚未更新为java.time的旧代码进行交互。 - Basil Bourque

2
(不适用于Android)
转换为Java 8的不可变LocalDateTime,它是Calendar/Date的继承者。这个API是一个巨大的提升,可能一开始会有点压倒性。
有一个像minusDays(long)这样的方法将返回一个新的日期。这使得线程安全等问题得到了解决。例如,能够共享一个值而不担心有人会改变它。

2
建议使用 java.time 类。但是 LocalDateTime 是错误的特定类。遗留类 GregorianCalendar(通常是 Calendar 后面的具体类)已被 ZonedDateTime 替换。 - Basil Bourque
@BasilBourque 完全同意,因为 Calendar 主要用于区域设置相关的显示。而 ZonedDateTime 处理夏令时等问题。 - Joop Eggen

2

java.time.ZonedDateTime

Calendar类很糟糕,它的常见具体类GregorianCalendar也是如此。这些遗留类早已被现代的java.time类所取代,特别是ZonedDateTime

当遇到一个Calendar对象时,请检查它是否确实是一个GregorianCalendar对象。如果是,则使用添加到旧类中的新to…/from…方法进行转换。

if( myCal instanceof GregorianCalendar ) {
    GregorianCalendar gc = ( GregorianCalendar ) myCal ;  // Cast to the concrete type.
    ZonedDateTime zdt = gc.toZonedDateTime() ;  // Convert from legacy class to modern class.
}

不可变对象

java.time类明智地使用不可变对象模式,适用于它们的目的。

因此,您不会在现有对象中操作单个成员字段。相反,您传递一个新的字段值,以基于原始值和您特定的不同值的组合产生一个新对象。请参见with方法。

添加/减去一段时间

在您的情况下,似乎您想执行加法或减法以到达一个新时刻。

使用以下方式指定一段时间:

  • Duration用于小时-分钟-秒钟的比例
  • Period用于年-月-日的比例。

示例:添加几天以获得稍后的时刻。

Period p = Period.ofDays( 3 ) ;  
ZonedDateTime threeDaysLater = zdt.plus( p ) ;

为了完成这种工作,你可能会发现以下链接的ThreeTen-Extra库提供的IntervalLocalDateRange类很方便。

关于java.time

java.time框架内置于Java 8及更高版本。这些类取代了老旧的遗留日期时间类,如java.util.DateCalendarSimpleDateFormat


其中,java.time是一个链接。
要了解更多,请查看Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310Joda-Time项目现在处于维护模式,建议迁移到java.time类。
您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。无需字符串,也无需java.sql.*类。
从哪里获取java.time类?

enter image description here

该项目是ThreeTen-Extra,它扩展了java.time的附加类。该项目是java.time可能未来添加的一个试验场。您可能会在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter, 以及更多

1

Joda-Time

如果你想以酷炫的方式进行日期时间处理,可以使用Joda-Time库。

这个非常成功的第三方开源日期时间框架为现在内置于Java 8及更高版本中的java.time框架提供了灵感。


这听起来非常酷。在Android项目中包含Joda有多重要? - Rich
1
只需将joda jar添加到“libs/”目录中,Joda包含许多日期/时间助手。但是我没有注意到您正在开发Android应用程序。在这种特定情况下,Joda库可能对您来说有些过于繁琐。 - Grzegorz Gajos
没问题,更多是学术性的问题。 - Rich
谷歌一下,找到一些特殊版本的Joda-Time,以解决Android中的某些限制问题,从而加快初始加载速度。 - Basil Bourque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接