我正在try 获取特定时区中特定月份的开始和结束时间,特别是"Asia/Kolkata"(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

请帮我找出我可能遗漏了什么,或者建议一种替代方法.

推荐答案

tl;dr

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()

Details

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

你的例子是错误的.如果你每个月都来月经,你会错过最后一天的最后一秒.

最好将你的时间跨度定义为半开放,其中开始是inclusive,结束是exclusive.因此,一个月从第一天的第一个时刻开始,一直到(但不包括)第following个月的第一个时刻.使用这种安排,你不会失go 最后一秒.

ZonedDateTime endZdt = endDate.plusDays ( 1 ).atStartOfDay ( z );

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

你试图让…伯爵

Timestamp.valueOf(istDateTime.toLocalDateTime()).getTime();

…在两个方面是不正确的:

  • 您拨打了toLocalDateTime,它没有任何时区或与UTC的偏移量.在使用朋友圈时,不要使用LocalDateTime个对象.有关更多说明,请参阅正确的Answer by Johnson-Pint.
  • 您使用的是一个有严重缺陷的遗留类Timestamp,它在几年前就被JSR310中定义的现代java.time类所取代.避免使用遗留类,只使用java.time个类.

要将我们的两个时刻调整到与UTC的时间子午线的零时-分-秒的偏移量,提取Instant个对象.

Instant startInstant = startZdt.toInstant ( );
Instant endInstant = endZdt.toInstant ( );

从那里你可以得到你的毫秒数.

long start = startInstant.toEpochMilli ( );
long end = endInstant.toEpochMilli ( );

我不建议你用这种方式来表现时刻.毫秒计数是模棱两可的,因为它只是一个没有上下文的整数,这造成了模棱两可.1970年01月01T00:00Z只是几十个common epoch references个中的一个.此外,人类无法破译来自纪元的计数.这意味着错误可能很容易逃脱注意.

我建议您使用标准的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类,用于将一段时间表示为一对Instant个对象.该类提供了几个方便的方法,如containsabutsintersection.该类解析/生成标准ISO 8601格式的文本,两个时刻之间用斜杠隔开.

Interval monthInterval = Interval.of( startInstant , endInstant ) ;

Java相关问答推荐

使用log 4j2格式的Hibernate 显示SQL日志(log)

强制Mockito返回null而不是emtpy list

Javascript在边界中心调整ImageView大小

屏蔽字母数字代码的Java正则表达式

将数组整体转换为链接表

Java .类参数不通过构造函数传递

流迭代列表<;对象>;上的NoSuchElementException

为什么不应用类型推断?

Spring-Boot Kafka应用程序到GraalVM本机映像-找不到org.apache.kafka.streams.processor.internals.DefaultKafkaClientSupplier

内存和硬盘中的Zip不同,这会导致下载后的Zip损坏

如何将Java文档配置为在指定的项目根目录中生成?

为什么Collectors.toList()不能保证易变性

使用While循环打印素数,无法正常工作

如何在Spring Boot中创建可以将值传递给配置的&Enable&Quot;注释?

删除打印语句会影响功能...腐败在起作用?

带有可选部分的Java DateTimeForMatter

对角线填充二维数组

JXBrowser是否支持加载Chrome扩展?

Java CDI:@Singleton@Startup@Inject无法实现接口

如何使用java区分以下结果