【问题标题】:Convert Long to DateTime from C# Date to Java Date从 C# 日期到 Java 日期,将 Long 转换为 DateTime
【发布时间】:2018-07-16 05:31:17
【问题描述】:

我一直在尝试用 Java 读取二进制文件,二进制文件是用 C# 编写的。其中一些数据包含 DateTime 数据。

当将 DateTime 数据写入文件(二进制)时,在 C# 上使用DateTime.ToBinary();

为了读取 DateTime 数据,它会首先使用 BitConverter.ToInt64(byte[], 0) 将字节转换为 long 数据,然后使用 DateTime.FromBinary(long) 将其再次从 long 转换为 DateTime 数据。 (所有这些都是用 C# 编写的)。

假设从字节转换后的长数据为= -8586803256090942249,将其转换为DateTime时,它将返回= 3/17/2018 5:07:56 PM

现在,我正在尝试使用 Java 读取该二进制文件。为了将字节数据转换为长数据,我使用了以下代码:ByteBuffer.wrap(byte[]).order(ByteOrder.LITTLE_ENDIAN).getLong()

它将像 C# 一样返回精确的长数据值。但是,当我尝试使用 Date date = new Date(long) 将其从长数据转换为 Java 中的 DateTime 时,它​​将返回 = Sun May 06 19:04:17 WIB 272097407

你能帮我解决这个问题的正确方法吗? Java 中的 C# 中的 DateTime.FromBinary() 是否有任何等价物?还是我的代码错了?非常感谢您的所有回答。

【问题讨论】:

  • 在 Java 库中似乎没有 DateTime.FromBinary() 的等价物。但是,使用 DateTimeOffset.ToUnixTimeMilliseconds() 并将其生成的值放入 Java 的 Date() 参数中听起来很有趣(因为 C# DateTime0001/01/01 开始,而 Java 的 Date 遵循 Unix 纪元格式,即从 1970/01/01 开始)。跨度>
  • 我可以做相反的事情吗?我的意思是可以用Java将长数据转换为从0001/01/01开始的日期时间吗?
  • 2018 年 3 月 17 日下午 5:07:56 也是印度尼西亚西部时间吗?
  • @imbaglia C#部分也可以从Unix时间格式转换为DateTime:DateTimeOffset.FromUnixTimeMilliseconds(millis).LocalDateTime。确保 Java 代码首先返回 Unix 纪元格式,然后在 C# 端使用该函数进行 DateTime 转换。
  • @OleV.V.是的 2018 年 3 月 17 日下午 5:07:56 也是印度尼西亚西部时间

标签: java c# datetime binary


【解决方案1】:

在 Java 中:

    long fromBytes = -8586803256090942249L;

    // Mask out kind and ticks
    int kind = Math.toIntExact((fromBytes >> 62) & 0x3);
    long ticks = fromBytes & 0x3FFF_FFFF_FFFF_FFFFL;
    LocalDateTime cSharpEpoch = LocalDate.of(1, Month.JANUARY, 1).atStartOfDay();
    // 100 nanosecond units or 10^-7 seconds
    final int unitsPerSecond = 10_000_000;
    long seconds = ticks / unitsPerSecond;
    long nanos = (ticks % unitsPerSecond) * 100;
    LocalDateTime ldt = cSharpEpoch.plusSeconds(seconds).plusNanos(nanos);

    switch (kind) {
    case 0: // Unspecified
    case 2: // Local time
        System.out.println("Result LocalDateTime: " + ldt);
        break;

    case 1: // UTC
        OffsetDateTime utcDateTime = ldt.atOffset(ZoneOffset.UTC);
        System.out.println("Result OffsetDateTime in UTC: " + utcDateTime);
        break;

    default:
        System.out.println("Not a valid DateTimeKind: " + kind);
        break;
    }

输出:

结果本地日期时间:2018-03-17T10:07:56.383355900

编辑:数字是

一个 64 位有符号整数,将 Kind 属性编码为 2 位 字段和 62 位字段中的 Ticks 属性。

Tetsuya Yamamoto 是正确的,因为 ticks 属性表示自 0001/01/01 开始(午夜)以来经过的 100 纳秒间隔数。 kind 是 0 表示未指定,1 表示 UTC 或 2 表示本地时间。所以我分别屏蔽了种类和勾号。

即使在您的情况下 kind 是 2,应该是当地时间,但似乎时间确实是 UTC。这是打印时间与您预期的印度尼西亚西部时间下午 5:07:56 一致的唯一方法。也许这个数字是在时区设置为 UTC 的计算机上生成的。

要获取您所在时区的时间:

    ZoneId targetZone = ZoneId.of("Asia/Jakarta");
    ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC).withZoneSameInstant(targetZone);
    System.out.println("Converted to target time zone: " + zdt);

转换为目标时区:2018-03-17T17:07:56.383355900+07:00[Asia/Jakarta]

这与您所说的 C# 方面的内容一致。

PS 如果可以,请避免使用 Java 中的 Date 类,它早已过时且设计不佳,并且在多年前已被 java.time, the modern Java date and time API 取代(我当然在上面使用了它)。如果您确实需要 Date 用于您无法更改或不想更改的旧版 API,正如您在评论中已经指出的那样,转换如下:

    Instant inst = ldt.atOffset(ZoneOffset.UTC).toInstant();
    Date date = Date.from(inst);
    System.out.println(date);

在默认时区 Asia/Jakarta 的 JVM 上输出:

2018 年 3 月 17 日星期六 17:07:56 WIB

致谢: Andreas 在回答(下面的链接)中解释了 64 位数字的结构并提供了文档链接。我已经把它们从那里拿走了。

链接

【讨论】:

  • 哦,很少使用那个包。即时结果是什么意思?这意味着这是 GMT+0 的日期时间?
  • 是的,输出第一行的时间 10:07:56 是 UTC。末尾的Z 仅表示(或偏移量为零)(输出格式为 ISO 8601)。
  • 哦,我终于可以将 Instant 数据转换为带有 Date date = Date.from(instantDate); 的日期。非常感谢@OleV.V.
  • @imbagila 是的,您可以将Instant 转换为java.util.Date,但只有在必要时才这样做,例如与尚未更新到 java.time。否则请避免使用DateCalendar 和其他旧的遗留类。它们完全被现代的 java.time 类所取代。
猜你喜欢
  • 2013-07-30
  • 2015-07-09
  • 2012-12-05
  • 2014-11-24
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多