Java日期时间开始和结束时间,没有任何TimeZone信息

时间:2019-06-28 20:12:58

标签: java datetime jodatime

我有两个字符串作为startDate和endDate。这些值例如:

startDate="2019-09-29T06:00:00.000Z"
endDate="2019-10-06T05:59:59.999Z"

现在让我们说这些在“山区时间”中作为startDate的一天的开始和对于endDate的一天的结束。但是由于这些是字符串,我如何使程序理解这些可以转换为Mountain当地时间,例如:

startDate="2019-09-29T00:00:00.000Z"
endDate="2019-10-05T11:59:59.999Z"

并且时区是动态的。我可以从东部时间或任何其他时区获取时间戳字符串。
更新
我意识到我应该提供更多信息。
因此,我输入的山区时间就是一个例子。它可以来自东部时间或任何其他时区。此方法知道的唯一事实是startDate是某个时区中一天的开始,而endDate是该时区中一天的结束。我知道'Z'是UTC时间,但是要找出哪个时区在一天的开始时间(startDate)和一天的结束时间(endDate)产生了该UTC时间并转换回相应的本地时间,这是我面临的挑战正在面对。

2 个答案:

答案 0 :(得分:4)

最后的Z是时区信息,表示offsetUTC 00:00(又名零,又名Zulu),因此首先您将字符串解析为存储该类型的字符串日期,时间和Z时区。

使用Java 8 Time API,可以是InstantOffsetDateTimeZonedDateTime
如果您的输入始终以Z结尾,而不是其他偏移量,请使用Instant

然后,您将转换为所需的时区,在美国山区时间称为America/Denver

示例

String startDate = "2019-09-29T06:00:00.000Z";
String endDate = "2019-10-06T05:59:59.999Z";
ZoneId zone = ZoneId.of("America/Denver");

System.out.println(Instant.parse(startDate).atZone(zone));
System.out.println(OffsetDateTime.parse(startDate).atZoneSameInstant(zone));
System.out.println(ZonedDateTime.parse(startDate).withZoneSameInstant(zone));

System.out.println(Instant.parse(endDate).atZone(zone));
System.out.println(OffsetDateTime.parse(endDate).atZoneSameInstant(zone));
System.out.println(ZonedDateTime.parse(endDate).withZoneSameInstant(zone));

输出

2019-09-29T00:00-06:00[America/Denver]
2019-09-29T00:00-06:00[America/Denver]
2019-09-29T00:00-06:00[America/Denver]
2019-10-05T23:59:59.999-06:00[America/Denver]
2019-10-05T23:59:59.999-06:00[America/Denver]
2019-10-05T23:59:59.999-06:00[America/Denver]

如果您不想在转换后保留时区,则可以通过调用toLocalDateTime()将其删除,例如

System.out.println(Instant.parse(endDate).atZone(zone).toLocalDateTime());

输出

2019-10-05T23:59:59.999

请注意结尾处没有Z

答案 1 :(得分:0)

我不能不动手指,这很有趣。据我了解,您不知道开始和结束字符串来自哪个时区,可能是任何时区。除了依次尝试所有这些方法之外,我们还能做什么?

O(N)

此打印:

    String startDate = "2019-09-29T06:00:00.000Z";
    String endDate = "2019-10-06T05:59:59.999Z";

    LocalTime dayStart = LocalTime.MIN;
    LocalTime dayEndEarliset = LocalTime.of(23, 59, 59);

    Instant startInstant = Instant.parse(startDate);
    Instant endInstant = Instant.parse(endDate);
    // Just out of curiosity find candidate time zones
    Set<ZoneId> candidateZones = ZoneId.getAvailableZoneIds()
            .stream()
            .map(ZoneId::of)
            .filter(zid -> startInstant.atZone(zid).toLocalTime().equals(dayStart)
                    && ! endInstant.atZone(zid).toLocalTime().isBefore(dayEndEarliset))
            .collect(Collectors.toSet());
    System.out.println("Potential time zones: " + candidateZones);
    // Real work: find candidate date(s)
    Set<LocalDate> candidateDates = ZoneId.getAvailableZoneIds()
            .stream()
            .map(ZoneId::of)
            .filter(zid -> startInstant.atZone(zid).toLocalTime().equals(dayStart)
                    && ! endInstant.atZone(zid).toLocalTime().isBefore(dayEndEarliset))
            .map(zid -> startInstant.atZone(zid).toLocalDate())
            .collect(Collectors.toSet());
    if (candidateDates.isEmpty()) {
        System.out.println("Cannot identify a date from the instants");
    } else if (candidateDates.size() > 1) {
        System.out.println("Ambiguous date, candidates are " + candidateDates);
    } else {
        System.out.println("The date is " + candidateDates.iterator().next());
    }

是的,美国/丹佛是预期的候选时区之一。到目前为止,我已经找到开始日期。您可以以类似的方式找到开始日期和结束日期对。相对于流操作,您可能更喜欢老式循环。

但是,日期不会总是很明确。让我们尝试在太平洋/ Pago_Pago时区中设置一天的间隔。现在是偏移-11:00。

Potential time zones: [America/Inuvik, America/Yellowknife, America/Regina, America/Boise, SystemV/MST7MDT, America/El_Salvador, America/Costa_Rica, America/Shiprock, America/Guatemala, America/Denver, America/Belize, America/Swift_Current, America/Managua, Mexico/BajaSur, Canada/Mountain, America/Cambridge_Bay, Navajo, America/Chihuahua, America/Ojinaga, MST7MDT, Pacific/Galapagos, America/Mazatlan, US/Mountain, America/Edmonton, America/Tegucigalpa, Canada/Saskatchewan, Etc/GMT+6, SystemV/CST6]
The date is 2019-09-29

现在程序的输出为:

    ZoneId zone = ZoneId.of("Pacific/Pago_Pago");
    LocalDate testDate = LocalDate.of(2019, Month.OCTOBER, 2);
    String startDate = testDate.atStartOfDay(zone).toInstant().toString();
    String endDate = testDate.atTime(LocalTime.of(23, 59, 59, 999_000_000))
            .atZone(zone)
            .toInstant()
            .toString();

这是因为还有时区,偏移为+13:00。我们可以说偏移量必须是-11:00或+13:00,但是我们不能确定两者中的哪一个。这样就有两个日期。

使用半开时间间隔

我不确定结束时间是否总是在相关时区的23:59:59.999或有时23:59:59.999999或其他时间。同样,无论我们添加多少个小数,理论上仍然存在在该结束时间之后但仍在同一日期内的时刻。在代码中,我保守地接受23:59:59及之后的任何时间作为结束时间。完全避免疑问的点是:使用半开间隔。如上所示,将间隔的开始表示为00:00:00,将结束日期表示为00:00:00,该日期位于最后一个日期之后的 中。并将开始日期视为包含日期,结束日期视为排除范围