创建一个datetime对象以表示实例,而不管时区如何进行字符串操作

时间:2019-04-07 16:13:44

标签: java datetime jodatime datetime-format localdate

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

1 个答案:

答案 0 :(得分:1)

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

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

公认的良好做法是将日期和时间存储在数据库的 UTC 中。如果您使用的是SQL数据库,通常应使用其timestamp with time zone数据类型(“带时区”部分有点虚伪,因为您无法将时间戳存储在所选时区中;它始终是UTC,但我只是说这是个好习惯,所以这很棒)。

假设您已从用户25/04/2019 13:10获得此信息。将其转换为LocalDateTime很容易(如果您知道怎么做,请参见下面的代码示例)。如果我们不知道时区(例如欧洲/伊斯坦布尔或亚洲/伊斯坦布尔)或UTC偏移量(例如+03:00),则不可能存储在UTC中。想象一下,约翰·约翰·杜(John Doe Jr.)的旅客从未飞行过,也不知道约定将到达时间指定为到达机场的当地时间。因此对他来说13:10在他自己的时区(美国/芝加哥,等于18:10 UTC),在13:10在出发时区(美国/纽约,等于17:10 UTC), 13:10 UTC,到达时区的13:10(等于10:10 UTC)或其他时间。

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

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

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

Java日期/时间类型

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

  • LocalDateTime是没有UTC偏移量或时区的日期和时间,例如2019-04-25T13:10。因此,它定义时间点。
  • 另一方面,Instant是没有UTC偏移量或时区的时间点。因此,它定义日期和时间。它以UTC(例如2019-04-25T10:10Z)打印,并在内部以自该纪元以来的秒数和纳秒数表示。
  • OffsetDateTime是日期和时间具有UTC偏移量,例如2018-04-25T13:10+03:00。因此,确实定义了一个时间点,而确实定义了日期和一天中的时间。
  • 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);
  

到达时间: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);
  

到达时间点(以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);
  

UTC到达时间:2019-04-25T10:10Z

编辑:正如我所说,在您的SQL数据库中,您通常希望将日期和时间保存在UTC中的数据类型为timestamp with time zone的列中。详细信息取决于您的数据库和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);
  

格式化的本地时间到达时间:25/04/2019 13:10

链接