在 Java 中迭代日期



我需要遍历一系列日期。不知道第二天如何循环。 我正在使用java.util.Date.因此,plusDays(1)不能在 for 循环中用于获取下一个日期。

用于 for 循环date1 = new Date(date1.getTime() + (1000 * 60 * 60 * 24))。但是我认为每次在 for 循环中通过调用 new 来创建对象不是一个好主意。

请建议第二天循环服用的更好方法。

tl;博士

LocalDate.now().plusDays( 1 )  // Get tomorrow's date.

避免使用旧的日期时间类。

切勿使用java.util.Date

这个可怕的类几年前被JSR 310中定义的现代java.time类所取代。CalendarGregorianCalendarSimpleDateFormat同上。

LocalDate

对于仅日期值,不带时间,也不带时区,请使用LocalDate类。

致电LocalDate.now以获取当前日期。

对于任何特定时刻,日期因时区而异。因此,请指定要查看日期的时区。如果省略,则会隐式应用 JVM 的当前缺省时区。

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;  // Capture the current date as seen in a particular time zone.
for ( int i = 0 ; i < 7 ; i++ )
{
LocalDate localDate = today.plusDays( i );
System.out.println( "localDate = " + localDate );
}

运行时。

localDate = 2020-12-17
localDate = 2020-12-18
localDate = 2020-12-19
localDate = 2020-12-20
localDate = 2020-12-21
localDate = 2020-12-22
localDate = 2020-12-23

如果您熟悉 Java 流,请使用LocalDate::datesUntil

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );  // Capture the current date as seen in a particular time zone.
today.datesUntil( today.plusWeeks( 1 ) ).forEach( System.out :: println );

关于java.time

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

要了解更多信息,请参阅Oracle 教程。并搜索堆栈溢出以获取许多示例和解释。规范为 JSR 310。

Joda-Time项目现在处于维护模式,建议迁移到 java.time 类。

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

从哪里获得java.time类?

Java SE 8、Java SE 9、Java SE
  • 10、Java SE 11及更高版本 - 标准 Java API 的一部分,具有捆绑实现。
    • Java 9带来了一些小功能和修复。
  • Java SE 6 和 Java
  • SE7
    • 大多数java.time功能在ThreeTen-Backport中向后移植到 Java 6 和 7。
  • Android
    • 更高版本的 Android (26+) 捆绑了java.time类的实现。
    • 对于早期的Android(<26),一个称为API脱糖的过程带来了最初未内置于Android中的java.time功能的子集。
      • 如果脱糖不能提供你需要的东西,ThreeTenABP项目将ThreeTen-Backport(如上所述)适应Android。请参阅如何使用ThreeTenABP...

您可以通过将Date转换为LocalDate来使用plusDays()

import java.util.*;
import java.time.*;
public class MyClass {
public static void main(String args[]) {
Date date = new Date();
LocalDate dateCopy =  date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
System.out.println("Date:  " + dateCopy);

for(int counter = 1; counter <= 5; counter++){
dateCopy = dateCopy.plusDays(1);
System.out.println("Date:  " + dateCopy);
}
}
}

请注意,我使用相同的dateCopy变量,因为 plusDays() 返回LocalDate的副本,并且我不想创建新变量。

上面的代码将生成以下控制台日志:

Date:  2020-12-17
Date:  2020-12-18
Date:  2020-12-19
Date:  2020-12-20
Date:  2020-12-21
Date:  2020-12-22

所有现有的答案都很棒。这个答案提供了一些额外的信息,这些信息也可以解决你的问题,或者至少引导你朝着正确的方向前进。

使用现代 API:

import java.time.LocalDate;
class Main {
public static void main(String[] args) {
LocalDate startDate = LocalDate.of(2020, 12, 5);// 5th Dec 2020
LocalDate endDate = LocalDate.of(2020, 12, 15);// 15th Dec 2020
for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
System.out.println(date);
}
}
}

输出:

2020-12-05
2020-12-06
2020-12-07
2020-12-08
2020-12-09
2020-12-10

如果将日期作为字符串对象:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d/M/uuuu");
LocalDate startDate = LocalDate.parse("5/12/2020", dtf);// 5th Dec 2020
LocalDate endDate = LocalDate.parse("10/12/2020", dtf);// 10th Dec 2020
for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
System.out.println(date);
}
}
}

输出:

2020-12-05
2020-12-06
2020-12-07
2020-12-08
2020-12-09
2020-12-10

使用旧版 API:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(2020, 11, 5);// 5th Dec 2020
Date startDate = calendar.getTime();
calendar.set(2020, 11, 10);// 10th Dec 2020
Date endDate = calendar.getTime();
SimpleDateFormat sdfForOutput = new SimpleDateFormat("yyyy-MM-dd");
Date date = startDate;
for (calendar.setTime(startDate); !date.after(endDate); calendar.add(Calendar.DAY_OF_YEAR,
1), date = calendar.getTime()) {
System.out.println(sdfForOutput.format(date));
}
}
}

输出:

2020-12-05
2020-12-06
2020-12-07
2020-12-08
2020-12-09
2020-12-10

如果将日期作为字符串对象:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdfForParsing = new SimpleDateFormat("d/M/yyyy");
Date startDate = sdfForParsing.parse("5/12/2020");// 5th Dec 2020
Date endDate = sdfForParsing.parse("10/12/2020");// 10th Dec 2020
Calendar calendar = Calendar.getInstance();
SimpleDateFormat sdfForOutput = new SimpleDateFormat("yyyy-MM-dd");
Date date = startDate;
for (calendar.setTime(startDate); !date.after(endDate); calendar.add(Calendar.DAY_OF_YEAR,
1), date = calendar.getTime()) {
System.out.println(sdfForOutput.format(date));
}
}
}

输出:

2020-12-05
2020-12-06
2020-12-07
2020-12-08
2020-12-09
2020-12-10

请注意,java.util的日期时间 API 及其格式 APISimpleDateFormat已过时且容易出错。建议完全停止使用它们并切换到现代日期时间 API。有关现代日期时间 API 的更多信息,请参阅跟踪:日期时间

注意:出于任何原因,如果你必须坚持使用Java 6或Java 7,你可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到Java 6和7。

如果您正在为 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请查看通过脱糖提供的 Java 8+ API 和如何在 Android Project 中使用 ThreeTenABP。

正如其他人所说,如果有任何方法,请避免使用java.util.Date。更喜欢Basil Bourque的好答案。

有时我们需要与使用遗留日期时间类作为Date的遗留代码进行互操作,并且我们无法立即升级到java.time。

  • 如果您获得Date形成遗留 API,请使用 ariefbayu 的答案。

  • 如果对于迭代的每个日期,您需要一个用于旧 API 的老式Date对象,我将向您展示如何操作。

    LocalDate startDateInclusive = LocalDate.of(2021, Month.MARCH, 12);
    LocalDate endDateExlusive = LocalDate.of(2021, Month.MARCH, 17);
    startDateInclusive.datesUntil(endDateExlusive)
    .map(d -> d.atStartOfDay(ZoneId.systemDefault()))
    .map(ZonedDateTime::toInstant)
    .map(Date::from)
    .forEach(System.out::println);
    

我在America/Cambridge_Bay时区运行了这个片段并得到了以下输出:

Fri Mar 12 00:00:00 MST 2021
Sat Mar 13 00:00:00 MST 2021
Sun Mar 14 00:00:00 MST 2021
Mon Mar 15 00:00:00 MDT 2021
Tue Mar 16 00:00:00 MDT 2021

LocalDate.datesUnitil()给了我们一连串的LocalDate。通过一系列map操作,我将每个LocalDate转换为Date,最终打印出来。除了打印之外,您还需要调用一个或多个旧方法,例如:

.forEach(jud -> {
someLegayMethod(jud);
someOtherLegacyMethod(jud, "some other argument");
});

在上面的输出中,您会注意到 Java 知道夏令时 (DST) 是在 3 月 14 日引入的,因此从 3 月 15 日起,山区夏令时的时区缩写MDT,而不是山区标准时间的MST。当这两个Date物体都在00:00下落时,它们之间实际上只有23小时。

二手date1 = new Date(date1.getTime() + (1000 * 60 * 60 * 24))对于循环。但我认为通过以下方式创建对象不是一个好主意 每次在 for 循环中调用 new。

每次创建一个新对象都没有问题(除非在非常特殊的情况下)。代码不好还有其他几个原因:

  • 做我们自己的时间数学是一个坏习惯。我们应该依赖库类,因为

    A. 它提供了更清晰的代码

    B. 日期和时间数学通常比我们想象的要复杂,因此很容易出错;具体来说,我们可能会错过很多极端情况,这在测试中经常被忽视。

  • 代码假定一天始终为 24 小时。正如我上面所说,情况并非如此。

链接

Oracle 教程:日期时间解释如何使用 java.time。

最新更新