创建一个 datetime 对象来表示一个实例,而不管字符串操作的时区

Create a datetime object to represent an instance regardless of timezones for string manipulation

我的 String 形式如下:
month 形式为 01-12
day 的形式为 01-31
year 的形式例如2019
time 我不知道它的形式是可以包含毫秒还是类似于 HH:MM
所有这些都应该代表一个 "timestamp" 是准确的,即不相对于时区,而是例如如果我做了一个简化的例子:
day/month/year time 无论时区问题如何,它都应该被认为是正确的(我希望我能解释清楚)。
我的问题是:从这些对象中创建一些 LocalTime 或类似对象的最佳方法是什么,这样它就不会由于某些语言环境等设置而改变,并且我可以正确地进行任何我需要的字符串操作或获得一个纪元?

在我看来,你在这里问的是不可能或毫无意义的事情。

我们就拿评论里的例子来说吧。一名乘客预订了到达伊斯坦布尔时间为 25/04/2019 13:10 的航班。按照惯例,到达时间以到达机场的当地时间给出。由于伊斯坦布尔处于 UTC 偏移 +03:00,因此到达时间等于 2019-04-25 10:10 UTC。

公认的良好做法是在数据库中以 UTC 格式存储日期和时间。如果您使用的是 SQL 数据库,您通常应该使用它的 timestamp with time zone 数据类型(“with time zone”部分是一个谎言,因为您不能将时间戳与您选择的时区一起存储; 它始终是 UTC,但我只是说这是一个很好的做法,所以这很棒)。

假设您已将此作为用户的输入:25/04/2019 13:10。将其转换为 LocalDateTime 很容易(当您知道如何操作时;下面的代码示例)。如果我们不知道时区(例如 Europe/Istanbul 或 Asia/Istanbul)或 UTC 偏移量(例如 +03),则 不可能 将其存储为 UTC :00)。想象一下,乘客 John Doe Jr. 以前从未乘坐过飞机,也不知道按照到达机场当地时间给出到达时间的惯例。所以对他来说13:10可能是他所在时区的13:10(America/Chicago,等于18:10UTC),出发时区的13:10(America/New_York, 等于17:10 UTC), 13:10 UTC, 13:10 到达时区(等于10:10 UTC)或其他。

现在假设我们知道输入在Europe/Istanbul时区。然后转换为 UTC 很简单。我们将时间存储为 2019-04-25 10:10 UTC。现在它不会因为计算机或 JVM 的任何时区设置而改变。与数据库中的其他 UTC 时间进行比较很简单,并且您可以安全地忽略时区。当您需要向用户显示时间(例如,将其打印在票上)时,您可以转换为伊斯坦布尔时间 (13:10)(或用户想要的时区)。

不要为 纪元时间 烦恼,除非你使用的 API 需要它们。 标准 Java 纪元 定义为 1970 年 1 月 1 日 00:00 UTC。请注意,它是在 UTC 中定义的,因此它是一个明确定义的时间点。纪元时间是自纪元以来的秒数或毫秒数的有符号计数。我们的示例到达时间是自纪元以来的 1 556 187 000 秒,因此这是您的纪元时间。如您所见,它并不意味着人类可读性。您不会想要破译日志文件或 运行 以这种方式表示时间的调试会话。您也不想对以这种方式表示时间的数据库进行任何查询。

远离字符串操作。您的日期和时间是否仅在日期和时间对象中起作用。当您收到一个字符串时,将其解析为适当的日期时间对象。仅当您需要将其呈现给用户或将其作为字符串传输到另一个系统时,为此目的将其格式化为字符串。

Java date/time 类型

Java 为我们提供了以下日期和时间类型:

  • A LocalDateTime 是没有 UTC 偏移量或时区的日期和时间,例如 2019-04-25T13:10。所以它没有定义一个时间点。
  • 另一方面,Instant 是没有 UTC 偏移量或时区的时间点。所以它 而不是 定义日期和时间。它以 UTC 格式打印(例如,2019-04-25T10:10Z),并在内部表示为自纪元以来的秒数和纳秒数。
  • OffsetDateTime UTC 偏移量的日期和时间,例如 2018-04-25T13:10+03:00。所以这个确实定义了一个时间点并且确实定义了一天中的日期和时间。
  • A ZonedDateTime 是日期和时间 时区 ,例如 2018-04-25T13:10+03:00[Europe/Istanbul]。所以这也确实定义了一个时间点并定义了一天中的日期和时间。

它在代码中的样子

    DateTimeFormatter userInputFormatter = DateTimeFormatter.ofPattern("dd/MM/uuuu HH:mm");
    String userInput = "25/04/2019 13:10";
    ZoneId arrivalTimeZone = ZoneId.of("Europe/Istanbul");

    // Parse into a LocalDateTime
    LocalDateTime arrivalTimeLocal = LocalDateTime.parse(userInput, userInputFormatter);
    System.out.println("Arrival time:                           " + arrivalTimeLocal);

Arrival time: 2019-04-25T13:10

    // Convert to Instant in order to have a well-defined point in time
    Instant arrivalTime = arrivalTimeLocal.atZone(arrivalTimeZone).toInstant();
    System.out.println("Arrival point in time (printed in UTC): " + arrivalTime);

Arrival point in time (printed in UTC): 2019-04-25T10:10:00Z

    // Convert to UTC for storing in SQL database
    OffsetDateTime arrivalTimeUtc = arrivalTime.atOffset(ZoneOffset.UTC);
    System.out.println("Arrival time in UTC:                    " + arrivalTimeUtc);

Arrival time in UTC: 2019-04-25T10:10Z

编辑: 正如我所说,在您的 SQL 数据库中,您通常希望将 UTC 日期和时间保存在数据类型为 [=17= 的列中].详细信息取决于您的数据库和 JDBC 驱动程序。我相信在 MySQL(可能还有其他 DBMS)中,类型只是 timestamp。这是一个典型的例子:

    // Save into a database column of datatype timestamp with time zone
    PreparedStatement insert = yourDatabaseConnection.prepareStatement(
            "insert into your_table (your_timestamp_with_time_zone_column) values (?);");
    insert.setObject(1, arrivalTimeUtc);
    int rowsInserted = insert.executeUpdate();

如果 SQLite 没有时区或日期时间数据类型,更喜欢将 ISO 8601 格式存储在字符列中,因为这比数字列中的纪元时间更具可读性。 Instant.toString() 生成您需要的字符串:

    PreparedStatement insert = yourDatabaseConnection.prepareStatement(
            "insert into your_table (your_varchar_column) values (?);");
    insert.setString(1, arrivalTime.toString());
    int rowsInserted = insert.executeUpdate();

Instant.parse 将在检索后解析相同的字符串。

    // Convert to arrival time zone, e.g., for printing on ticket
    String arrivalTimeForUser = arrivalTime.atZone(arrivalTimeZone)
            .format(userInputFormatter);
    System.out.println("Formatted arrival time in local time:   " + arrivalTimeForUser);

Formatted arrival time in local time: 25/04/2019 13:10

链接