是否有 Avro 的 "duration" 逻辑类型的 API 实现?

Is there an API implementation of Avro's "duration" logical type?

当前的 Apache Avro (1.8.2) 文档提到了“duration”逻辑类型:

A duration logical type annotates Avro fixed type of size 12, which stores three little-endian unsigned integers that represent durations at different granularities of time. The first stores a number in months, the second stores a number in days, and the third stores a number in milliseconds.

虽然这一切都说得通,但我在 .Net 或 Java 库中找不到实际的实现。 documentation for logical types 清楚地列出了除持续时间(日期、时间-毫秒、时间-微量、时间戳-毫秒和时间戳-微量)之外的所有逻辑类型。

"duration" 在我的 Avro 模式中相应地定义:

{
    "type": "record",
    "name": "DataBlock",
    "fields": [
    {
        "name": "duration",
        "type": {
            "type": "fixed",
            "name": "DataBlockDuration",
            "size": 12
        }
    }]
}

在 .Net 中(请原谅 VB),我必须手动序列化持续时间:

Dim ret(11) As Byte
Dim months = BitConverter.GetBytes(duration.Months)
Dim days = BitConverter.GetBytes(duration.Days)
Dim milliseconds = BitConverter.GetBytes(duration.Milliseconds)

Array.Copy(months, 0, ret, 0, 4)
Array.Copy(days, 0, ret, 4, 4)
Array.Copy(milliseconds, 0, ret, 8, 4)

在 Java 中反序列化时,我必须通过这样做转换为 org.joda.time.Period:

IntBuffer buf = ByteBuffer
                  .wrap(dataBlock.getDuration().bytes())
                  .order(ByteOrder.LITTLE_ENDIAN)
                  .asIntBuffer();

Period period = Period
                  .months(buf.get(0))
                  .withDays(buf.get(1))
                  .withMillis(buf.get(2));

是我遗漏了什么,还是 Avro 团队编写了规范但忘记实施了?似乎这种数据类型必须在没有 Avro API 的任何帮助的情况下实现。

乔达时间

Joda-Time project is now in maintenance mode, with the team advising migration to the java.time class 是的。概念相似,因为这两个项目都由同一个人 Stephen Colebourne 领导。

java.time

java.time 框架提供了两个独立的 classes 来表示独立于时间轴的时间跨度:

  • Period
    若干年、月、日。
  • Duration
    天数(与日历无关的通用 24 小时时间块)、小时、分钟、秒和小数秒(纳秒)。

您可以将前两个数字用作 Period, and the third number for a Duration

Period p = Period.ofMonths( months ).plusDays( days ) ;
Duration d = Duration.ofMillis( millis ) ;

您可能想要 normalize the years & months of the Period 对象。例如,“15 个月”的时间段将标准化为“1 年零 3 个月”。

Period p = Period.ofMonths( months ).plusDays( days ).normalized() ;

ISO 8601

java.time classes 在 parsing/generating 字符串时使用标准 ISO 8601 标准格式。

对于一段时间或持续时间,这意味着使用 PnYnMnDTnHnMnS 格式。 P 标记开始,T 将任何年-月-日与任何时-分-秒分开。例如,"P3Y6M4DT12H30M5S" 表示持续时间 "three years, six months, four days, twelve hours, thirty minutes, and five seconds".

要生成这样的字符串,只需在 PeriodDuration 上调用 toString。要解析,请调用 parse

Avro 中的奇怪概念

Avro 的持续时间(月 + 天 + 毫秒)概念对我来说似乎很奇怪。最大的问题是将年-月-日与时-分-秒混合使用几乎没有任何实际意义(想想看)。跟踪几个月而不是几年是令人惊讶的。

org.threeten.extra.PeriodDuration

如果您坚持要将年-月-日与时-分-秒合并,请考虑添加 ThreeTen-Extra library to your project. It offers a PeriodDuration class.

PeriodDuration pd = PeriodDuration.of( p , d ) ;  // Pass `Period` and `Duration` objects as covered above.

同样,您可能希望调用 normalizedStandardDays and normalizedYears


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

  • Java SE 8, Java SE 9, Java SE 10,及以后
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
  • Android
    • Android java.time classes.
    • 捆绑实施的更高版本
    • 对于较早的 Android (<26),ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See .

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

根据 Apache 问题跟踪器 AVRO-2123,已指定逻辑持续时间类型但尚未实现。

是的,Apache 团队已经编写了规范,但忘记在这个细节中实现它。

我还在 Avro 版本 1.8.2 中的解压缩 jar 文件中搜索了任何 joda 库的导入,只找到了 class org.apache.avro.data.TimeConversions,它为其他逻辑获得了一些转换"date"(映射到 org.joda.time.LocalDate)等类型,但不适用于 Joda-class Period.

看来您使用 Joda 的 Period-class 来规避问题的方法很好,因为:

  • Avro 仍然使用 Joda-Time(尽管后者处于维护模式),
  • Period-class 可以完全映射 Avro 规范的持续时间(以月、天和毫秒为单位)(并且使用 Avro 规范要求的无符号整数来获得始终为正的持续时间也是一个避免带有混合符号的奇怪时期的好事)。

我知道的 Joda-Time 的可能替代方案:

  • Threeten-Extra-class PeriodDuration(参见 Basil Bourque 的回答)
  • Time4J-class net.time4j.Duration(我的库)

Threeten-Extra-class 的功能比 Joda-class 少(根本没有本地化,降低了 ISO-8601 合规性等),但对于您来说可能仍然足够特殊的 Avro 相关场景,而 Time4J-class 比 Joda 提供更多的功能(在 ISO 合规性、格式化、解析、规范化等领域)。