提问人:Sanjay Prajapat 提问时间:10/3/2023 更新时间:10/3/2023 访问量:104
在 java 中获取非 UTC 时区的月份开始时间和结束时间
Get month's start time and end time in non UTC timezone in java
问:
我正在尝试获取特定时区中特定月份的开始和结束时间,特别是“亚洲/加尔各答”(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();
}
但是,我注意到上面的代码根据计算机上配置的时区返回开始和结束时间。即使我明确指定了 ZoneId.of(“Asia/Kolkata”),它也不会产生预期的结果。
当我在设置为 UTC 时区的机器上测试 2023 年 9 月的代码时,我得到了以下输出:
开始时间: 1693526400000
结束时间:1696118399999
但是,无论计算机的时区如何,我都希望输出保持一致:
开始时间: 1693506600000
结束时间:1696098599999
请帮助我确定我可能遗漏的内容或建议替代方法。
答:
当您从 转换为 时,将引入本地时区:ZonedDateTime
Timestamp
return Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();
由于您正在经历 ,因此该方法将假定为本地时区。LocalDateTime
Timestamp.valueOf
从文档中:
public static Timestamp valueOf(LocalDateTime dateTime)
获取 来自对象的实例,其年、月、月日、小时、分钟、秒和纳时间值与提供的相同。
Timestamp
LocalDateTime
LocalDateTime
提供的
LocalDateTime
被解释为本地时区的本地日期时间。
相反,您可以从以下位置创建:Timestamp
Instant
return Timestamp.from(istDateTime.toInstant()).getTime();
不过,由于您似乎以毫秒为单位,因此您可以完全跳过该类型:long
Timestamp
return istDateTime.toInstant().toEpochMilli();
评论
java.sql.Timestamp
TL的;博士
YearMonth
.of( 2023 , Month.NOVEMBER )
.atDay( 1 )
.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) )
.toInstant()
.toEpochMilli()
...和:
YearMonth
.of( 2023 , Month.NOVEMBER )
.plusMonths( 1 )
.atDay( 1 )
.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) )
.toInstant()
.toEpochMilli()
详
2023 年 9 月
使用 java.time.YearMonth
类来表示月份。
YearMonth ym = YearMonth.of( 2023 , Month.NOVEMBER ) ;
特定月份在特定时区的开始和结束时间,特别是“亚洲/加尔各答”(UTC+05:30)。
首先获取日期。
LocalDate startDate = ym.atDay ( 1 );
LocalDate endDate = ym.atEndOfMonth ( );
获得第一天的第一刻。这样做需要一个时区。不要假设一天从 00:00 开始,因为某些区域中的某些日期可能会在另一个时间开始,例如 01:00。让 java.time 确定一天中的第一个时刻。
ZoneId z = ZoneId.of ( "Asia/Kolkata" );
ZonedDateTime startZdt = startDate.atStartOfDay ( z );
结束时间: 2023-09-30 23:59:59
您的示例不正确。如果你紧挨着你的月经,你将错过最后一天的最后一秒。
最好将你的时间跨度定义为半开,其中开始是包容的,而结束是排他性的。因此,一个月从第一天的第一刻开始,一直到下个月的第一刻,但不包括下个月的第一天。使用这种安排,您不会失去最后一秒。
ZonedDateTime endZdt = endDate.plusDays ( 1 ).atStartOfDay ( z );
显然,您希望将这两个时刻表示为自 UTC 1970-01-01T00:00Z 中所示的 1970 年第一个时刻的纪元引用以来的毫秒数。
你试图得到这个计数......
Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();
...在两个方面不正确:
- 您调用了该调用,该调用丢失了任何时区或与 UTC 的偏移量的概念。使用时刻时不要使用对象。有关更多解释,请参阅 Johnson-Pint 的正确答案。
toLocalDateTime
LocalDateTime
- 你使用了一个有严重缺陷的遗留类,该类在几年前被 JSR 310 中定义的现代 java.time 类所取代。避免使用传统类,而只使用 java.time 类。
Timestamp
要将我们的两个时刻调整为 UTC 时间子午线的偏移量为 0 小时-分钟-秒,请提取对象。Instant
Instant startInstant = startZdt.toInstant ( );
Instant endInstant = endZdt.toInstant ( );
从那里你可以得到你的毫秒数。
long start = startInstant.toEpochMilli ( );
long end = endInstant.toEpochMilli ( );
我不建议你以这种方式表现时刻。毫秒数是不明确的,因为它只是一个没有上下文的整数,这会产生歧义。1970-01-01T00:00Z 只是数十个常见纪元参考之一。此外,人类无法破译纪元的计数。这意味着错误可能很容易被忽视。
我建议您使用标准 ISO 8601 格式以文本方式传达日期时间值。在UTC中,例如。将日期部分与时间部分分开。是 的缩写,偏移量为零。2023-10-02T20:53:59Z
T
Z
+00:00
下面是一个完整的代码示例。
package work.basil.example;
import java.time.*;
public class MonthMoments
{
public static void main ( String[] args )
{
YearMonth ym = YearMonth.of ( 2023 , Month.NOVEMBER );
LocalDate startDate = ym.atDay ( 1 );
LocalDate endDate = ym.atEndOfMonth ( );
ZoneId z = ZoneId.of ( "Asia/Kolkata" );
ZonedDateTime startZdt = startDate.atStartOfDay ( z );
ZonedDateTime endZdt = endDate.plusDays ( 1 ).atStartOfDay ( z );
Instant startInstant = startZdt.toInstant ( );
Instant endInstant = endZdt.toInstant ( );
long start = startInstant.toEpochMilli ( );
long end = endInstant.toEpochMilli ( );
System.out.println ( "startZdt = " + startZdt );
System.out.println ( "endZdt = " + endZdt );
System.out.println ( "startInstant = " + startInstant );
System.out.println ( "endInstant = " + endInstant );
System.out.println ( "start = " + start );
System.out.println ( "end = " + end );
}
}
运行时:
startZdt = 2023-11-01T00:00+05:30[Asia/Kolkata]
endZdt = 2023-12-01T00:00+05:30[Asia/Kolkata]
startInstant = 2023-10-31T18:30:00Z
endInstant = 2023-11-30T18:30:00Z
start = 1698777000000
end = 1701369000000
要处理成对的时刻,我建议将 ThreeTen-Extra 库添加到您的项目中。该库包括 Interval
类,用于将时间跨度表示为一对对象。该类提供了几种方便的方法,例如 、 和 。该类解析/生成标准 ISO 8601 格式的文本,其中两个时刻用斜杠分隔。Instant
contains
abuts
intersection
Interval monthInterval = Interval.of( startInstant , endInstant ) ;
评论
YearMonth
1696098599999
30 September 2023 23:59:59.999 GMT+05:30
23:59:59.1
30 September 2023 23:59:59.999 GMT+05:30
… 23:59:59.999001
… 23:59:59.999002
… 23:59:59.999003
… 23:59:59.999000001
… 23:59:59.999000002
… 23:59:59.999000003
评论
Timestamp
Instant
toLocalDateTime()