如何使用 T 和 Z 将 LocalDate 格式化为 ISO 8601?

Posted

技术标签:

【中文标题】如何使用 T 和 Z 将 LocalDate 格式化为 ISO 8601?【英文标题】:How to format LocalDate to ISO 8601 with T and Z? 【发布时间】:2021-04-12 06:07:34 【问题描述】:

我正在尝试生成一个随机日期和时间,并将其转换为"yyyy-MM-dd'T'HH:mm:ss'Z'" 格式。

这是我尝试过的:

  public static String generateRandomDateAndTimeInString() 
    LocalDate date = LocalDate.now().minus(Period.ofDays((new Random().nextInt(365 * 70))));
    System.out.println("date and time :: " + date.toString());
    return formatDate(date) ;
  

  public static String formatDate(LocalDate date)
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    return dateFormat.format(date);
  

但在dateFormat.format(date) 行中,它抱怨:

java.lang.IllegalArgumentException:无法将给定的对象格式化为日期

第二个问题是,print的输出不包含时间:

date :: 1998-12-24 

我不知道如何让它工作。

【问题讨论】:

这能回答你的问题吗? Changing one date to another date format using LocalDate LocalDate 只有一天、一个月和一年。如果您也想要一天中的时间,请考虑LocalDateTime,如果您想要偏移量或时区,请查看OffsetDateTimeZonedDateTime。它们都可以通过合适的DateTImeFormatter 从/到String 进行解析和格式化。不再需要SimpleDateFormat... 【参考方案1】:

切勿使用SimpleDateFormat 格式化java.time 类型

使用SimpleDateFormat,您应该只格式化旧的日期时间类型,例如java.util.Date。为了格式化java.time 日期时间类型,您需要使用DateTimeFormatter

切勿将Z 括在单引号内

在格式中将Z 括在单引号内是一个错误。符号Z 代表zulu 并指定UTC+00:00。如果将其括在单引号中,则它仅表示字符文字 Z 并且在解析时不会用作 UTC+00:00

您不需要明确使用格式化程序

对于这个要求,您不需要显式使用格式化程序,因为OffsetDateTime#toString 已经以您需要的格式返回字符串。但是,如果OffsetDateTime 对象中的 数为零,则相同的和随后的较小单位将被OffsetDateTime#toString 截断。如果您需要完整格式而不考虑 seconds 的值,那么当然,您必须使用 DateTimeFormatter

import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Random;

public class Main 
    public static void main(String[] args) 
        System.out.println(generateRandomDateAndTimeInString());
    

    public static String generateRandomDateAndTimeInString() 
        LocalDate date = LocalDate.now().minus(Period.ofDays((new Random().nextInt(365 * 70))));
        System.out.println("date and time :: " + date.toString());
        return formatDate(date);
    

    public static String formatDate(LocalDate date) 
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX");
        // return date.atStartOfDay().atOffset(ZoneOffset.UTC).toString();
        return date.atStartOfDay().atOffset(ZoneOffset.UTC).format(dtf);
    

示例运行:

date and time :: 1996-09-05
1996-09-05T00:00:00Z

请注意,java.util 的日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用,转用modern date-time API。

无论出于何种原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7。 如果您正在为一个 android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。

Trail: Date Time 了解有关现代日期时间 API 的更多信息。

如果您出于任何原因仍需要使用SimpleDateFormat

使用ZoneOffset.UTCLocalDate 转换为ZonedDateTime 并在一天开始时➡️ 将ZonedDateTime 转换为Instant ➡️ 从Instant 获取java.util.Date 对象。

public static String formatDate(LocalDate date) 
    Date utilDate = Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant());
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
    return dateFormat.format(utilDate);

【讨论】:

感谢您的回复,能否请您使用较新的java.util.Calendar 写一个答案? java.util.Calendar 是在 java.util.Date 之上的另一个 hack。我建议您不要使用它,因为您已经在使用现代日期时间 API。不要将容易出错的 java.util 日期时间 API 与它混合。如果您坚持,我可以在几分钟内为您提供相同的服务。 @Jeff - 我在答案的末尾添加了一个部分,只是因为您已提出要求,但正如我反复提到的那样,请勿将 java.util 日期时间 API 用于任何生产代码。它们很容易出错,不应该与现代的java.time API 混合使用。如有任何疑问,请随时发表评论。 更新? java.util.CalendarDate 更新几年,但对我们没有任何帮助,因为它无法格式化。 Calendar 24 岁,近 7 年前(2014 年 3 月)被 java.time 取代。 我该怎么做,它也会产生随机时间?【参考方案2】:

如果你想忽略时间部分,那么你可以像这样使用ZonedDateTime

DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
return ZonedDateTime.of(
        date, 
        LocalTime.MIN, 
        ZoneId.of("Europe/Paris")
).format(dateFormat);

输出示例

2013-10-19T00:00:00+0200

或者更好,您可以只使用toString 将格式化日期作为默认格式为ZonedDateTime 的字符串:

return ZonedDateTime.of(
        date,
        LocalTime.MIN,
        ZoneId.of("Europe/Paris")
).toString();

输出

2013-10-19T00:00+02:00[Europe/Paris]

注意

这个日期总是用00:00:00作为时间部分,因为我们使用的是LocalTime.MIN

另外,您可以将ZoneId 更改为预期的Zone,这只是一个示例。

重要

DateFormatSimpleDateFormat 是遗留库,所以请不要将它们与java.time 库混合,在顶部您使用的是LocalDate,这意味着您正在使用这个java.time 库所以继续在你的所有代码中使用它。

【讨论】:

在格式中将Z 括在单引号内是一个错误。请更正。 谢谢@LiveandLetLive 我改了;)【参考方案3】:
    ZoneOffset utc = ZoneOffset.UTC;
    LocalDate today = LocalDate.now(utc);
    LocalDate seventyYearsAgo = today.minusYears(70);
    int totalDays = Math.toIntExact(ChronoUnit.DAYS.between(seventyYearsAgo, today));
    LocalDate date = today.minusDays(new Random().nextInt(totalDays));
    String dateString = date.atStartOfDay(utc).toString();
    System.out.println("date and time :: " + dateString);

示例输出:

日期和时间 :: 1983-08-24T00:00Z

注意事项:

让 java.time 从年转换为天。它提供了更易读、更正确的代码(一年并不总是 365 天)。 要在字符串中包含时间和 UTC 偏移量,请转换 ZonedDateTimeOffsetDateTime,因为此类对象包含时间和偏移量。 LocalDate 没有。这是一个没有时间且与 UTC 没有偏移的日期。您要求的 Z 表示与 UTC 的偏移量为 0。

如果您还想要输出中的小时、分钟和秒,您可以通过计算秒而不是天来获得它。在这种情况下,对整个操作使用 OffsetDateTime(如果在与 UTC 不同的时区,则使用 ZonedDateTime)。

    ZoneOffset utc = ZoneOffset.UTC;
    OffsetDateTime today = OffsetDateTime.now(utc).truncatedTo(ChronoUnit.SECONDS);
    OffsetDateTime seventyYearsAgo = today.minusYears(70);
    long totalSeconds = ChronoUnit.SECONDS.between(seventyYearsAgo, today);
    OffsetDateTime date = today.minusSeconds(ThreadLocalRandom.current().nextLong(0, totalSeconds));
    String dateString = date.toString();
    System.out.println("date and time :: " + dateString);

日期和时间 :: 1996-09-21T06:49:56Z

我正在使用ThreadLocalRandom,因为它可以在指定的时间间隔内生成随机长值。有趣的是ThreadLocalRandom 有很多Random 没有的便捷方法。

【讨论】:

以上是关于如何使用 T 和 Z 将 LocalDate 格式化为 ISO 8601?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用pyspark函数处理日期格式的T和Z

如何将LocalDate格式更改为LocalDate而不生成字符串[duplicate]

如何将 LocalDate 格式化为字符串?

localdate如何返回成字符串前端

如何将 LocalDate 对象格式化为 MM/dd/yyyy 并保持格式不变

我们如何将 com.datastax.driver.core.LocalDate 转换为 YYYY/MM/DD 格式?