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

计算时间。从时间戳开始的时间,从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 在线手电筒追加按钮JS
  • ¥15 调用函数时,无关变量的改变引起函数值的改变
  • ¥15 xy坐标转化为经纬度坐标
  • ¥15 一般三角模糊数的上界值和下届值取中值的多少比较合理?
  • ¥15 关于#python#的问题,请各位专家解答!
  • ¥20 Hbase启动失败,无法启动HMaster
  • ¥20 Lumerical FDTD solutions 中模型的相对阻抗,有效介电常数和有效磁导率的实部和虚部的数据如何获得?
  • ¥100 sql reporting service 远程smtp服务器配置支持
  • ¥15 ppyoloe_r带角度目标检测,loss_cls没法收敛
  • ¥15 淘宝交易指数如何解读,其关联的数据指标是什么