如何获取具有日期,小时和分钟的ISO 8601格式的当前时刻?


323

什么是最优雅的方式 UTC当前时刻的ISO 8601格式表示?它应该看起来像:2010-10-12T08:50Z

例:

String iso8601 = DateFormat.getDateTimeInstance(DateFormat.ISO_8601).format(date);


5
@marcolopes当心,使用a private static SimpleDateFormat并不是线程安全的。
vegemite4me,2014年


此其他问题类似,但一个问题将截短至整秒,而该问题将截断至整分钟。
罗勒·布尔克

Answers:


292

使用SimpleDateFormat格式化任何Date你想要的对象:

TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); // Quoted "Z" to indicate UTC, no timezone offset
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

new Date()如上所示使用a 将格式化当前时间。


8
@Joachim这种“标准”方法的缺点是"yyyy-MM-dd'T'HH:mmZ",每次我需要一个ISO-8601格式的日期时,我的应用程序中都必须有许多实例。随着JodaTime,因为我看到,我可以使用预先定义的格式ISODateTimeFormat,它完成这个工作对我来说..
yegor256

8
@Joachim Z是SimpleDateFormat中的有效模式,请执行以下操作:yyyy-MM-dd'T'HH:mm'Z'
Buhake Sindi

33
-1这将为您提供当前时区中的日期/时间,而不是UTC。卡洛斯的答案包括必要的UTC代码。
Scott Rippey 2012年

16
我只是解决了时区错误-因为这是公认的答案,所以我认为这是正确的做法。
BoD

2
Z的引用必须像“ yyyy-MM-dd'T'HH:mm'Z'”
Kimball Robinson

224

对于默认时区不是UTC的系统:

TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

SimpleDateFormat如果经常需要,可以实例声明为全局常量,但是请注意,此类不是线程安全的。如果由多个线程同时访问,则必须同步。

编辑:如果进行许多不同的“时间/日期”操作,我希望使用乔达时间...编辑2
:纠正:setTimeZone不接受字符串(由保罗纠正)


9
在某些内置库中,这种格式不是恒定的吗?
Daniel Kaplan 2014年

“ DateFormat”有两种不同的软件包,请记住使用“ import java.text.DateFormat”
oabarca 2014年

3
如果要当前时区,请使用TimeZone tz = TimeZone.getDefault();。而是
oabarca

你应该不是在格式引用“Z”,因为这使得它在逻辑上不显着。如果如上例中的TimeZone为UTC,则未加引号的Z将仅在输出中导致Z ...但是,如果TimeZone不是UTC,则当时间可能不为Z时,Z仍将作为表示UTC的文字输出。是。如果使用方系统将Z读取为UTC,但时间不是,则时间将转移。相反,如果解析器使用带引号的Z而不设置TimeZone,它将解析UTC时间为本地时间(再次移位)。如果设置了TZ,则不需要此引号,但如果未设置,则很危险。
马特·惠普尔

@MattWhipple-但是,如果使用不带引号的Z,则结果将是类似的2016-06-14T13:56+0000-问题是要求类似的东西2016-06-14T13:56Z
user85421

184

Java 8本机

从Java 8开始,java.time使其变得简单,并且线程安全。

ZonedDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.ISO_INSTANT )

结果: 2015-04-14T11:07:36.639Z

您可能会想使用打火机 Temporal诸如Instant或的LocalDateTime,但它们缺少格式化程序支持或时区数据。只有ZonedDateTime工作的开箱即用。

通过调整或链接ZonedDateTimeDateTimeFormatter的选项/操作,您可以轻松控制,可以在一定程度上时区精度

ZonedDateTime.now( ZoneId.of( "Europe/Paris" ) )
             .truncatedTo( ChronoUnit.MINUTES )
             .format( DateTimeFormatter.ISO_DATE_TIME )

结果: 2015-04-14T11:07:00+01:00[Europe/Paris]

细化的要求(例如删除秒部分)仍必须通过自定义格式或自定义后期处理来满足。

.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME ) // 2015-04-14T11:07:00
.format( DateTimeFormatter.ISO_LOCAL_DATE ) // 2015-04-14
.format( DateTimeFormatter.ISO_LOCAL_TIME ) // 11:07:00
.format( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm" ) ) // 2015-04-14 11:07

对于Java 6和7,您可以考虑使用java.time的反向端口,例如ThreeTen-Backport,它也具有一个Android端口。两者都比Joda轻巧,并且从Joda的经验中学到了-尤其是。考虑到java.time是由Joda的作者设计的。


9
好答案。注意,显式指定时区而不是隐式依赖JVM当前的默认时区是一个好习惯。如果需要,您可以指定一个特定的时区,例如ZoneId.of( "America/Montreal" )
罗勒·布尔克

1
Instant只需调用.toString()方法即可将其格式化为ISO 8601格式:return DateTimeFormatter.ISO_INSTANT.format(this);
VadymVL

这是最好的,因为您避免使用默认值。
Christophe Roussy

6
警告:使用ISO_INSTANT的Java 8解决方案有一个令人讨厌的怪癖。当毫秒恰好为0时,它们不包含在输出中。根据ISO 8601标准,这是允许的,但是许多其他语言的库和解析器并不那么宽容。如果有必要始终包含零,请改用它:DateTimeFormatter ISO_8601 = ofPattern(“ yyyy-MM-dd'T'HH:mm:ss.SSSX”)。withZone(UTC);
jurgen

从Java 8切换到11之后,我看到DateTimeFormatter.ISO_INSTANTDateTimeFormatter.ISO_DATE_TIME在秒后使用6位数字,而不是3位,而Spring不再使用3位数字@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
DanielM

126

Java 8:

thisMoment = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mmX")
                              .withZone(ZoneOffset.UTC)
                              .format(Instant.now());

Java 8之前的版本:

thisMoment = String.format("%tFT%<tRZ",
                           Calendar.getInstance(TimeZone.getTimeZone("Z")));

文档

'R'    24小时制的时间格式为“%tH:%tM”
'F'    ISO 8601完成日期格式为“%tY-%tm-%td”。


26
好吧,您可以包括一个额外的第三方库作为您项目的依赖项(您可能希望保持最新状态,并随您的应用程序一起交付,嘿,它只是额外的3.2 MB),然后执行do DateTime dt = new DateTime(); DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); String str = fmt.print(dt);,或者您封装了return String.format("%tFT%<tRZ", new Date());上一堂课,然后str = Iso8601Time.now()在任何需要的地方做。(这并不是ISO格式会发生变化。)如果事实证明,您不仅需要ISO格式的当前日期,还可以使用lib。
aioobe 2010年

2
完全同意这一点。
yegor256

3
请注意,可以更改标记为已接受的答案;-)
aioobe 2010年

8
%tFT%<tTZ将包括秒;%tFT%<tTZ.%<tL将包括毫秒。
Patrick Linskey 2012年

2
一个非常简洁的解决方案,应该是现在已经被接受的答案:)
AbdelHady

94

从Java 8开始,您可以简单地执行以下操作:

Instant.now().toString();

java.time.Instant文档:

现在

public static Instant now()

从系统时钟获取当前时刻。

这将查询系统UTC时钟以获得当前时刻。

 


public String toString()

使用ISO-8601表示形式的此瞬间的字符串表示形式。

使用的格式与相同DateTimeFormatter.ISO_INSTANT


11
这是正确的答案,而且在所有这些炮制中都没有得到更高的评价,这是一个丑闻SimpleDateFormats
大卫·摩尔

很高兴我阅读了更多答案。我同意,这应该是现在的答案。
Xan-Kun Clark-Davis

而且由于是toString,因此在许多情况下,您甚至不需要显式调用它。
kaya3

1
警告:在前面的答案中,警告也适用于此:“警告:使用ISO_INSTANT的Java 8解决方案有一个令人讨厌的怪癖。当毫秒恰好是0时,它们不包含在输出中。根据ISO 8601,这是允许的标准,但许多其他语言的库和解析器并不那么宽容。”
Dave L.

27

使用JodaTime

ISO 8601日历系统是Joda-Time中的默认实现

是JodaTime Formatter的文档

编辑:

如果您不想添加或看不到添加上述库的价值,则可以在内置SimpleDateFormat类中使用,以将Date格式化为所需的ISO格式

如@Joachim Sauer所建议

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ");
String nowAsString = df.format(new Date());

3
您将如何生成问题中所示格式的String?
Joachim Sauer

4
我想说的是,不要为这样简单的事情添加库相关性(可以通过两行Java代码来实现)。(当然,如果需求增加,那就
另当别论了

1
@aioobe,如果它只是在应用程序的多个部分中使用,则可以默认使用,在其他情况下,jodatime更好。
Jigar Joshi

5
@aioobe jodatime在任何情况下都更好,因为它具有ISODateTimeFormat-ISO-8601的预定义格式化程序。
yegor256

3
@ org.life.java:仅当您可以正确切换到Jode时间时才有意义。仅使用Joda time的此功能时,将没有它们。
Joachim Sauer

20

Apache的DateFormatUtilscommons-lang3具有有用的常量,例如:DateFormatUtils.ISO_DATETIME_FORMAT


3
更准确地说,当前时刻的UTC:DateFormatUtils.format(new Date(), "yyyy-MM-dd'T'HH:mm'Z'", TimeZone.getTimeZone("UTC"));在默认时区当前时刻:DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(new Date()); @ yegor256感谢您的提及DateFormatUtils,拥有apache commons-lang值得使用日期格式。
babinik 2013年

19

如果您不希望加入Jodatime(尽管如此)

javax.xml.bind.DatatypeConverter.printDateTime(
    Calendar.getInstance(TimeZone.getTimeZone("UTC"))
);

返回一个字符串:

2012-07-10T16:02:48.440Z

与原始要求略有不同,但仍为ISO-8601。



10

乔达时代

更新:乔达时间的项目现在处于维护模式,与团队的建议迁移java.time类。对于Java 6和7,请参见ThreeTen- Backport项目,该项目在ThreeTenABP项目中进一步适用于Android 。

使用Joda-Time库…

String output = new DateTime( DateTimeZone.UTC ).toString() ;

这是线程安全的。Joda-Time创建新的不可变对象,而不是更改现有对象。

如果您确实想要求一种格式,而无需秒,而只能解析为分钟,那么请使用Joda-Time中许多其他内置的格式化程序之一。

DateTime now = new DateTime( DateTimeZone.UTC ) ;
String output = ISODateTimeFormat.dateHourMinute.print( now ) ;

java.time

对于Java 8和更高版本,Joda-Time继续工作。但是内置的java.time框架取代了Joda-Time。因此,您可以方便地将代码从Joda-Time迁移到java.time。

查看我的其他答案以获取现代解决方案。


关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

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

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能向java.time添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多


7

对于Java版本7

您可以遵循Oracle文档:http : //docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X-用于ISO 8601时区

TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

System.out.println(nowAsISO);

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
//nowAsISO = "2013-05-31T00:00:00Z";
Date finalResult = df1.parse(nowAsISO);

System.out.println(finalResult);

5

您可以将Java的SimpleDateFormatyyyy-MM-dd'T'HH:mm:ssXXXISO 8601 的以下模式一起使用。

示例代码:(列出所有可用时区)

for (String timeZone : TimeZone.getAvailableIDs())
{
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
    dateFormat.setTimeZone(TimeZone.getTimeZone(timeZone));
    String formatted = dateFormat.format(new Date());
    System.out.print(formatted);

    if (formatted.endsWith("Z"))
    {
        // These time zone's have offset of '0' from GMT.
        System.out.print("\t(" + timeZone + ")");
    }

    System.out.println();
}

您可以使用:

TimeZone.getDefault()

默认的虚拟机时区。这里更多

您可能会注意到几个以结尾的时区的日期时间'Z'。这些时区'0'与GMT的时间差为。

更多信息可以在这里找到。


5

我相信最简单的方法是先进入即时,然后像这样进行字符串化:

String d = new Date().toInstant().toString();

这将导致:

2017-09-08T12:56:45.331Z


4
如果您需要当前时间,则无需遍历老式Date对象。做吧Instant.now().toString()。如果您是Date从旧版API获得的,则您的方法是最好的方法。
Ole VV

作为OLE VV评论说,麻烦的旧日期,时间类,如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat现在的遗产,由取代java.time内置到Java 8和更高等级。请参见Oracle 教程
罗勒·布尔克

5

tl; dr

其他一些答案在推荐java.time类中是正确的,但是会根据您的特定需求使用不必要的长度。

Instant.now()                               // Capture the current moment in UTC with a resolution as fines nanoseconds but usually in microseconds or milliseconds.
       .truncatedTo( ChronoUnit.MINUTES )   // Lop off any seconds or fractional second, to get a value in whole minutes.
       .toString()                          // Generate a String in standard ISO 8601 format where a `T` separates the year-month-day from the hour-minute-second, and the `Z` on the end for “Zulu” means UTC.

2018-01-23T12:34Z

Instant::toString

jav.time.Instant类表示UTC片刻,总是在UTC。

Instant instant = Instant.now() ;

Instant.toString():2018-01-23T12:34:56.123456Z

Z您的例子字符串的结尾2010-10-12T08:50Z的发音是“祖鲁”和指UTC

您所需的格式恰好符​​合ISO 8601标准。解析/生成字符串时,java.time类默认使用这些标准格式。因此,无需指定格式化模式。就像Instant::toString上面看到的那样打电话。

如果您特别希望整分钟都没有秒或小数秒,请截断。通过ChronoUnit类指定时间单位。

Instant instant = Instant.now().truncatedTo( ChronoUnit.MINUTES ) ;
String output = instant.toString();  // Generate a `String` object in standard ISO 8601 format.

关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

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

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能向java.time添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多


2

这是一个优化的整个类,因此,调用“ now()”不会做更多的事情。

public class Iso8601Util
{
    private static TimeZone tz = TimeZone.getTimeZone("UTC");
    private static DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");

    static
    {
        df.setTimeZone(tz);
    }

    public static String now()
    {
        return df.format(new Date());
    }
}

9
将其设置为静态可能会引起麻烦,因为SimpleDateFormat并非线程安全的:“日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一种格式,则必须在外部进行同步。” docs.oracle.com/javase/7/docs/api/java/text/...
尤哈Palomäki

2
正如@JuhaPalomäki所提到的,这不是线程安全的。如果您使用的是Java 8或更高版本,则ThreadLocal.withInitial可以解决此问题。如果您使用的是Java 7或更低版​​本,请创建一个新的ThreadLocal并通过覆盖ThreadLocal.initialValue来
user393274

@ user393274-如果您使用的是Java 8,则应使用InstantDateTimeFormat以及其他现代线程安全的替代方法

我对SimpleDateFormat的问题是不是线程安全的感到困惑。如果两个线程同时执行静态初始化程序,那么我看不到发生什么比SimpleDateFormat构造函数被调用两次但发生的情况更糟的事情,但是只有其中的一个瞬间(最后一个完成)被记录为“ df”。
gilbertpilz

2
private static String getCurrentDateIso()
{
    // Returns the current date with the same format as Javascript's new Date().toJSON(), ISO 8601
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
    dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    return dateFormat.format(new Date());
}

1

不过,joda-time仅支持扩展格式:“ 2015-12-09T00:22:42.930Z”,不支持基本格式:“ 20151209T002242.930Z” ...我们最好使用Java SimpleDateFormat测试一系列格式。


1

我在Android中使用Calendar和SimpleDateFormat做到了。以下方法返回带有“ GMT”时区的日历(这是世界时区)。然后,您可以使用Calendar类,通过Calendar类的setTimeZone()方法来设置不同时区之间的小时。

private static final String GMT = "GMT";
private static final String DATE_FORMAT_ISO = "yyyyMMdd'T'HHmmss";

public static Calendar isoToCalendar(final String inputDate) {
    Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(GMT));
    try {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_ISO, Locale.US);
        dateFormat.setTimeZone(TimeZone.getTimeZone(GMT));
        Date date = dateFormat.parse(inputDate);
        calendar.setTime(date);
    } catch (ParseException e) {
        Log.e("TAG",e.getMessage());
    }
    return calendar;
}

记住:Date类不知道TimeZone的存在。因此,如果调试一个日期,则始终会看到当前时区的日期。


1

如果您关心性能,则可以创建一个在处理ISO8601格式日期方面优于标准Java解析器和格式化程序的。DatetimeProcessor实现是线程安全的,可以缓存在并发映射或静态字段中。

<dependency>
  <groupId>com.axibase</groupId>
  <artifactId>date-processor</artifactId>
  <version>1.0.3</version>
</dependency>
import com.axibase.date.DatetimeProcessor;
import com.axibase.date.PatternResolver;
import org.junit.Before;
import org.junit.Test;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

public class DateFormatTest {
    private Clock clock;

    @Before
    public void prepare() {
        clock = Clock.fixed(Instant.ofEpochMilli(1571285405300L), ZoneId.of("Europe/Berlin"));
    }

    @Test
    public void testIsoMillis(){
        final DatetimeProcessor formatter = PatternResolver.createNewFormatter("iso");
        assertThat(formatter.print(clock.millis(), ZoneOffset.UTC), is("2019-10-17T04:10:05.300Z"));
    }

    @Test
    public void testIsoMillisLocalZone(){
        final DatetimeProcessor formatter = PatternResolver.createNewFormatter("iso");
        assertThat(formatter.print(clock.millis(), clock.getZone()), is("2019-10-17T06:10:05.300+02:00"));
    }

    @Test
    public void testIsoMinutes(){
        final DatetimeProcessor formatter = PatternResolver.createNewFormatter("yyyy-MM-ddTHH:mmXXX");
        assertThat(formatter.print(clock.millis(), ZoneOffset.UTC), is("2019-10-17T04:10Z"));
    }
}

0

他们应该添加一些简单的方法来从Date转换为Instant,并且还增加了一种称为的方法toISO8601,这是很多人正在寻找的方法。作为其他答案的补充,从java.util.Date到ISO 8601格式:

Instant.ofEpochMilli(date.getTime()).toString();

使用自动完成功能时,它不是真正可见的,但是 java.time.Instant.toString()

使用ISO-8601的此瞬间的字符串表示形式


0
DateTimeFormatter.ISO_DATE_TIME
        .withZone(ZoneOffset.UTC)
        .format(yourDateObject.toInstant())

尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。
Vishal Chhodwani

-2

尝试这个,

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSZ");
        String date=sdf.format (new Date() );

对于ISO 8601格式


2
这将使用错误时区中的日期,请参见Carlos的答案。
斯蒂安·土壤和拉耶斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.