【问题标题】:Converting a separate date, hours, minutes, seconds, and AM/PM to a java.time.Instant将单独的日期、小时、分钟、秒和 AM/PM 转换为 java.time.Instant
【发布时间】:2020-11-12 09:44:07
【问题描述】:

我从前端收到一个单独的LocalDate(变量名称是date),以及用于小时、分钟、秒的单独整数和一个“AM”或“PM”字符串,我需要将这些组合成一个java.time.Instant 对象以存储在数据库中。我尝试如下构造LocalTime,如果这是下午时间,则添加12小时,然后构造Instant

LocalTime time = LocalTime.of("pm".equals(amPm) ? hours + 12: hours, minutes, seconds);

Instant instant = date.atTime(time).toInstant(ZoneOffset.UTC);

但是当我存储并重新加载页面时,虽然日期始终不变,但时间总是在改变。如果我将日期设置为 1/29/1900 并将时间设置为 07:01:01 AM,则我正在创建和存储的 Instant 在调试时具有值:1900-01-29T07:01:01Z正确,但是当页面重新加载时,时间显示为 02:01:01 AM,这就是存储在数据库中的时间。

我是不是在错误地构造时间或瞬间?

【问题讨论】:

  • 您的转换假定来自前端的时间是 UTC,这可能是不正确的。您需要知道前端的时区才能进行正确的转换。
  • 您收到的 12 点(无论是午夜还是中午)是 0 还是 12? 12 将是 12 小时制的常规值。你知道你收到的是大写还是小写的 AM 或 PM?您是否需要考虑不同的案例组合?
  • 如果前端以UTC传输时间,则1900-01-29T07:01:01Z对于Instant是正确的。将其存储到数据库时会出现问题吗?很可能是某种时区问题。
  • 另一方面,如果数据库使用某个非 UTC 时区,例如 America/New_York(不推荐,但有些数据库会这样做),那么 02:01:01 AM 可能是正确的,并且您的错误在于值的获取和/或显示。

标签: java time localtime java.time.instant


【解决方案1】:

毫无疑问,您的意外观察是由一个或多个时区问题引起的。

因此,您需要做的第一件事是确保您知道所涉及的时区。

  1. 您的前端使用哪个时区向您发送日期和时间?
  2. 您的数据库使用哪个时区来存储日期和时间并在您检查时显示给您? (建议使用 UTC 来存储时间。)

一旦你知道这一点,你可以检查:

  • 从 1/29/1900 07:01:01 AM 从某个时区的前端到 1900-01-29T07:01:01ZInstant 的转换是否正确? Instant 以 UTC 显示其时间(由结尾的 Z 表示)。
  • 数据库时区从Instant 到凌晨02:01:01 的转换是否正确?
  • 是否从数据库中正确获取时间?我假设您正在将其取回 Java 中?
  • Java 中的时间是否在前端正确转换为 02:01:01 AM?我再次假设在页面重新加载时您正在显示从数据库中获取的时间,但我认为您没有告诉我们,所以我可能是错的。

回答你的问题:

我是不是在错误地构造时间或瞬间?

视情况而定;这当然是可能的。

  • 您对时间的构造是假设pm 总是小写,并且 12 点钟(午夜或中午)被指定为 0。一方面我发现这两种假设或多或少不太可能,另一方面他们无法解释您观察到的 5 小时的差异。 12 通常在 12 小时制上给出为 12(不是 0)。您的问题以大写形式给出PM
  • Instant 的构造假定前端以 UTC 格式发送时间。在我看来,这听起来也不太可能,这可能是您观察到页面重新加载后显示的时间不正确的原因或原因之一。

代码示例

在下面的 sn-p 中,我做了相反的假设:12 被给出为 12,AM/PM 无论如何都可能是,前端时区是 America/New_York。可能还差得远,但可能有一个细节可供您选择并用于您的目的。

    DateTimeFormatter timeFormatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive() // Accept all of am, AM, aM and Am
            .appendPattern("h:m:sa")
            .toFormatter(Locale.US);
    ZoneId zone = ZoneId.of("America/New_York");
    
    LocalDate date = LocalDate.of(1900, Month.JANUARY, 29);
    int hours = 7;
    int minutes = 1;
    int seconds = 1;
    String amPm = "AM";
    
    String constructedTimeString
            = "" + hours + ':' + minutes + ':' + seconds + amPm;
    LocalTime time = LocalTime.parse(constructedTimeString, timeFormatter);

    Instant instant = date.atTime(time).atZone(zone).toInstant();
    
    System.out.println(instant);

输出是:

1900-01-29T12:01:01Z

极客部分:避免将时间格式化为字符串并将其解析回

我不禁思考是否可以让 java.time 解析 AM/PM 字符串,而不必为一天中的时间构造一个字符串并对其进行解析。有可能,但是我们需要使用低级的TemporalAccessor接口,否则通常是不必要的。

    DateTimeFormatter amPmFormatter = new DateTimeFormatterBuilder()
            .parseCaseInsensitive() // Accept all of am, AM, aM and Am
            .appendPattern("a")
            .toFormatter(Locale.US);

    int hours = 7;
    int minutes = 1;
    int seconds = 1;
    String amPm = "AM";
    
    TemporalAccessor parsedAmPm = amPmFormatter.parse(amPm);
    LocalTime time = LocalTime.of(0, minutes, seconds)
            .with(ChronoField.AMPM_OF_DAY, parsedAmPm.get(ChronoField.AMPM_OF_DAY))
            .with(ChronoField.CLOCK_HOUR_OF_AMPM, hours);

    System.out.println(time);

07:01:01

Instant 的构造像以前一样进行。

【讨论】:

    猜你喜欢
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多