在java中获取非UTC时区的月份开始时间和结束时间

San*_*pat 1 java time spring datetime date

我试图获取特定时区特定月份的开始和结束时间,特别是“亚洲/加尔各答”(UTC+05:30)。

例如,以下是 2023 年 9 月的开始和结束时间。

开始时间: 2023-09-01 00:00:00

结束时间: 2023-09-30 23:59:59

我用Java实现了以下功能:

public static Long getStartTimeStampOfMonth(Month month, Year year) {
    LocalDate startDate = LocalDate.of(year.getValue(), month, 1);
    LocalDateTime startDateTime = LocalDateTime.of(startDate, LocalTime.MIN);
    ZonedDateTime istDateTime = startDateTime.atZone(ZoneId.of("Asia/Kolkata"));
    return Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();
}

public static Long getEndTimeStampOfMonth(Month month, Year year) {
    LocalDate startDate = LocalDate.of(year.getValue(), month, 1);
    LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth());
    LocalDateTime endDateTime = LocalDateTime.of(endDate, LocalTime.MAX);
    ZonedDateTime istDateTime = endDateTime.atZone(ZoneId.of("Asia/Kolkata"));
    return Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();
}
Run Code Online (Sandbox Code Playgroud)

但是,我注意到上面的代码根据计算机上配置的时区返回开始和结束时间。即使我已明确指定 ZoneId.of("Asia/Kolkata"),它也不会产生所需的结果。

当我在设置为 UTC 时区的计算机上测试 2023 年 9 月的代码时,我得到以下输出:

开始时间:1693526400000

结束时间:1696118399999

但是,我希望输出保持一致,无论机器的时区如何:

开始时间:1693506600000

结束时间:1696098599999

请帮助我确定我可能缺少的内容或建议替代方法。

Mat*_*int 5

ZonedDateTime当您从 转换为 时,将引入本地时区Timestamp

return Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();
Run Code Online (Sandbox Code Playgroud)

由于您正在使用 a LocalDateTime,因此该Timestamp.valueOf方法将假定本地时区。

来自文档

public static Timestamp valueOf(LocalDateTime dateTime)

Timestamp从对象获取 的实例LocalDateTime,其与提供的 具有相同的年、月、日、时、分、秒和纳秒日期时间值LocalDateTime

提供的LocalDateTime被解释为当地时区的当地日期时间。

Timestamp相反,您可以从以下位置创建Instant

return Timestamp.from(istDateTime.toInstant()).getTime();
Run Code Online (Sandbox Code Playgroud)

不过,由于您似乎以毫秒long为单位,因此您可以完全跳过该类型:Timestamp

return istDateTime.toInstant().toEpochMilli();
Run Code Online (Sandbox Code Playgroud)

  • 一个很好解释的答案。由于老式的“java.sql.Timestamp”不是最终目标,因此我无条件同意跳过该类的 hack。 (2认同)

Bas*_*que 5

太长了;博士

\n
YearMonth\n.of( 2023 , Month.NOVEMBER )\n.atDay( 1 ) \n.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) \n.toInstant()\n.toEpochMilli()\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa6 和:

\n
YearMonth\n.of( 2023 , Month.NOVEMBER )\n.plusMonths( 1 )\n.atDay( 1 ) \n.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) \n.toInstant()\n.toEpochMilli()\n
Run Code Online (Sandbox Code Playgroud)\n

细节

\n
\n

2023 年 9 月

\n
\n

使用java.time.YearMonthclass 代表月份。

\n
YearMonth ym = YearMonth.of( 2023 , Month.NOVEMBER ) ;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

特定时区特定月份的开始和结束时间,特别是“亚洲/加尔各答”(UTC+05:30)。

\n
\n

首先获取日期。

\n
YearMonth\n.of( 2023 , Month.NOVEMBER )\n.atDay( 1 ) \n.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) \n.toInstant()\n.toEpochMilli()\n
Run Code Online (Sandbox Code Playgroud)\n

获得第一天的第一个时刻。这样做需要时区。不要假设一天从 00:00 开始,因为某些区域的某些日期可能在其他时间开始,例如 01:00。让java.time确定一天中的第一个时刻。

\n
YearMonth\n.of( 2023 , Month.NOVEMBER )\n.plusMonths( 1 )\n.atDay( 1 ) \n.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) \n.toInstant()\n.toEpochMilli()\n
Run Code Online (Sandbox Code Playgroud)\n
\n

结束时间: 2023-09-30 23:59:59

\n
\n

你的例子不正确。如果你的月经临近,你就会错过最后一天的最后一秒。

\n

最好将您的时间跨度定义为半开放式,其中开始是包容性的,而结束是排他性的。因此,一个月从第一天的第一时刻开始,一直到(但不包括)下个月的第一时刻。使用这种安排,您将不会失去最后一秒。

\n
ZonedDateTime endZdt = endDate.plusDays ( 1 ).atStartOfDay ( z );\n
Run Code Online (Sandbox Code Playgroud)\n

显然,您希望将这两个时刻表示为自 UTC 1970-01-01T00:00Z 中看到的 1970 年第一个时刻的纪元参考以来的毫秒数。

\n

您尝试获取该计数\xe2\x80\xa6

\n
Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa6 在两个方面是不正确的:

\n
    \n
  • 您拨打的电话toLocalDateTime丢失了任何时区或与 UTC 的偏移量的概念。LocalDateTime处理时刻时不要使用物体。有关更多解释,请参阅Johnson-Pint 的正确答案
  • \n
  • 您使用了一个有严重缺陷的遗留类Timestamp,该类多年前已被 JSR 310 中定义的现代java.time类取代。避免使用遗留类,仅使用java.time类。
  • \n
\n

要将我们的两个时刻调整为距 UTC 时间子午线零小时-分钟-秒的偏移,请提取Instant对象。

\n
YearMonth ym = YearMonth.of( 2023 , Month.NOVEMBER ) ;\n
Run Code Online (Sandbox Code Playgroud)\n

从那里你可以得到毫秒数。

\n
LocalDate startDate = ym.atDay ( 1 );\nLocalDate endDate = ym.atEndOfMonth ( );\n
Run Code Online (Sandbox Code Playgroud)\n

我不建议您以这种方式表现时刻。毫秒计数是不明确的,因为它只是一个没有上下文的整数,这会产生歧义。1970-01-01T00:00Z 只是数十个常见纪元引用之一。此外,人类无法破译纪元的计数。这意味着错误很容易被忽视。

\n

我建议您使用标准ISO 8601格式以文本方式传达日期时间值。在 UTC 中,一个例子是2023-10-02T20:53:59Z. 将T日期部分与时间部分分开。这Z是一个缩写+00:00,偏移量为零。

\n

这是一个完整的代码示例。

\n
ZoneId z = ZoneId.of ( "Asia/Kolkata" );\nZonedDateTime startZdt = startDate.atStartOfDay ( z );\n
Run Code Online (Sandbox Code Playgroud)\n

运行时:

\n
ZonedDateTime endZdt = endDate.plusDays ( 1 ).atStartOfDay ( z );\n
Run Code Online (Sandbox Code Playgroud)\n

要处理成对的时刻,我建议将ThreeTen-Extra库添加到您的项目中。该库包含该类Interval,以将一段时间表示为一对Instant对象。该类提供了几种方便的方法,例如containsabutsintersection。该类解析/生成标准 ISO 8601 格式的文本,其中两个时刻用斜杠分隔。

\n
Interval monthInterval = Interval.of( startInstant , endInstant ) ;\n
Run Code Online (Sandbox Code Playgroud)\n