计算时间。从时间戳开始的时间,从Go中的1601-01-01开始

时间:2019-09-12 06:58:28

标签: datetime go time

我正在尝试解析.ndx文件。其中包含一个64位值,代表自1601年1月1日(UTC)起100纳秒间隔的数量。 这是python中的实现:https://github.com/mekh/jtv2xmltv/blob/master/jtv2xml.py#L31

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='foo'>
    <a><b>a</b></a>
    <a><b>b</b></a>
    <a><b>c</b></a>
</div>

https://play.golang.org/p/--zQUtJN5Lh

看起来像增量变量溢出,即使按小时设置也是如此。有什么方法可以计算出来吗?

编辑:在文档https://golang.org/pkg/time/#Duration

中找到
  

Duration表示两个瞬间之间经过的时间,以int64纳秒计数。该表示将最大可表示持续时间限制为大约290年。

是否有第三方套餐的有效期超过290年?

EDIT2 :最后我需要时间。给定时间戳记的时间

3 个答案:

答案 0 :(得分:4)

time.Duration是一个int64值,表示以纳秒为单位的持续时间。如前所述,int64的最大值约为290年,因此无法表示更长的持续时间。

最简单的天真解决方案

一种简单的解决方案是将输入转换为time.Duration,这将代表实际持续时间的百分之一,因为输入以100纳秒为单位。您可以将此持续时间添加到从参考日期开始的时间:1601-01-01 UTC一百次,您已完成:

func getTime(input int64) time.Time {
    t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
    d := time.Duration(input)
    for i := 0; i < 100; i++ {
        t = t.Add(d)
    }
    return t
}

测试:

fmt.Println(getTime(132118740000000000))

输出(在Go Playground上尝试):

2019-09-02 05:00:00 +0000 UTC

天真的解决方案已优化

是的,上面的解决方案有一个包含100次迭代的循环,这可能不是最佳选择。

加快上述速度的一种方法是减少迭代次数。如果输入不是很大,我们可能会这样做。例如,如果输入乘以2也适合int64,则可以将其乘以2,然后只需要进行50次迭代。同样,如果input*10也适合int64,我们可以将其乘以10,然后只需要10次迭代即可。

输入为100纳秒单位。 100可以被100、50、25、20、10、5、4、2整除,因此为了不损失任何纳秒,如果输入乘以这些乘积仍然适合int64,我们可以检查这些因素,如果可以的话,我们可以将迭代次数除以它。在最佳情况下(如果持续时间少于2.9年,我们可以将迭代次数减少为1)。

示例如下:

var divisors = []int64{100, 50, 25, 20, 10, 5, 4, 2}

func getTime(input int64) time.Time {
    iterations := 100
    for _, div := range divisors {
        if input <= math.MaxInt64/div {
            input *= div
            iterations /= int(div)
            break
        }
    }

    t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
    d := time.Duration(input)
    for i := 0; i < iterations; i++ {
        t = t.Add(d)
    }
    return t
}

这将输出相同的内容,请在Go Playground上进行尝试。在此示例中,迭代次数仅为2。

最小迭代

类似于上述解决方案,但是在这里,在每次迭代中,我们将以最大可能的持续时间增加时间。即:time.Duration(math.MaxInt64),但是由于输入是以100纳秒为单位的,因此,我们将使用time.Duration(math.MaxInt64).Truncate(100 * time.Nanosecond)。我们会一直这样做,直到剩余持续时间小于最大持续时间为止,这将是获得我们正在寻找的即时时间的最后加法。另外,我们也不需要第一个循环来寻找最大的除数(可以将迭代次数减少到最大)。

func getTime(input int64) time.Time {
    maxd := time.Duration(math.MaxInt64).Truncate(100 * time.Nanosecond)
    maxdUnits := int64(maxd / 100) // number of 100-ns units

    t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
    for input > maxdUnits {
        t = t.Add(maxd)
        input -= maxdUnits
    }
    if input != 0 {
        t = t.Add(time.Duration(input * 100))
    }
    return t
}

输出再次相同。在Go Playground上尝试这个。

此解决方案保证了最少的迭代。例如。如果持续时间少于290年,则将有一个time.Add()通话。如果持续时间在290至580年之间,则会有2次time.Add()通话,等等。

请注意,在最后的time.Add()调用中,我们将input乘以100将100纳秒单位转换为纳秒。这将始终成功,因为在此之前的循环将其循环的时间减小到只要它大于maxdUnits。如果还需要添加一些内容,我们也仅将其称为最终time.Add(),以确保迭代次数最少。实际上,这可能总是正确的,因此可以if忽略掉:即使input为0,加零也不会改变t,我将其添加为true。 “最小迭代”标题。

答案 1 :(得分:0)

如果您不需要以前的日期,例如1900,为什么不将日期更改为1900?

因此,您从ndx中获取int64,将其差异从1601移至1900,将其除以100,然后根据新纪元进行计算。没有循环,并且应该非常快速和准确

答案 2 :(得分:-1)

使用syscall找到了另一个解决方案。Filetime:https://parsiya.net/blog/2018-11-01-windows-filetime-timestamps-and-byte-wrangling-with-go/

// toTime converts an 8-byte Windows Filetime to time.Time.
func toTime(t [8]byte) time.Time {
    ft := &syscall.Filetime{
        LowDateTime:  binary.LittleEndian.Uint32(t[:4]),
        HighDateTime: binary.LittleEndian.Uint32(t[4:]),
    }
    return time.Unix(0, ft.Nanoseconds())
}