如何使用 Java 日历从日期中减去 X 天?

Posted

技术标签:

【中文标题】如何使用 Java 日历从日期中减去 X 天?【英文标题】:How to subtract X days from a date using Java calendar? 【发布时间】:2010-09-17 18:14:05 【问题描述】:

有人知道使用 Java 日历从日期中减去 X 天的简单方法吗?

我找不到任何函数可以让我直接从 Java 中的日期中减去 X 天。有人能指出我正确的方向吗?

【问题讨论】:

仅供参考,麻烦的旧日期时间类,如 java.util.Calendar 现在是 legacy,被 java.time 类取代。见Tutorial by Oracle。 【参考方案1】:

取自the docs here:

根据日历的规则,在给定的日历字段中添加或减去指定的时间量。例如,要从日历的当前时间减去 5 天,可以通过调用实现:

Calendar calendar = Calendar.getInstance(); // this would default to now
calendar.add(Calendar.DAY_OF_MONTH, -5).

【讨论】:

请小心执行此操作,因为它并不总是像您期望的那样滚动。 这个答案有很多赞成票,但使用起来安全吗?或者这样更好:***.com/a/10796111/948268 在这种情况下你不会使用Calendar.DAY_OF_MONTH,因为它不会像你想要的那样处理几个月之间的滚动。请改用Calendar.DAY_OF_YEAR 这应该是安全的,显然当你添加它时,它是 DAY_OF_MONTH 还是 DAY_OF_YEAR ***.com/q/14065198/32453 @carson 这种方法有哪些问题?【参考方案2】:

您可以使用add 方法并传递一个负数。但是,您也可以编写一个不使用 Calendar 类的更简单的方法,如下所示

public static void addDays(Date d, int days)

    d.setTime( d.getTime() + (long)days*1000*60*60*24 );

这将获取日期的时间戳值(自纪元以来的毫秒数)并添加适当的毫秒数。您可以为 days 参数传递一个负整数来进行减法。这将比“正确”的日历解决方案更简单:

public static void addDays(Date d, int days)

    Calendar c = Calendar.getInstance();
    c.setTime(d);
    c.add(Calendar.DATE, days);
    d.setTime( c.getTime().getTime() );

请注意,这两种解决方案都会更改作为参数传递的Date 对象,而不是返回一个全新的Date。如果需要,可以轻松更改任一功能以另一种方式执行。

【讨论】:

不要相信 .setTime 和 .add 是 Calendar 的静态方法。您应该使用实例变量 c。 @Edward:你是对的,感谢您指出我的错误,我已经修复了代码,现在它应该可以正常工作了。 小心第一种方式,例如在处理闰年时它可能会失败:***.com/a/1006388/32453 仅供参考,诸如 java.util.Calendar 这样麻烦的旧日期时间类现在是 legacy,被 java.time 类所取代。见Tutorial by Oracle。【参考方案3】:

Anson's answer 适用于简单的情况,但如果您要进行更复杂的日期计算,我建议您查看Joda Time。它会让你的生活更轻松。

在 Joda Time 仅供参考

DateTime dt = new DateTime();
DateTime fiveDaysEarlier = dt.minusDays(5);

【讨论】:

@ShahzadImam,查看DateTimeFormat。它将允许您使用任意格式将 DateTime 实例转换为字符串。它与 java.text.DateFormat 类非常相似。 从 Java 8 开始考虑使用 java.time docs.oracle.com/javase/8/docs/api/java/time/… 仅供参考,Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。见Tutorial by Oracle。【参考方案4】:

tl;博士

LocalDate.now().minusDays( 10 )

最好指定时区。

LocalDate.now( ZoneId.of( "America/Montreal" ) ).minusDays( 10 )

详情

与早期版本的 Java 捆绑在一起的旧日期时间类,例如 java.util.Date/.Calendar,已被证明是麻烦的、令人困惑的和有缺陷的。避开他们。

java.time

Java 8 及更高版本用新的 java.time 框架取代了那些旧类。见Tutorial。由JSR 310 定义,受Joda-Time 启发,并由ThreeTen-Extra 项目扩展。 ThreeTen-Backport 项目将这些类反向移植到 Java 6 和 7;将 ThreeTenABP 项目迁移到 android

这个问题很模糊,不清楚它要求的是仅日期还是日期时间。

LocalDate

对于仅日期,没有时间,使用LocalDate 类。请注意,时区对于确定诸如“今天”之类的日期至关重要。

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate tenDaysAgo = today.minusDays( 10 );

ZonedDateTime

如果您的意思是日期时间,则使用Instant 类在UTC 的时间线上获取片刻。从那里调整到时区以获取 ZonedDateTime 对象。

Instant now = Instant.now();  // UTC.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
ZonedDateTime tenDaysAgo = zdt.minusDays( 10 );


关于java.time

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

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

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

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10 及更高版本 内置。 标准 Java API 的一部分,带有捆绑实现。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 ThreeTen-Backport 中的大部分 java.time 功能都向后移植到 Java 6 和 7。 Android java.time 类的 Android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter 和more。

【讨论】:

这是一个比其他选项更好的改进(它也更新了很多并且使用了新的方法)。如果您现在正在访问此问题,这就是要走的路。【参考方案5】:
int x = -1;
Calendar cal = ...;
cal.add(Calendar.DATE, x);

java.util.Calendar#add(int,int)

【讨论】:

【参考方案6】:

与其按照 Eli 的建议编写自己的 addDays,我更愿意使用 Apache 中的 DateUtils。它非常方便,尤其是当您必须在项目中的多个位置使用它时。

API 说:

addDays(Date date, int amount)

将天数添加到返回新对象的日期。

请注意,它返回一个新的Date 对象,并且不会对前一个对象本身进行更改。

【讨论】:

【参考方案7】:

可以通过以下方式轻松完成

Calendar calendar = Calendar.getInstance();
        // from current time
        long curTimeInMills = new Date().getTime();
        long timeInMills = curTimeInMills - 5 * (24*60*60*1000);    // `enter code here`subtract like 5 days
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

        // from specific time like (08 05 2015)
        calendar.set(Calendar.DAY_OF_MONTH, 8);
        calendar.set(Calendar.MONTH, (5-1));
        calendar.set(Calendar.YEAR, 2015);
        timeInMills = calendar.getTimeInMillis() - 5 * (24*60*60*1000);
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

【讨论】:

这个答案是不明智的,使用可怕的旧日期时间类,这些类现在是遗留的,被 java.time 类取代。此外,此代码忽略了时区的关键问题,天真地假设每天 24 小时。另一个问题是使用日期时间类来解决仅日期问题。使用LocalDate 更简单、更合适。【参考方案8】:

我相信使用 java.time.Instant 类。

从当前时间减去 5 天并获得结果为日期的示例:

new Date(Instant.now().minus(5, ChronoUnit.DAYS).toEpochMilli());

再减1小时加15分钟的例子:

Date.from(Instant.now().minus(Duration.ofHours(1)).plus(Duration.ofMinutes(15)));

如果您需要更高的准确度,Instance 可以测量到纳秒级。处理纳秒部分的方法:

minusNano()
plusNano()
getNano()

另外,请记住,Date 不如 Instant 准确。我的建议是尽可能留在 Instant 课程中

【讨论】:

或者根据口味,稍微短一点:Date.from(Instant.now().minus(5, ChronoUnit.DAYS)) 不错!你说的对。我实际上更新了使用这个的答案,还展示了 java.time.Duration 类的用法,在这种情况下也非常有效。【参考方案9】:

有人推荐 Joda Time 所以 - 我一直在使用这个 CalendarDate 类http://calendardate.sourceforge.net

这对 Joda Time 来说是一个有点竞争的项目,但更基础的只有 2 个课程。因为我不想使用比我的项目更大的包,所以它非常方便并且非常适合我需要的东西。与 Java 对应物不同,它的最小单位是天,所以它实际上是一个日期(没有精确到毫秒或其他什么)。创建日期后,您要做的就是减去 myDay.addDays(-5) 之类的内容,以返回 5 天。您可以使用它来查找星期几之类的东西。 另一个例子:

CalendarDate someDay = new CalendarDate(2011, 10, 27);
CalendarDate someLaterDay = today.addDays(77);

还有:

//print 4 previous days of the week and today
String dayLabel = "";
CalendarDate today = new CalendarDate(TimeZone.getDefault());
CalendarDateFormat cdf = new CalendarDateFormat("EEE");//day of the week like "Mon"
CalendarDate currDay = today.addDays(-4);
while(!currDay.isAfter(today)) 
    dayLabel = cdf.format(currDay);
    if (currDay.equals(today))
        dayLabel = "Today";//print "Today" instead of the weekday name
    System.out.println(dayLabel);
    currDay = currDay.addDays(1);//go to next day

【讨论】:

【参考方案10】:

Eli Courtwright 第二种解法是错误的,应该是:

Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, -days);
date.setTime(c.getTime().getTime());

【讨论】:

Eli的解决方案中的函数名称为addDays;他的解决方案是正确的。如果要从日期中减去天数,请为 days 传递一个负值。 嗯,这是程序员在创建函数时必须指定的东西,不是吗? :P【参考方案11】:

我面临同样的挑战,我需要回退 1 天(即使前一天落入上一年或上个月,也应该能够回滚一天)。

我做了以下,基本上减去 24 小时 1 天。 someDateInGregorianCalendar.add(Calendar.HOUR, -24);

或者,我也可以这样做

GregorianCalendar cal = new GregorianCalendar();
    cal.set(Calendar.YEAR, 2021);
    cal.set(Calendar.MONTH, 0);
    cal.set(Calendar.DATE, 1);
    System.out.println("Original: " + cal.getTime());
    cal.add(Calendar.DATE, -1);
    System.out.println("After adding DATE: " + cal.getTime());

输出:

Original: Fri Jan 01 15:08:33 CET 2021
After adding DATE: Thu Dec 31 15:08:33 CET 2020

【讨论】:

以上是关于如何使用 Java 日历从日期中减去 X 天?的主要内容,如果未能解决你的问题,请参考以下文章

使用日历视图减去日期[重复]

如何从 Java 中的 Date 对象中减去 X 天?

如何从普通日期中减去天数?

如何在 TypeScript 中从日期中减去天数

如何从日期中减去一天?

从日期我只想在javascript / angularjs中减去1天