普通网友 2019-09-12 06:58
浏览 785
已采纳

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

I'm trying to parse .ndx file. Which contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). Here's implementation in python: https://github.com/mekh/jtv2xmltv/blob/master/jtv2xml.py#L31

package main

import (
    "fmt"
    "time"
)

func main() {
    var timestamp int64
    timestamp = 132118740000000000

    delta := time.Duration(timestamp)*time.Microsecond
    fmt.Println(delta)

    seconds, _ := divmod(timestamp, 1000000)
    hours, seconds := divmod(seconds, 3600)
    delta = time.Duration(hours)*time.Hour
    fmt.Println(delta)

    layout := "2006-01-02"
    start, _ := time.Parse(layout, "1601-01-01")

    fmt.Println(start.Add(delta))
}

func divmod(numerator, denominator int64) (quotient, remainder int64) {
    quotient = numerator / denominator // integer division, decimals are truncated
    remainder = numerator % denominator
    return
}

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

Looks like delta variable overflows, even when setting by hours. Is there any way to calculate this?

EDIT: Found in docs https://golang.org/pkg/time/#Duration

A Duration represents the elapsed time between two instants as an int64 nanosecond count. The representation limits the largest representable duration to approximately 290 years.

Is there any 3rd party packages for duration longer than 290 years?

EDIT2: in the end I need time.Time of a given timestamp

  • 写回答

2条回答 默认 最新

  • dreamfly0514 2019-09-12 09:31
    关注

    time.Duration is an int64 value representing a duration in nanoseconds. As stated, max value of int64 is about 290 years, so bigger durations cannot be represented by it.

    Simplest, naive solution

    One simple solution is to convert your input to time.Duration, which will represent one hundredth of your actual duration, because the input is in 100-nanosecond units. You may add this duration to a time starting with the reference date: 1601-01-01 UTC a hundred times, and you're done:

    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
    }
    

    Testing it:

    fmt.Println(getTime(132118740000000000))
    

    Output (try it on the Go Playground):

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

    Naive solution optimized

    Yes, the above solution has a loop with 100 iterations, which may not be optimal.

    One way to speed up the above is to reduce the number of iterations. We may do so if the input is not "very" large. For example if the input multiplied by 2 also fits into int64, we could pre-multiply it by 2, and then we would only need 50 iterations. Similarly, if the input*10 would also fit into int64, we could pre-multiply it by 10 and then we would only need 10 iterations.

    The input is 100-nanosecond units. 100 is dividable by 100, 50, 25, 20, 10, 5, 4, 2, so to not lose any nanoseconds, we could check these factors if the input multiplied by these still fits into int64, and if so we can divide the iterations count by it. In the best case scenario (if duration is less than 2.9 years, we can reduce the iterations to 1).

    Example doing so:

    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
    }
    

    This will output the same, try it on the Go Playground. In this example the number of iterations is only 2.

    Fewest iterations

    Similar to the above solution, but here in each iteration we will increment the time with the biggest duration possible. That is: time.Duration(math.MaxInt64), but since the input is in 100-nanosecond units, to be exact, we'll use time.Duration(math.MaxInt64).Truncate(100 * time.Nanosecond). We keep doing this until the remaining duration is less than the maximum, which will be the final addition to get the time instant we're looking for. As an extra, we also don't need the first loop which looked for the biggest divisor (to which the iterations count could be reduced).

    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
    }
    

    Output again is the same. Try this one on the Go Playground.

    This solution guarantees fewest iterations. E.g. if duration is less than 290 years, there will be a single time.Add() call. If duration is between 290 and 580 years, there will be 2 time.Add() calls etc.

    Note that in the final time.Add() call we multiply input by 100 to convert 100-nanosecond units to nanoseconds. This will always succeed, because the loop before that decrements it as long as its bigger than maxdUnits. We also only call this final time.Add() if there is still something left to add, to ensure the fewest iterations. In practice this will likely always be true, so this if could be left out: even if input is 0, adding zero will not change t, I added it to be true to the "fewest iterations" title.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 急matlab编程仿真二阶震荡系统
  • ¥20 TEC-9的数据通路实验
  • ¥15 ue5 .3之前好好的现在只要是激活关卡就会崩溃
  • ¥50 MATLAB实现圆柱体容器内球形颗粒堆积
  • ¥15 python如何将动态的多个子列表,拼接后进行集合的交集
  • ¥20 vitis-ai量化基于pytorch框架下的yolov5模型
  • ¥15 如何实现H5在QQ平台上的二次分享卡片效果?
  • ¥15 python爬取bilibili校园招聘网站
  • ¥30 求解达问题(有红包)
  • ¥15 请解包一个pak文件