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

How to format LocalDate to ISO 8601 with T and Z?

我正在尝试生成随机日期和时间,并将其转换为 "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: Cannot format given Object as a Date

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

date :: 1998-12-24 

我不知道如何让它工作。

如果你想忽略时间部分,那么你可以像这样使用 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 库,所以在你的所有代码中继续使用它。

切勿使用 SimpleDateFormat

格式化 java.time 类型

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

切勿将 Z 括在单引号内

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

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

对于此要求,您不需要显式使用格式化程序,因为 OffsetDateTime#toString 已经 returns 了您需要的格式的字符串。但是,如果 seconds 的数量在 OffsetDateTime 对象中为零,则相同和后续更小的单位是 t运行 由 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 and

Trail: Date Time.

了解有关现代日期时间 API 的更多信息

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

ZoneOffset.UTCLocalDate转换为ZonedDateTime并在一天开始时➡️将ZonedDateTime转换为Instant➡️获得java.util.Date来自 Instant.

的对象
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);
}
    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);

示例输出:

date and time :: 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);

date and time :: 1996-09-21T06:49:56Z

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