【问题标题】:Convert Java Date to UTC String将 Java 日期转换为 UTC 字符串
【发布时间】:2012-07-02 21:42:54
【问题描述】:

java.util.DatetoString() 方法以本地时区显示日期。

我们希望将数据打印在UTC 中的常见场景有几种,包括日志、数据导出以及与外部程序的通信。

  • 在 UTC 中创建 java.util.Date 的字符串表示形式的最佳方法是什么?
  • 如何将 j.u.Date 的 toString() 格式替换为更好的格式(感谢 @JonSkeet!)?

附录

我认为以自定义格式和时区打印日期的标准方式非常繁琐:

final Date date = new Date();
final String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
final SimpleDateFormat sdf = new SimpleDateFormat(ISO_FORMAT);
final TimeZone utc = TimeZone.getTimeZone("UTC");
sdf.setTimeZone(utc);
System.out.println(sdf.format(date));

我一直在寻找像这样的单线:

System.out.println(prettyPrint(date, "yyyy-MM-dd'T'HH:mm:ss.SSS zzz", "UTC"));

【问题讨论】:

  • “普通字符串格式”是什么意思?
  • 与打印 Java.Util.Date 的格式相同,“EEE MMM dd hh:mm:ss zzz yyyy”,例如“2012 年 7 月 2 日星期一 16:01:57 IDT”。
  • 亲爱的投票者,请提供信息。
  • 对于您提供的所有示例,这似乎是一种不好的格式。为什么不使用扩展的 ISO-8601 表示? (yyyy-MM-ddTHH:mm:ss.SSS) 它独立于语言环境、可排序、固定长度且更易于解析。
  • 默认值太差了,我希望很少使用它。使用SimpleDateFormat 做得更好并不是困难。

标签: java datetime string-formatting utc


【解决方案1】:

按照有用的 cmets,我完全重建了日期格式化程序。用法应该是:

  • 短一点(一班)
  • 将一次性对象(时区、格式)表示为字符串
  • 支持有用的、可排序的 ISO 格式和开箱即用的旧格式

如果您认为此代码有用,我可能会在 github 上发布源代码和 JAR。

用法

// The problem - not UTC
Date.toString()                      
"Tue Jul 03 14:54:24 IDT 2012"

// ISO format, now
PrettyDate.now()        
"2012-07-03T11:54:24.256 UTC"

// ISO format, specific date
PrettyDate.toString(new Date())         
"2012-07-03T11:54:24.256 UTC"

// Legacy format, specific date
PrettyDate.toLegacyString(new Date())   
"Tue Jul 03 11:54:24 UTC 2012"

// ISO, specific date and time zone
PrettyDate.toString(moonLandingDate, "yyyy-MM-dd hh:mm:ss zzz", "CST") 
"1969-07-20 03:17:40 CDT"

// Specific format and date
PrettyDate.toString(moonLandingDate, "yyyy-MM-dd")
"1969-07-20"

// ISO, specific date
PrettyDate.toString(moonLandingDate)
"1969-07-20T20:17:40.234 UTC"

// Legacy, specific date
PrettyDate.toLegacyString(moonLandingDate)
"Wed Jul 20 08:17:40 UTC 1969"

代码

(这段代码也是a question on Code Review stackexchange的主题)

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
 * Formats dates to sortable UTC strings in compliance with ISO-8601.
 * 
 * @author Adam Matan <adam@matan.name>
 * @see http://stackoverflow.com/questions/11294307/convert-java-date-to-utc-string/11294308
 */
public class PrettyDate {
    public static String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
    public static String LEGACY_FORMAT = "EEE MMM dd hh:mm:ss zzz yyyy";
    private static final TimeZone utc = TimeZone.getTimeZone("UTC");
    private static final SimpleDateFormat legacyFormatter = new SimpleDateFormat(LEGACY_FORMAT);
    private static final SimpleDateFormat isoFormatter = new SimpleDateFormat(ISO_FORMAT);
    static {
        legacyFormatter.setTimeZone(utc);
        isoFormatter.setTimeZone(utc);
    }

    /**
     * Formats the current time in a sortable ISO-8601 UTC format.
     * 
     * @return Current time in ISO-8601 format, e.g. :
     *         "2012-07-03T07:59:09.206 UTC"
     */
    public static String now() {
        return PrettyDate.toString(new Date());
    }

    /**
     * Formats a given date in a sortable ISO-8601 UTC format.
     * 
     * <pre>
     * <code>
     * final Calendar moonLandingCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
     * moonLandingCalendar.set(1969, 7, 20, 20, 18, 0);
     * final Date moonLandingDate = moonLandingCalendar.getTime();
     * System.out.println("UTCDate.toString moon:       " + PrettyDate.toString(moonLandingDate));
     * >>> UTCDate.toString moon:       1969-08-20T20:18:00.209 UTC
     * </code>
     * </pre>
     * 
     * @param date
     *            Valid Date object.
     * @return The given date in ISO-8601 format.
     * 
     */

    public static String toString(final Date date) {
        return isoFormatter.format(date);
    }

    /**
     * Formats a given date in the standard Java Date.toString(), using UTC
     * instead of locale time zone.
     * 
     * <pre>
     * <code>
     * System.out.println(UTCDate.toLegacyString(new Date()));
     * >>> "Tue Jul 03 07:33:57 UTC 2012"
     * </code>
     * </pre>
     * 
     * @param date
     *            Valid Date object.
     * @return The given date in Legacy Date.toString() format, e.g.
     *         "Tue Jul 03 09:34:17 IDT 2012"
     */
    public static String toLegacyString(final Date date) {
        return legacyFormatter.format(date);
    }

    /**
     * Formats a date in any given format at UTC.
     * 
     * <pre>
     * <code>
     * final Calendar moonLandingCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
     * moonLandingCalendar.set(1969, 7, 20, 20, 17, 40);
     * final Date moonLandingDate = moonLandingCalendar.getTime();
     * PrettyDate.toString(moonLandingDate, "yyyy-MM-dd")
     * >>> "1969-08-20"
     * </code>
     * </pre>
     * 
     * 
     * @param date
     *            Valid Date object.
     * @param format
     *            String representation of the format, e.g. "yyyy-MM-dd"
     * @return The given date formatted in the given format.
     */
    public static String toString(final Date date, final String format) {
        return toString(date, format, "UTC");
    }

    /**
     * Formats a date at any given format String, at any given Timezone String.
     * 
     * 
     * @param date
     *            Valid Date object
     * @param format
     *            String representation of the format, e.g. "yyyy-MM-dd HH:mm"
     * @param timezone
     *            String representation of the time zone, e.g. "CST"
     * @return The formatted date in the given time zone.
     */
    public static String toString(final Date date, final String format, final String timezone) {
        final TimeZone tz = TimeZone.getTimeZone(timezone);
        final SimpleDateFormat formatter = new SimpleDateFormat(format);
        formatter.setTimeZone(tz);
        return formatter.format(date);
    }
}

【讨论】:

  • @Michael-O:对这篇文章的最后一次编辑太离谱了,应该是评论。
  • @Michael-O:然后留下关于该事实的评论,如果它是真实的并且在答案的上下文中很重要,您的评论将得到支持,因此每个人都会看到它。
  • CDT、UDT 等确实不是 ISO-8601 时区说明符,这一点很有用。编辑劝告以否决答案是没有有用。
  • 为了清楚起见,声称"2012-07-03T11:54:24.256 UTC" 之类的日期时间字符串是“ISO 格式”并且应该被否决的答案是错误的是因为。 @Michael-O 以令人反感的方式引起对错误的关注这一事实并没有改变这一点。顺便说一下,迈克尔,如果你还没有,请参阅 meta.stackoverflow.com/questions/303570/…。
  • @Michael-O 如果你知道答案,为什么不发布呢?
【解决方案2】:

如果 XStream 是依赖项,请尝试:

new com.thoughtworks.xstream.converters.basic.DateConverter().toString(date)

【讨论】:

    【解决方案3】:

    为什么不直接使用 java.text.SimpleDateFormat ?

    Date someDate = new Date();
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    String s = df.format(someDate);
    

    或参见: http://www.tutorialspoint.com/java/java_date_time.htm

    【讨论】:

    • 在哪里转换为 UTC? OP 已经在使用 SimpleDateFormat。
    • 更新:可怕的类 Date 和 SimpleDateFormat 已被 JSR 310 中定义的现代 java.time 类所取代。
    【解决方案4】:

    tl;博士

    你问:

    我一直在寻找像这样的单线:

    问,你就会收到。从糟糕的遗留类Date 转换为现代替代品Instant。

    myJavaUtilDate.toInstant().toString()
    

    2020-05-05T19:46:12.912Z

    java.time

    在 Java 8 及更高版本中,我们内置了新的 java.time package (Tutorial)。受 Joda-Time 启发,由 JSR 310 定义,并由 ThreeTen-Extra 项目扩展。

    最好的解决方案是对日期时间对象而不是字符串进行排序。但如果您必须使用字符串,请继续阅读。

    Instant 代表时间线上的一个时刻,基本上在 UTC 中(有关详细信息,请参阅课程文档)。 toString 实现默认使用DateTimeFormatter.ISO_INSTANT 格式。此格式包括零位、三位、六位或九位数字,以显示精度高达nanosecond 的秒数。

    String output = Instant.now().toString(); // Example: '2015-12-03T10:15:30.120Z'
    

    如果您必须与旧的Date 类互操作,请通过添加到旧类的新方法与 java.time 进行转换。示例:Date::toInstant。

    myJavaUtilDate.toInstant().toString()
    

    如果您需要小数秒的位数一致或不需要小数秒,您可能需要使用备用格式化程序。

    如果你想截断几分之一秒,另一种方法是使用ZonedDateTime 而不是Instant,调用它的method to change the fraction to zero。

    请注意,我们必须为ZonedDateTime 指定一个时区(因此是名称)。在我们的例子中,这意味着 UTC。 ZoneID 的子类ZoneOffset 拥有一个方便的constant for UTC。如果我们省略时区,JVM 的current default time zone 会被隐式应用。

    String output = ZonedDateTime.now( ZoneOffset.UTC ).withNano( 0 ).toString();  // Example: 2015-08-27T19:28:58Z
    


    关于java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

    Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

    您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 和 JPA 2.2 支持 java.time。

    从哪里获得 java.time 类?


    乔达时间

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

    我一直在寻找单线

    如果使用Joda-Time 2.3 库,则很容易。 ISO 8601 是默认格式。

    时区

    在下面的代码示例中,请注意我指定了一个时区,而不是依赖于默认时区。在这种情况下,我根据您的问题指定UTC。末尾的Z,发音为“Zulu”,表示没有时区偏移 UTC。

    示例代码

    // import org.joda.time.*;
    
    String output = new DateTime( DateTimeZone.UTC );
    

    输出……

    2013-12-12T18:29:50.588Z
    

    【讨论】:

    • 投反对票没有评论?这个问题要求以 UTC 格式输出日期时间,我给了一个。
    • 一个使用 Joda-Time 处理现有 Date 实例的班轮:new DateTime(date).withZone(DateTimeZone.UTC).toString()
    【解决方案5】:

    以下基于the accepted answer above 的简化代码对我有用:

    public class GetSync {
        public static String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
        private static final TimeZone utc = TimeZone.getTimeZone("UTC");
        private static final SimpleDateFormat isoFormatter = new SimpleDateFormat(ISO_FORMAT);
        static {
            isoFormatter.setTimeZone(utc);
        }
    
        public static String now() {
            return isoFormatter.format(new Date()).toString();
        }
    }
    

    我希望这对某人有所帮助。

    【讨论】:

    • 请注意,与接受的答案一样,要实际获得符合 ISO 8601 的有效输出,您应该使用 ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXX"。
    • 更新:可怕的类 Date 和 SimpleDateFormat 已被 JSR 310 中定义的现代 java.time 类所取代。
    【解决方案6】:

    如果你只想使用 java.util.Date,这里有一个小技巧你可以使用:

    String dateString = Long.toString(Date.UTC(date.getYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()) );

    【讨论】:

      【解决方案7】:

      java.time.Instant

      只需使用Instant 中的java.time。

          System.out.println(Instant.now());
      

      这是刚刚打印出来的:

      2018-01-27T09:35:23.179612Z
      

      Instant.toString 总是给出 UTC 时间。

      输出通常是可排序的,但也有不幸的例外。 toString 为您提供了足够多的三位小数组来呈现它所拥有的精度。在我 Mac 上的 Java 9 上,Instant.now() 的精度似乎是微秒,但我们应该预计,在大约千分之一的情况下,它会达到整数毫秒并且只打印三位小数。小数位数不相等的字符串将按错误的顺序排序(除非您编写自定义比较器来考虑这一点)。

      Instant 是现代 Java 日期和时间 API java.time 中的类之一,我强烈建议您使用它来代替过时的 Date 类。 java.time 内置于 Java 8 及更高版本中,也已向后移植到 Java 6 和 7。

      【讨论】:

        猜你喜欢
        • 2021-04-19
        • 1970-01-01
        • 1970-01-01
        • 2020-03-21
        • 1970-01-01
        • 2018-05-22
        • 1970-01-01
        • 2015-10-24
        • 2016-02-28
        相关资源
        最近更新 更多