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

Datetime 计算时间。Go中从1601-01-01开始的时间戳的时间,datetime,go,time,Datetime,Go,Time,我正在尝试解析.ndx文件。它包含一个64位值,表示自1601年1月1日(UTC)以来的100纳秒间隔数。 下面是python的实现: 看起来增量变量溢出,即使按小时设置也是如此。有没有办法计算这个 编辑:在文档中找到 持续时间以int64纳秒计数表示两个瞬间之间经过的时间。该表述将可表述的最大期限限制为约290年 是否有超过290年的第三方软件包 EDIT2:最后我需要时间。给定时间戳的时间是一个int64值,表示以纳秒为单位的持续时间。如上所述,int64的最大值约为290年,因此不能用它

我正在尝试解析.ndx文件。它包含一个64位值,表示自1601年1月1日(UTC)以来的100纳秒间隔数。 下面是python的实现:

看起来增量变量溢出,即使按小时设置也是如此。有没有办法计算这个

编辑:在文档中找到

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

是否有超过290年的第三方软件包

EDIT2:最后我需要时间。给定时间戳的时间是一个
int64
值,表示以纳秒为单位的持续时间。如上所述,
int64
的最大值约为290年,因此不能用它来表示更长的持续时间

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

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
}
输出(在上尝试):

朴素解优化 是的,上面的解决方案有一个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
}
输出再次是相同的。试一试这个

此解决方案保证最少的迭代。例如,如果持续时间少于290年,将有一个
time.Add()
调用。如果持续时间介于290年和580年之间,将有2个
time.Add()
调用等


请注意,在最后的
time.Add()
调用中,我们将
input
乘以
100
将100纳秒单位转换为纳秒。这将始终成功,因为只要它大于
maxdUnits
,前面的循环就会递减它。如果还有什么东西需要添加,我们也只调用这个final
time.Add()
,以确保最少的迭代。在实践中,这可能总是正确的,因此可以忽略这一点:即使
输入
为0,添加零也不会改变
t
,我将其添加到“最少迭代次数”标题中。如果您不需要之前的日期,例如1900,为什么不将日期移到1900


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

使用syscall找到另一个解决方案。Filetime:


对第三方软件包的请求在这里是无关紧要的。但一个简单的解决方案可能是存储一个
time.Duration
,以及一个乘数。如果您需要3000年的持续时间,您可以存储30年的
time.duration
,例如10倍的乘数。在其他情况下,如果您只关心年数,那么一个简单的
int
就可以了。这两种方法是否适用于您取决于您对持续时间变量所做的操作。正如您所引用的,
time.duration
不能表示超过290年的持续时间。那你想要什么呢?给定时间戳的
1601-01-01
之后的小时、分钟、秒值;或者一个表示时间戳的
time.time
值,或者什么?请澄清您的问题。顺便说一句,您的示例时间戳看起来“短”,它表示1642年。如果将0添加到它:
132118740000000000
,则它将是
2019-09-02
fmt.Println(getTime(132118740000000000))
2019-09-02 05:00:00 +0000 UTC
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
}
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
}
// 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())
}