当前位置: 首页 > 知识库问答 >
问题:

Java 8 Time API - ZonedDateTime -解析时指定默认的ZoneId

索寒
2023-03-14

我正在尝试编写一个泛型方法来返回一个< code>ZonedDateTime,给定日期为< code>String及其格式。

如果日期String中没有指定默认的ZoneId,我们如何使ZonedDateTime使用默认的ZoneId

可以使用java.util完成。日历,但我想使用Java8时间API。

这个问题使用的是固定时区。我将格式指定为参数。日期及其格式都是< code>String参数。更一般。

代码和输出如下:

public class DateUtil {
    /** Convert a given String to ZonedDateTime. Use default Zone in string does not have zone.  */
    public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {
        //use java.time from java 8
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
        ZonedDateTime zonedDateTime = ZonedDateTime.parse(date, formatter);
        return zonedDateTime;
    }

    public static void main(String args[]) {
        DateUtil dateUtil = new DateUtil();
        System.out.println(dateUtil.parseToZonedDateTime("2017-09-14 15:00:00+0530", "yyyy-MM-dd HH:mm:ssZ"));
        System.out.println(dateUtil.parseToZonedDateTime("2017-09-14 15:00:00", "yyyy-MM-dd HH:mm:ss"));
    }
}

输出

2017-09-14T15:00+05:30
Exception in thread "main" java.time.format.DateTimeParseException: Text '2017-09-14 15:00:00' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2017-09-14T15:00 of type java.time.format.Parsed
    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
    at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)
    at com.nam.sfmerchstorefhs.util.DateUtil.parseToZonedDateTime(DateUtil.java:81)
    at com.nam.sfmerchstorefhs.util.DateUtil.main(DateUtil.java:97)
Caused by: java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2017-09-14T15:00 of type java.time.format.Parsed
    at java.time.ZonedDateTime.from(ZonedDateTime.java:565)
    at java.time.format.Parsed.query(Parsed.java:226)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
    ... 3 more
Caused by: java.time.DateTimeException: Unable to obtain ZoneId from TemporalAccessor: {},ISO resolved to 2017-09-14T15:00 of type java.time.format.Parsed
    at java.time.ZoneId.from(ZoneId.java:466)
    at java.time.ZonedDateTime.from(ZonedDateTime.java:553)
    ... 5 more

共有3个答案

武向文
2023-03-14

根据Java 8 ZonedDateTime实现,如果ZonedDateTime中没有zone,就不能解析日期。

为了解决给定的问题,您必须设置try-catch,以防出现任何异常,它将考虑默认时区。

请查找修订后的程序如下:

public class DateUtil {
     /** Convert a given String to ZonedDateTime. Use default Zone in string does not have zone.  */
    public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {
        //use java.time from java 8
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
        ZonedDateTime zonedDateTime = null;
        try {
            zonedDateTime = ZonedDateTime.parse(date, formatter);
        } catch (DateTimeException e) {
            // If date doesn't contains Zone then parse with LocalDateTime 
            LocalDateTime localDateTime = LocalDateTime.parse(date, formatter);
            zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
        }
        return zonedDateTime;
    }

    public static void main(String args[]) {
        DateUtil dateUtil = new DateUtil();
        System.out.println(dateUtil.parseToZonedDateTime("2017-09-14 15:00:00+0530", "yyyy-MM-dd HH:mm:ssZ"));
        System.out.println(dateUtil.parseToZonedDateTime("2017-09-14 15:00:00", "yyyy-MM-dd HH:mm:ss"));
    }
}

您的案例参考http://www.codenuclear.com/java-8-date-time-intro有关即将推出的java特性的更多详细信息

童浩言
2023-03-14
匿名用户

java.time 库中几乎没有默认值,这主要是一件好事 - 你所看到的就是你得到的,句号。

我建议,如果您的日期字符串不包含Zone -它是一个< code>LocalDateTime,而不能是< code>ZonedDateTime,这就是您所得到的异常的含义(即使由于过于灵活的代码结构而出现了冗长)。

我的主要建议是,如果您知道该模式没有区域信息,则解析为本地日期时间。

但是,如果您确实必须这样做,以下是另一种执行所需操作的方法(不使用异常来控制流的替代解决方案):

TemporalAccessor parsed = f.parse(string);
if (parsed.query(TemporalQueries.zone()) == null) {
  parsed = f.withZone(ZoneId.systemDefault()).parse(string);
}
return ZonedDateTime.from(parsed);

这里我们使用中间解析结果来确定字符串是否包含区域信息,如果没有,我们再次解析(使用相同的字符串,但不同的打印机解析器),以便它这次将包含一个区域。

或者,您可以创建这个类,这将避免您第二次解析,并允许您解析分区日期时间,假设所有其他字段都存在:

class TemporalWithZone implements TemporalAccessor {
  private final ZoneId zone;
  private final TemporalAccessor delegate;
  public TemporalWithZone(TemporalAccessor delegate, ZoneId zone) {
    this.delegate = requireNonNull(delegate);
    this.zone = requireNonNull(zone);
  }

  <delegate methods: isSupported(TemporalField), range(TemporalField), getLong(TemporalField)>

  public <R> R query(TemporalQuery<R> query) {
    if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId()) {
      return (R) zone;
    }
    return delegate.query(query);
  }
}

焦宏硕
2023-03-14

ZonedDateTime需要构建时区或偏移量,而第二个输入没有它。(它只包含日期和时间)。

因此,您需要检查是否可以构建ZonedDateTime,如果不可能,则必须为其选择任意区域(因为输入没有指示正在使用的时区,因此您必须选择一个要使用的时区)。

一种替代方法是首先尝试创建一个< code>ZonedDateTime,如果不可能,则创建一个< code>LocalDateTime并将其转换为时区:

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {
    // use java.time from java 8
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
    ZonedDateTime zonedDateTime = null;
    try {
        zonedDateTime = ZonedDateTime.parse(date, formatter);
    } catch (DateTimeException e) {
        // couldn't parse to a ZoneDateTime, try LocalDateTime
        LocalDateTime dt = LocalDateTime.parse(date, formatter);

        // convert to a timezone
        zonedDateTime = dt.atZone(ZoneId.systemDefault());
    }
    return zonedDateTime;
}

在上面的代码中,我使用的是<code>ZoneId。systemDefault(),它获取JVM默认时区,但即使在运行时,也可以在不通知的情况下进行更改,因此最好始终明确使用哪个时区。

API使用IANA时区名称(格式始终为地区/城市,如Sao_Paulo欧洲/柏林)。避免使用3个字母的缩写(如CSTPST),因为它们含糊不清且不标准。

您可以通过调用 ZoneId.getAvailableZoneIds() 来获取可用时区的列表(并选择最适合您系统的时区)。

如果要使用特定时区,只需使用<code>ZoneId。(“America/New_York”)(或ZoneId.getAvailableZoneIds()返回的任何其他有效名称,纽约只是一个示例)而不是ZoneId.systemDefault()

另一种方法是使用parseBest()方法,该方法尝试创建一个合适的日期对象(使用TemporalQuery的列表),直到创建您想要的类型:

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

    // try to create a ZonedDateTime, if it fails, try LocalDateTime
    TemporalAccessor parsed = formatter.parseBest(date, ZonedDateTime::from, LocalDateTime::from);

    // if it's a ZonedDateTime, return it
    if (parsed instanceof ZonedDateTime) {
        return (ZonedDateTime) parsed;
    }
    if (parsed instanceof LocalDateTime) {
        // convert LocalDateTime to JVM default timezone
        LocalDateTime dt = (LocalDateTime) parsed;
        return dt.atZone(ZoneId.systemDefault());
    }

    // if it can't be parsed, return null or throw exception?
    return null;
}

在本例中,我只使用了 ZonedDateTime::from 和 LocalDateTime::from,因此格式化程序将尝试首先创建一个 ZonedDateTime,如果不可能,则尝试创建 LocalDateTime

然后我检查返回的类型是什么并相应地执行操作。您可以添加任意数量的类型(所有主要类型,例如 LocalDate、LocalTimeOffsetDateTime 等,都有一个适用于 parseBestfrom 方法 - 如果需要,您也可以创建自己的自定义 TemporalQuery,但我认为内置方法足以满足这种情况)。

当使用<code>atZone()</code>方法将<code>LocalDateTime</code>转换为<code>ZonedDateTime>/code时,存在一些关于夏令时(DST)的棘手情况。

我将使用我居住的时区(美国/Sao_Paulo)作为示例,但这可能发生在具有 DST 的任何时区。

在圣保罗,夏令时 2016 年 10 月 16 日开始:午夜时分,时钟从午夜向前移动 1 小时到凌晨 1 点(偏移量从 -03:00 更改为 -02:00)。因此,00:00 到 00:59 之间的所有本地时间在此时区中都不存在(您也可以认为时钟从 23:59:59.9999999999 直接更改为 01:00)。如果我在此间隔内创建本地日期,则会将其调整为下一个有效时刻:

ZoneId zone = ZoneId.of("America/Sao_Paulo");

// October 16th 2016 at midnight, DST started in Sao Paulo
LocalDateTime d = LocalDateTime.of(2016, 10, 16, 0, 0, 0, 0);
ZonedDateTime z = d.atZone(zone);
System.out.println(z);// adjusted to 2017-10-15T01:00-02:00[America/Sao_Paulo]

当DST结束:2月19日th2017年午夜,时钟向后移动1小时,从午夜到18日th的23 PM(偏移量从-02:00更改为-03:00)。因此,从23:00到23:59的所有本地时间都存在两次(在两个偏移量中:-03:00-02:00),您必须决定您想要哪一个。默认情况下,它在DST结束前使用偏移量,但您可以使用在DST结束后获取偏移量:

// February 19th 2017 at midnight, DST ends in Sao Paulo
// local times from 23:00 to 23:59 at 18th exist twice
LocalDateTime d = LocalDateTime.of(2017, 2, 18, 23, 0, 0, 0);
// by default, it gets the offset before DST ends
ZonedDateTime beforeDST = d.atZone(zone);
System.out.println(beforeDST); // before DST end: 2018-02-17T23:00-02:00[America/Sao_Paulo]

// get the offset after DST ends
ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();
System.out.println(afterDST); // after DST end: 2018-02-17T23:00-03:00[America/Sao_Paulo]

请注意,DST结束前后的日期有不同的偏移量(-02:00-03:00)。如果您使用的时区有DST,请记住这些角落情况可能会发生。

 类似资料:
  • 问题内容: 我想解析Go中的JSON对象,但想为未指定的字段指定默认值。例如,我具有struct类型: A,B和C的默认值分别是“ a”,“ b”和“ c”。这意味着当我解析json时: 我想得到的结构: 使用内置包可以吗?否则,是否有任何具有此功能的Go库? 问题答案: 使用encoding / json可以实现:调用时,不需要给它一个空结构,可以给它一个默认值。 例如: 在Go操场上运行此示例

  • 问题内容: 有没有一种方法可以指定与struct一起使用的默认dtype ? 我特别想成为和成为。相反,我得到和 问题答案: 默认值取决于您的系统。在64位系统上,默认类型为64位。在32位系统上,默认类型为32位。无法使用其他系统C标头更改默认值,即重新编译numpy。 您当然可以明确指定dtype,例如 编辑:正如kazemakase在下面提到的,以上内容仅适用于int32 / int64。在

  • 问题内容: 我想有一个可选参数,如果仅存在未指定值的标志,则默认为一个值,但是存储用户指定的值,而不是如果用户指定一个值,则存储默认值。是否已经有可用于此的措施? 一个例子: 我可以创建一个动作,但想查看是否存在执行此操作的方法。 问题答案: 表示0或1参数 当参数为0时设置默认值 将参数转换为int 如果即使未指定,也要设置为1 ,则包括。也就是说, 然后

  • 本文向大家介绍JS函数多个参数默认值指定方法分析,包括了JS函数多个参数默认值指定方法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JS函数多个参数默认值指定方法。分享给大家供大家参考,具体如下: 函数有一个参数时,以往这样定义(参数为p1): 当需要为p1设定一个默认值时 当函数需要2个参数时,以前习惯这样写 后来发现完全不需要这样写,js函数甚至不需要在括弧内预设参数名,可以用一

  • 问题内容: 使用Java 1.8.0_51时,以下代码(摘自无法从TemporalAccessor获取OffsetDateTime) 引发异常: 这次我在做什么错? 问题答案: 您忘记设置时间了。 如果将我的答案与代码进行比较,您会注意到唯一的区别是时间信息丢失。一个包含时间信息,并从当前的格式不处理它,实例不能形成即可。 您还可以在包含以下内容的stacktrace中看到它 根据您的需要,您可以

  • 问题内容: 想知道不指定classpath选项时默认的classpath是什么吗? 问题答案: 当前的工作目录()。 从Java™教程中:PATH和CLASSPATH: 类路径 的 默认值为“。” ,表示仅搜索当前目录。指定CLASSPATH变量或-cp命令行开关将覆盖此值。 这包括子目录吗? 不,在类路径中没有条目是“递归的”。您必须明确列出每个子目录(或jar)。但是,如果您有一个表示clas