Java 时间戳 - 如何创建一个日期为 2007 年 9 月 23 日的时间戳?

111

我该如何创建一个带有日期 23/09/2007 的时间戳?


注意:java.sql.Timestamp 是现在已经遗留,许多年前被现代的 java.time 类所取代,这些类是在 JSR 310 中定义的。 - Basil Bourque
8个回答

162

通过 Timestamp,我认为你是指 java.sql.Timestamp。 你会注意到这个类有一个接受 long 参数的构造函数。 你可以使用 DateFormat 类来解析它:

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date date = dateFormat.parse("23/09/2007");
long time = date.getTime();
new Timestamp(time);

1
常规的 ISO 日期格式为 yyyy-MM-dd,否则太好了! - vidstige
1
新建 Timestamp(time); 出现错误,没有接受 long 值的这种构造函数 :( - Bhanu Sharma
1
@Bhanu 文档显示这确实需要一个长整型值,并且似乎可以正常工作:http://docs.oracle.com/javase/7/docs/api/java/sql/Timestamp.html#Timestamp(long) - Hazok
请注意,像java.util.Datejava.util.Calendarjava.text.SimpleDateFormat这样的旧日期时间类现在已经成为遗留系统,被内置于Java 8及更高版本中的java.time类所取代。请参阅Oracle的教程 - Basil Bourque

134
这个怎么样?
java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf("2007-09-23 10:10:10.0");

1
时间戳 timestamp = Timestamp.valueOf("2007-09-23 10:10:10.0"); 在 JDK 7 中显示“valueOf”方法未定义于类型“Timestamp”。 - Ashish Ratan
新的Timestamp(time);出现错误,没有接受字符串值的构造函数 :( - Bhanu Sharma
@Bhanu 构造函数接收以毫秒为单位的Unix时间。如果您想从字符串中获取时间戳,请使用静态方法valueOf。 - Hazok
4
这似乎是这个问题的最佳答案。 - Hazok
1
最佳答案中的Date已经被弃用,使用它是不好的实践。这个答案更好。谢谢。 - Alberici

18

你所说的时间戳是指什么?如果你指的是自Unix纪元以来的毫秒数:

GregorianCalendar cal = new GregorianCalendar(2007, 9 - 1, 23);
long millis = cal.getTimeInMillis();

如果您想要一个实际的java.sql.Timestamp对象:

Timestamp ts = new Timestamp(millis);

2
错误且无法编译!编译错误:09是八进制数,但9超出了八进制范围。逻辑错误:月份是从0开始计算的,你将得到2007年10月23日。 - user85421
如果代码无法编译,我就不可能出现逻辑错误。 :) 说真的,Carlos,你发现得很好。八进制数我之前也注意到了,但还是粘贴错了。 :( - Matthew Flaschen
实际上,Timestamp构造函数已经被弃用了,你可以使用Timestamp.valueOf()方法。 - Shiva Komuravelly
@ShivaKomuravelly 并非所有的时间戳构造函数都已被弃用,至少不是以毫秒为单位的构造函数。以日期为参数的构造函数已被弃用。 - no name
这里有一个常量可以代替月份为9的写法:Calendar.SEPTEMBER - Guillaume Husta

14

简而言之

java.sql.Timestamp.from (
    LocalDate.of ( 2007 , 9 , 23 )
             .atStartOfDay( ZoneId.of ( "America/Montreal" ) )
             .toInstant()
)

java.time

让我们使用内置于 Java 8 及更高版本中的 java.time 框架展示代码来更新此页面。
这些新类受到 Joda-Time 的启发,由 JSR 310 定义,并由 ThreeTen-Extra 项目扩展。它们取代了早期 Java 版本中捆绑的臭名昭著的旧日期时间类。
在 java.time 中,Instant 是 UTC 时间线上的一个时刻。而 ZonedDateTime 是调整为某个时区(ZoneId)的 Instant。
时区在这里非常重要。没有应用时区,2007年9月23日 这一日期无法转换为时间线上的时刻。请考虑巴黎的新一天比蒙特利尔早,“昨天”仍未结束。
此外,java.sql.Timestamp表示日期和时间。因此,我们必须注入一天中的时间。我们假设您想要一天中的第一个时刻作为时间。请注意,由于夏令时和可能的其他异常情况,这不总是时间00:00:00.0
请注意,与旧的java.util.Date类和Joda-Time不同,java.time类型的分辨率为纳秒而不是毫秒。这与java.sql.Timestamp的分辨率相匹配。
请注意,java.sql.Timestamp有一个讨厌的习惯,即在通过其toString方法生成字符串表示形式时,隐式应用您JVM的当前默认时区到其日期时间值。在这里,您可以看到我的America/Los_Angeles时区应用。相比之下,java.time类更加明智,使用标准ISO 8601格式。
LocalDate d = LocalDate.of ( 2007 , 9 , 23 ) ;
ZoneId z = ZoneId.of ( "America/Montreal" ) ;
ZonedDateTime zdt = d.atStartOfDay( z ) ;
Instant instant = zdt.toInstant() ;
java.sql.Timestamp ts = java.sql.Timestamp.from ( instant ) ;

转储到控制台。

System.out.println ( "d: " + d + " = zdt: " + zdt + " = instant: " + instant + " = ts: " + ts );

当运行时。

d: 2007-09-23 = zdt: 2007-09-23T00:00-04:00 [美国/蒙特利尔] = instant: 2007-09-23T04:00:00Z = ts: 2007-09-22 21:00:00.0

顺便提一下,从JDBC 4.2开始,您可以直接使用java.time类型。不需要使用java.sql.Timestamp

  • PreparedStatement.setObject
  • ResultSet.getObject

关于java.time

java.time框架是Java 8及更高版本内置的。这些类取代了老旧的遗留日期时间类,例如java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。

了解更多信息,请参见 Oracle 教程。并搜索 Stack Overflow 获取许多示例和解释。规范是 JSR 310

您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。无需字符串,也不需要 java.sql.* 类。

如何获取 java.time 类?

ThreeTen-Extra项目扩展了java.time的附加类。此项目是java.time可能未来添加内容的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuarter更多

3
我想知道如何让OP或版主将你的回答标记为正确答案。被采纳的答案太过陈旧。 - Syed Ali
非常好,除了一个打字错误。asStartOfDay() 应该是 atStartOfDay()。 - Tullochgorum
@Tullochgorum 已修复。谢谢。顺便说一下,在 Stack Overflow 上,如果您愿意,可以自己进行此类编辑。 - Basil Bourque

6
你也可以执行以下操作:
// untested
Calendar cal = GregorianCalendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH, 23);// I might have the wrong Calendar constant...
cal.set(Calendar.MONTH, 8);// -1 as month is zero-based
cal.set(Calendar.YEAR, 2009);
Timestamp tstamp = new Timestamp(cal.getTimeInMillis());

2
错误:尝试使用System.out.println输出结果!你会得到类似这样的东西:"2009-10-23 15:26:56.171"月份是基于0的,所以9表示十月! - user85421
我知道其中一个常量是以零为基础的,谢谢。帖子已更新。 - Alex
1
哦,我确实写上了“未经测试” :) - Alex

4
根据API,接受年份、月份等参数的构造函数已经被弃用。相反,您应该使用接受long类型参数的构造函数。 您可以使用Calendar实现来构建所需日期,并访问表示时间的long型数值,例如使用getTimeInMillis方法。

1

为了完整起见,这里也提供了一个使用Joda-Time版本2.5及其DateTime类的解决方案:

new Timestamp(new DateTime(2007, 9, 23, 0, 0, DateTimeZone.forID( "America/Montreal" )).getMillis())

1
很好的回答,但你错过了一个至关重要的部分:时区。如果省略时区,则JVM的当前默认时区将应用于DateTime对象。这意味着您的结果会因不同计算机或主机操作系统或JVM设置的配置而异。为了获得可预测的结果,请向该DateTime构造函数传递一个时区。选择适当的时区名称以符合您的意图。例如,DateTimeZone.forID("America/Montreal")DateTimeZone.UTC - Basil Bourque
那段代码可以更简洁。没有必要将其转换为java.util.Date以获取并传递自纪元以来的毫秒数。只需向DateTime对象询问其自纪元以来的毫秒数即可。用.getMillis()替换.toDate().getTime() - Basil Bourque
@BasilBourque 根据情况,您可能希望省略时区以获得可预测的结果。 “在您当前的时区”可以是一个完全合理和可预测的结果。 为什么要硬编码时区或要求用户手动设置时区,当他们的计算机已经设置了时区呢? 如果我的计算机上的应用程序开始输出根据 America/Montreal 时区调整的日期,我肯定会感到非常惊讶。 - dan carter
@dancarter 省略可选的时区会导致混淆。这种省略会增加歧义,即(a)程序员是否打算依赖隐式默认值或(b)程序员是否未考虑时区问题(这是非常常见的)。如果您确实想使用JVM的当前默认时区,请通过调用DateTimeZone.getDefault()并将结果作为可选参数传递来明确说明。 (顺便说一句,对于Locale.getDefault()也是如此,可选参数的歧义问题相同。) - Basil Bourque

-1
一个更通用的答案是导入java.util.Date,然后当你需要将timestamp设置为当前日期时,只需将其设置为new Date()

因为 Date("string"); 已经被弃用。 - Ashish Ratan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接