Java月的最后一天问题

Java月的最后一天问题,java,android,date,calendar,Java,Android,Date,Calendar,我正在尝试创建一个覆盖整个月的日期范围,即 [开始日期;结束日期] 因此,我有一个参考日期,并尝试从中创建一个新的日期。我对“结束日期”有问题,因为我希望它接近一天的结束(即23:59:59) 我使用的代码如下所示: public static Date previousMonthLastDate(Date referenceDate) { Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));

我正在尝试创建一个覆盖整个月的日期范围,即

[开始日期;结束日期]

因此,我有一个参考日期,并尝试从中创建一个新的日期。我对“结束日期”有问题,因为我希望它接近一天的结束(即23:59:59)

我使用的代码如下所示:

  public static Date previousMonthLastDate(Date referenceDate) {
    Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    calendar.setTime(referenceDate);
    calendar.add(Calendar.MONTH, -1); // move to the previous month
    int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    calendar.set(Calendar.DAY_OF_MONTH, lastDay);
    // set the time to be the end of the day
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 59);
    calendar.set(Calendar.SECOND, 59);

    return calendar.getTime();
  }
此代码在Android仿真器上正常工作。然而,在真正的手机上运行它会给出错误的日期。因此,我假设这是某种时区问题

在电话里,不是说2010年8月31日,而是说2010年9月1日。该值将设置在将一天中的小时数设置为23的代码行之后

有什么想法吗


谢谢。

如果您希望时区取决于手机设置,则在创建日历时不应强制设置时区。只需使用:

Calendar calendar = Calendar.getInstance();

我无法回答为什么会发生这种情况,但您是否尝试过将其设置为下个月的第一天并减去1秒/毫秒

calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.add(Calendar.MILLISECOND, -1);

Calendar API为您提供了这一开箱即用的功能,以下是实现这一功能的技巧:

// Get the instance of the Calendar.
Calendar calendar = Calendar.getInstance();
// Set the date of the First day of Month
calendar.set(Calendar.DAY_OF_MONTH, 1);
// Roll it to previous day of Year to get the Last day of Month
calendar.roll(Calendar.DAY_OF_YEAR, -1);

编辑:您还必须设置小时、分钟、秒和毫秒。

我在这样的环境中工作:

使用此代码,我将日期设置为间隔:

Date day = new Date()
Calendar startDate = Calendar.getInstance();
Calendar endDate = Calendar.getInstance();

// Set time
startDate.setTime(day);
endDate.setTime(day);

startDate.set(Calendar.DAY_OF_MONTH, 1);
startDate.set(Calendar.HOUR_OF_DAY, 0);
startDate.set(Calendar.MINUTE, 0);
startDate.set(Calendar.SECOND, 0);
startDate.set(Calendar.MILLISECOND, 0);

endDate.set(Calendar.DAY_OF_MONTH, endDate.getActualMaximum(Calendar.DAY_OF_MONTH));
endDate.set(Calendar.HOUR_OF_DAY, 23);
endDate.set(Calendar.MINUTE, 59);
endDate.set(Calendar.SECOND, 59);
使用此代码,我可以得到间隔:

Date day = new Date()
Calendar startDate = Calendar.getInstance();
Calendar endDate = Calendar.getInstance();

// Set time
startDate.setTime(day);
endDate.setTime(day);

startDate.set(Calendar.DAY_OF_MONTH, 1);
startDate.set(Calendar.HOUR_OF_DAY, 0);
startDate.set(Calendar.MINUTE, 0);
startDate.set(Calendar.SECOND, 0);
startDate.set(Calendar.MILLISECOND, 0);

endDate.set(Calendar.DAY_OF_MONTH, endDate.getActualMaximum(Calendar.DAY_OF_MONTH));
endDate.set(Calendar.HOUR_OF_DAY, 23);
endDate.set(Calendar.MINUTE, 59);
endDate.set(Calendar.SECOND, 59);
tl;博士
  • 确定日期需要时区
  • 显式指定比隐式依赖JVM的当前默认时区更好
避免遗留类 您使用的是麻烦的旧日期时间类,现在是遗留的,被java.time类取代

使用java.time 使用java.time类,这项工作要容易得多

LocalDate
该类表示一个仅限日期的值,不包含一天中的时间和时区

时区 时区对于确定日期至关重要。在任何一个特定的时刻,世界各地的日期都因地区而异。例如,中午夜后几分钟是新的一天,而中仍然是“昨天”

大陆/地区
的格式指定,例如,或
太平洋/奥克兰
。切勿使用3-4个字母的缩写,如
EST
IST
,因为它们不是真正的时区,也不是标准化的,甚至不是唯一的(!)

始终明确指定时区。如果省略,则隐式应用JVM的当前默认时区。JVM中任何应用程序的任何代码都可以随时更改此默认值。因此,如果至关重要,请询问用户所需/预期的时区。如果不是很重要,您可以明确地请求默认值,以在代码中明确您的意图,而不是依赖隐式默认值的模糊性

ZoneId z = ZoneId.systemDefault();  // Get JVM’s current default time zone.
LocalDate today = LocalDate.now( z );
TemporalAdjuster
该接口提供了可以调整日期时间值的类。该类提供了几个方便的实现

LocalDate firstOfThisMonth = today.with( TemporalAdjusters.firstDayOfMonth() );
ZoneDateTime
若要仅将该日期转换为带有时间的日期,请应用时区
ZoneId
以获取
zoneDateTime
对象

不要假设一天的第一个时刻是00:00:00。由于夏令时(DST)等异常情况,第一个时刻可能与时间01:00:00类似。让java.time在startofDay调用

ZonedDateTime zdtStartOfMonth = firstOfThisMonth.atStartOfDay( z );
半开 你试图确定一个月的最后时刻,这是错误的做法。最后一刻有一个无限可分的分数秒。由于不同的系统使用不同的粒度,因此尝试解析为特定粒度是不明智的。旧的Java日期时间类使用毫秒,一些数据库(如Postgres)使用微秒,Java.time类使用纳秒,其他系统使用其他变体

日期-时间工作中通常使用的定义时间跨度的更明智方法是半开放的,即开始是包含的,而结束是独占的。这意味着一个月从月的第一天的第一个时刻开始,一直到下个月的第一个时刻,但不包括下个月的第一个时刻

调整到一个时区以获得一个特定的时刻

ZonedDateTime zdtStartOfNextMonth = firstOfNextMonth.atStartOfDay( z );
将某一时刻与这段时间进行比较的逻辑是:“这一时刻(a)等于或在开始之后,以及(b)小于结束吗?”。注意“b”部分中缺少“或相等”。这意味着我们正在接近,但不包括结局

此外,说“a”部分的较短方式是“不在开头之前”。因此,我们可以更简单地问,“这一刻不是在开始之前,而是在结束之前吗?”

ZonedDateTime moment = ZonedDateTime.now( z );
Boolean spanContainsMoment = ( ! moment.isBefore( zdtStartOfMonth ) ) && ( moment.isBefore( zdtStartOfNextMonth ) ) ;
顺便说一下,格式化时间跨度的文本表示的标准格式使用斜杠字符连接开始和结束

String output = zdtStartOfMonth.toString() + "/" + zdtStartOfNextMonth.toString() ;
间隔
可以使用库中的类表示此时间跨度。该类作为对象跟踪UTC的开始和结束。您可以从
ZoneDateTime
对象中提取
即时

Interval interval = Interval.of( zdtStartOfMonth.toInstant() , zdtStartOfNextMonth.toInstant() );
YearMonth
顺便说一句,你会发现这门课对你的工作很有用


关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

该项目现已启动,建议迁移到类

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

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

从哪里获得java.time类

  • 然后
    • 内置的
    • 标准JavaAPI的一部分,带有捆绑实现
    • Java9添加了一些次要功能和修复
    • 大部分java.time功能都在中向后移植到Java6和Java7
    • Android捆绑包实现的更高版本
      String output = zdtStartOfMonth.toString() + "/" + zdtStartOfNextMonth.toString() ;
      
      Interval interval = Interval.of( zdtStartOfMonth.toInstant() , zdtStartOfNextMonth.toInstant() );