比较两个 java.util.Dates 看它们是不是在同一天
Posted
技术标签:
【中文标题】比较两个 java.util.Dates 看它们是不是在同一天【英文标题】:Comparing two java.util.Dates to see if they are in the same day比较两个 java.util.Dates 看它们是否在同一天 【发布时间】:2011-01-31 20:12:04 【问题描述】:我需要比较两个Date
s(例如date1
和date2
)并得出一个boolean sameDay
,这两个Date
s 共享同一天是真的,如果它们是假的不是。
我该怎么做?这里似乎有一股混乱的旋风......如果可能的话,我想避免引入 JDK 之外的其他依赖项。
澄清一下:如果date1
和date2
共享相同的年月日,则sameDay
为真,否则为假。我意识到这需要了解时区...传入时区会很好,但只要我知道行为是什么,我就可以使用 GMT 或当地时间。
再次澄清:
date1 = 2008 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
=> sameDate = true
date1 = 2009 Jun 03 12:56:03
date2 = 2008 Jun 03 12:59:44
=> sameDate = false
date1 = 2008 Aug 03 12:00:00
date2 = 2008 Jun 03 12:00:00
=> sameDate = false
【问题讨论】:
澄清一下——你想知道两个 Date 对象是否在一周的同一天? 您要比较完整日期(日、月、年)还是仅比较月日? @Rob:不,同一天/月/年...我会澄清的。 那你为什么不用“等于”呢? 因为如果小时/分钟/秒不同,它们就不相等。 【参考方案1】:Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(date1);
cal2.setTime(date2);
boolean sameDay = cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
请注意,当涉及不同时区时,“同一天”并不像听起来那么简单。上面的代码将为两个日期计算相对于运行它的计算机使用的时区的日期。如果这不是您所需要的,您必须在确定“同一天”的确切含义后,将相关时区传递给 Calendar.getInstance()
调用。
是的,Joda Time 的 LocalDate
会让整个事情变得更加简洁和容易(尽管会出现涉及时区的同样困难)。
【讨论】:
谢谢,看起来它会做我想要的。在我的情况下,我正在比较一个系列中的连续日期,所以看起来我可以在我的系列中使用 Calendar 实例而不是 Date 实例。 @Jason 这可能不是一个好主意。 Calendar 的主要问题是它是一个非常重量级的类,具有很多内部状态,其中一些在其 equals() 实现中使用。如果你不为相等性比较你的日期并且不将它们放入 HashMaps,你应该没问题。 酷,谢谢,我只是使用这里询问的比较当前和前一天的逻辑。永远不要调用“equals”和“hashcode”函数。 @UmerHayat:代码比较的是一年中的哪一天,而不是一个月的哪一天。更少的比较,更短的代码。 建议:先比较DAY_OF_YEAR,就不用查年份了。好吧,这不像比较 int 真的很贵,但是...【参考方案2】:怎么样:
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
return fmt.format(date1).equals(fmt.format(date2));
如果需要,您还可以将时区设置为 SimpleDateFormat。
【讨论】:
(在我的情况下,我实际上使用的是 SimpleDateFormat,所以它看起来有点合适。) 看到这一点我真的很惊讶,但即使从性能的角度来看,这种SimpleDateFormat
方法实际上比这里提到的使用Calendar
s 的另一种方法要快。平均而言,它需要的时间是Calendar
方法的一半。 (至少在我的系统上)。赞一个!
这个答案的大部分成本是在 SimpleDateFormat 创建上。如果您想要更好的性能,请将其放在单例中的 threadLocal 字段中
我也在 Swift 中这样做。令人惊讶的是,如此简单的事情如何在大多数语言中都需要相同的 hack。 C# 是个例外 - if(d1.Date == d2.Date)...【参考方案3】:
我使用“apache commons lang”包来做这个(即org.apache.commons.lang.time.DateUtils)
boolean samedate = DateUtils.isSameDay(date1, date2); //Takes either Calendar or Date objects
【讨论】:
这使用了一个外部依赖...但是知道未来是件好事。 复制源代码就可以了:) 但是如果任何一天为空,它会抛出一个非法参数异常,这在某些情况下并不理想。 如***.com/a/2517824/1665809 中所述,如果涉及不同的时区,此解决方案可能不合适。 英国媒体报道。典型答案:当您只需要这一个功能时,通过添加这个大型库来增加您的应用程序大小。【参考方案4】:您可以通过计算每个日期的Julian Day Number 然后比较这些日期,避免外部依赖和使用日历对性能的影响:
public static boolean isSameDay(Date date1, Date date2)
// Strip out the time part of each date.
long julianDayNumber1 = date1.getTime() / MILLIS_PER_DAY;
long julianDayNumber2 = date2.getTime() / MILLIS_PER_DAY;
// If they now are equal then it is the same day.
return julianDayNumber1 == julianDayNumber2;
【讨论】:
但请注意,由于夏令时,fails to take into account 会更改一天的长度。但是直接Date
s 没有封装时区的概念,所以没有办法不随意地解决这个问题。
这是最快的解决方案——非常感谢,正是我想要的;因为我必须检查很多日期...
Nitpick:date1.getTime() 不返回儒略日数,而是自 1970-01-01 以来的毫秒数。巨大的差异。
这会在 UTC 时区执行比较。如果你想要你的本地时区或地球上的其他地方,你无法知道你得到的结果是否正确。【参考方案5】:
乔达时间
至于添加依赖项,恐怕 java.util.Date 和 .Calendar 真的太糟糕了,以至于我对任何新项目做的第一件事就是添加 Joda-Time 库。在 Java 8 中,您可以使用受 Joda-Time 启发的新 java.time 包。
Joda-Time 的核心是DateTime
类。与 java.util.Date 不同,它了解其分配的时区 (DateTimeZone
)。从 j.u.Date 转换时,分配一个区域。
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTimeQuébec = new DateTime( date , zone );
LocalDate
验证两个日期时间是否在同一日期的一种方法是转换为LocalDate
对象。
该转换取决于指定的时区。要比较 LocalDate
对象,它们必须已转换为相同的区域。
这是一个实用的小方法。
static public Boolean sameDate ( DateTime dt1 , DateTime dt2 )
LocalDate ld1 = new LocalDate( dt1 );
// LocalDate determination depends on the time zone.
// So be sure the date-time values are adjusted to the same time zone.
LocalDate ld2 = new LocalDate( dt2.withZone( dt1.getZone() ) );
Boolean match = ld1.equals( ld2 );
return match;
最好是另一个参数,指定时区而不是假设应该使用第一个 DateTime 对象的时区。
static public Boolean sameDate ( DateTimeZone zone , DateTime dt1 , DateTime dt2 )
LocalDate ld1 = new LocalDate( dt1.withZone( zone ) );
// LocalDate determination depends on the time zone.
// So be sure the date-time values are adjusted to the same time zone.
LocalDate ld2 = new LocalDate( dt2.withZone( zone ) );
return ld1.equals( ld2 );
字符串表示
另一种方法是创建每个日期时间的日期部分的字符串表示,然后比较字符串。
同样,指定的时区至关重要。
DateTimeFormatter formatter = ISODateTimeFormat.date(); // Static method.
String s1 = formatter.print( dateTime1 );
String s2 = formatter.print( dateTime2.withZone( dt1.getZone() ) );
Boolean match = s1.equals( s2 );
return match;
时间跨度
通用的解决方案是定义一个时间跨度,然后询问该跨度是否包含您的目标。此示例代码在 Joda-Time 2.4 中。请注意,“午夜”相关的类已被弃用。而是使用withTimeAtStartOfDay
方法。 Joda-Time 提供三个类以各种方式表示时间跨度:Interval、Period 和 Duration。
使用“半开”方法,其中跨度的开头包含在内,结尾不包含在内。
目标的时区可以与区间的时区不同。
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime target = new DateTime( 2012, 3, 4, 5, 6, 7, timeZone );
DateTime start = DateTime.now( timeZone ).withTimeAtStartOfDay();
DateTime stop = start.plusDays( 1 ).withTimeAtStartOfDay();
Interval interval = new Interval( start, stop );
boolean containsTarget = interval.contains( target );
java.time
Java 8 及更高版本附带java.time 框架。受 Joda-Time 启发,由 JSR 310 定义,并由 ThreeTen-Extra 项目扩展。见Tutorial。
Joda-Time 的制造者已指示我们所有人在方便时尽快迁移到 java.time。与此同时,Joda-Time 继续作为一个积极维护的项目。但预计未来的工作只会发生在 java.time 和 ThreeTen-Extra 而不是 Joda-Time。
简单总结一下 java.time……Instant
是 UTC 时间线上的一个时刻。应用时区 (ZoneId
) 以获取 ZonedDateTime
对象。要离开时间线,要获得日期时间的模糊不确定概念,请使用“本地”类:LocalDateTime
、LocalDate
、LocalTime
。
本答案的 Joda-Time 部分中讨论的逻辑适用于 java.time。
旧的 java.util.Date 类有一个新的 toInstant
方法用于转换为 java.time。
Instant instant = yourJavaUtilDate.toInstant(); // Convert into java.time type.
确定日期需要时区。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
我们将该时区对象应用于Instant
以获得ZonedDateTime
。从中我们提取一个仅日期值(LocalDate
),因为我们的目标是比较日期(不是小时、分钟等)。
ZonedDateTime zdt1 = ZonedDateTime.ofInstant( instant , zoneId );
LocalDate localDate1 = LocalDate.from( zdt1 );
对我们需要比较的第二个java.util.Date
对象执行相同的操作。我将只使用当前时刻。
ZonedDateTime zdt2 = ZonedDateTime.now( zoneId );
LocalDate localDate2 = LocalDate.from( zdt2 );
使用特殊的isEqual
方法来测试相同的日期值。
Boolean sameDate = localDate1.isEqual( localDate2 );
【讨论】:
java.time: 或者你可以做LocalDate localDate = LocalDate.fromDateFields(yourJavaUtilDate);
@CyberMew 呃? my LocalDate
class 中没有 fromDateFields()
。
对不起,我应该更清楚。该评论与 Joda-Time LocalDate 类相关。【参考方案6】:
将日期转换为 Java 8 java.time.LocalDate as seen here.
LocalDate localDate1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate localDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// compare dates
assertTrue("Not on the same day", localDate1.equals(localDate2));
【讨论】:
这是我最喜欢的答案。如果你想控制使用的时区而不是依赖计算机的设置,直接输入,例如ZoneId.of("America/Phoenix")
或ZoneId.of("Europe/Budapest")
,而不是系统默认值。【参考方案7】:
Java 8
如果您在项目中使用 Java 8 并比较 java.sql.Timestamp
,则可以使用 LocalDate
类:
sameDate = date1.toLocalDateTime().toLocalDate().equals(date2.toLocalDateTime().toLocalDate());
如果您使用的是 java.util.Date
,请查看 Istvan 的答案,该答案不那么模棱两可。
【讨论】:
使用 Java 8 是个好主意。您是否假设date1
和date2
是java.sql.Timestamp
?根据他们是Date
的问题,我会理解java.util.Date
。此外,您的代码使用计算机的时区设置,这在很多情况下都可以;我仍然希望在代码中明确说明这一事实。
@OleV.V.谢谢你的评论,我原来的回答确实是关于java.sql.Timestamp
。正如您所说,通常最好明确设置时区。【参考方案8】:
对于安卓用户:
您可以使用DateUtils.isToday(dateMilliseconds)
来检查给定日期是否为当天。
API 参考:https://developer.android.com/reference/android/text/format/DateUtils.html#isToday(long)
【讨论】:
此方法使用自 API 22 以来已弃用的对象时间:developer.android.com/reference/android/text/format/Time.html +1 适用于安卓开发者。该方法使用原语 long as 参数,只需使用DateUtils.isToday(x.getTime())
(x 是 java.util.date 实例)即可【参考方案9】:
private boolean isSameDay(Date date1, Date date2)
Calendar calendar1 = Calendar.getInstance();
calendar1.setTime(date1);
Calendar calendar2 = Calendar.getInstance();
calendar2.setTime(date2);
boolean sameYear = calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
boolean sameMonth = calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
boolean sameDay = calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
return (sameDay && sameMonth && sameYear);
【讨论】:
【参考方案10】:对于 Kotlin 开发人员,这是比较格式化字符串方法的版本:
val sdf = SimpleDateFormat("yyMMdd")
if (sdf.format(date1) == sdf.format(date2))
// same day
这不是最好的方法,但它很短且有效。
【讨论】:
SimpleDateFormat
类在几年前被 JSR 310 中定义的现代 java.time.DateTimeFormatter
类所取代。建议在 2019 年使用它是一个糟糕的建议。
这段代码忽略了时区的关键问题。对于任何给定的时刻,日期在全球因区域而异。【参考方案11】:
除了 Binil Thomas 解决方案
public static boolean isOnSameDay(Timestamp... dates)
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
String date1 = fmt.format(dates[0]);
for (Timestamp date : dates)
if (!fmt.format(date).equals(date1))
return false;
return true;
用法
isOnSameDay(date1,date2,date3 ...);
//or
isOnSameDay(mydates);
【讨论】:
【参考方案12】:使用 JAVA 8 我们可以将Date
对象转换为LocalDate
对象,因此我们可以这样做:
public static boolean isDatesAreDifferentDays(Date date1,Date date2)
LocalDate d1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate d2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
if(d1.getDayOfMonth() != d2.getDayOfMonth())
return false;
else if(d1.getMonth() != d2.getMonth())
return false;
else if(d1.getYear() != d2.getYear())
return false;
else
return true;
【讨论】:
【参考方案13】:您可以应用与 SimpleDateFormat 解决方案相同的逻辑,而无需依赖 SimpleDateFormat
date1.getFullYear()*10000 + date1.getMonth()*100 + date1.getDate() ==
date2.getFullYear()*10000 + date2.getMonth()*100 + date2.getDate()
【讨论】:
这些方法在 java.util.Date 中已弃用。您应该使用日历来执行此操作。它也是getYear,而不是getFullYear以上是关于比较两个 java.util.Dates 看它们是不是在同一天的主要内容,如果未能解决你的问题,请参考以下文章