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