【问题标题】:How to do date/time comparison如何进行日期/时间比较
【发布时间】:2014-01-22 08:12:33
【问题描述】:

在 Go 中进行日期比较有什么选择吗?我必须根据日期和时间对数据进行排序 - 独立。所以我可能允许一个对象出现在一个日期范围内,只要它也出现在一个时间范围内。在这个模型中,我不能简单地选择最早的日期、最年轻的时间/最晚日期、最晚时间和 Unix() 秒来比较它们。非常感谢任何建议。

最后,我写了一个时间解析字符串比较模块来检查时间是否在一个范围内。然而,这并不顺利。我有一些悬而未决的问题。我会在这里发布只是为了好玩,但我希望有更好的时间比较方法。

package main

import (
    "strconv"
    "strings"
)

func tryIndex(arr []string, index int, def string) string {
    if index <= len(arr)-1 {
        return arr[index]
    }
    return def
}

/*
 * Takes two strings of format "hh:mm:ss" and compares them.
 * Takes a function to compare individual sections (split by ":").
 * Note: strings can actually be formatted like "h", "hh", "hh:m",
 * "hh:mm", etc. Any missing parts will be added lazily.
 */
func timeCompare(a, b string, compare func(int, int) (bool, bool)) bool {
    aArr := strings.Split(a, ":")
    bArr := strings.Split(b, ":")
    // Catches margins.
    if (b == a) {
        return true
    }
    for i := range aArr {
        aI, _ := strconv.Atoi(tryIndex(aArr, i, "00"))
        bI, _ := strconv.Atoi(tryIndex(bArr, i, "00"))
        res, flag := compare(aI, bI)
        if res {
            return true
        } else if flag { // Needed to catch case where a > b and a is the lower limit
            return false
        }
    }
    return false
}

func timeGreaterEqual(a, b int) (bool, bool) {return a > b, a < b}
func timeLesserEqual(a, b int) (bool, bool) {return a < b, a > b}

/*
 * Returns true for two strings formmated "hh:mm:ss".
 * Note: strings can actually be formatted like "h", "hh", "hh:m",
 * "hh:mm", etc. Any missing parts will be added lazily.
 */
func withinTime(timeRange, time string) bool {
    rArr := strings.Split(timeRange, "-")
    if timeCompare(rArr[0], rArr[1], timeLesserEqual) {
        afterStart := timeCompare(rArr[0], time, timeLesserEqual)
        beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
        return afterStart && beforeEnd
    }
    // Catch things like `timeRange := "22:00:00-04:59:59"` which will happen
    // with UTC conversions from local time.
    // THIS IS THE BROKEN PART I BELIEVE
    afterStart := timeCompare(rArr[0], time, timeLesserEqual)
    beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
    return afterStart || beforeEnd
}

所以 TLDR,我写了一个 withinTimeRange(range, time) 函数,但它不能完全正常工作。 (事实上​​,大多数情况下,时间范围跨越几天的情况被破坏了。原始部分有效,我只是意识到在从本地转换为 UTC 时需要考虑这一点。)

如果有更好的(最好是内置的)方法,我很想听听!

注意: 举个例子,我用这个函数在 Javascript 中解决了这个问题:

function withinTime(start, end, time) {
    var s = Date.parse("01/01/2011 "+start);
    var e = Date.parse("01/0"+(end=="24:00:00"?"2":"1")+"/2011 "+(end=="24:00:00"?"00:00:00":end));
    var t = Date.parse("01/01/2011 "+time);
    return s <= t && e >= t;
}

但是我真的很想在服务器端做这个过滤器。

【问题讨论】:

    标签: datetime go datetime-comparison


    【解决方案1】:

    可以使用 Unix 纪元的 int64 以秒为单位比较日期。如果您需要更精确的比较,例如毫秒或微秒等。我猜 @Oleg Neumyvakin 的回答很完美。

    if expirationDate.Unix() > time.Now().Unix() {
    ...
    }
    

    【讨论】:

      【解决方案2】:

      正如the theread 中所述,我们可以使用github.com/google/go-cmp/cmp 包在测试中进行日期比较。

      func TestDates(t *testing.T) {
          date, _ := time.Parse(time.RFC3339, "2021-11-05T12:00:00+02:00")
          dateEqual, _ := time.Parse(time.RFC3339, "2021-11-05T11:00:00+01:00")
          dateNotEqual, _ := time.Parse(time.RFC3339, "2021-11-05T12:00:01+02:00")
      
          assertDates(t, date, dateEqual)    //pass
          assertDates(t, date, dateNotEqual) //fail
      }
      
      func assertDates(t *testing.T, expected, actual time.Time) {
          t.Helper()
      
          if diff := cmp.Diff(expected, actual); diff != "" {
              t.Errorf("mismatch (-expected +actual):\n%s", diff)
          }
      }
      

      【讨论】:

        【解决方案3】:

        如果您有兴趣比较某个时间是否接近另一个时间以进行测试,您可以为此使用testify assert.WithinDuration。例如:

        expectedTime := time.Now()
        actualTime := expectedTime.Add(100*time.Millisecond)
        assert.WithinDuration(t, expectedTime, actualTime, 1*time.Second) // pass
        assert.WithinDuration(t, expectedTime, actualTime, 1*time.Millisecond) // fail
        

        否则assert.WithinDuration 的实现可以在您的代码中重复使用,以确定两个时间的接近程度(从另一个日期中减去一个日期得出时间差):

        func WithinDuration(expected, actual time.Time, delta time.Duration) bool {
           dt := expected.Sub(actual)
           return dt >= -delta && dt <= delta
        }
        

        【讨论】:

          【解决方案4】:

          使用 time 包在 Go 中处理时间信息。

          可以使用 Before、After 和 Equal 来比较时间瞬间 方法。 Sub 方法减去两个瞬间,产生一个持续时间。 Add 方法添加一个时间和一个持续时间,产生一个时间。

          Play 示例:

          package main
          
          import (
              "fmt"
              "time"
          )
          
          func inTimeSpan(start, end, check time.Time) bool {
              return check.After(start) && check.Before(end)
          }
          
          func main() {
              start, _ := time.Parse(time.RFC822, "01 Jan 15 10:00 UTC")
              end, _ := time.Parse(time.RFC822, "01 Jan 16 10:00 UTC")
          
              in, _ := time.Parse(time.RFC822, "01 Jan 15 20:00 UTC")
              out, _ := time.Parse(time.RFC822, "01 Jan 17 10:00 UTC")
          
              if inTimeSpan(start, end, in) {
                  fmt.Println(in, "is between", start, "and", end, ".")
              }
          
              if !inTimeSpan(start, end, out) {
                  fmt.Println(out, "is not between", start, "and", end, ".")
              }
          }
          

          【讨论】:

          【解决方案5】:

          最近的协议更喜欢使用 RFC3339 per golang time package documentation

          一般来说,对于坚持这种格式的服务器,应该使用 RFC1123Z 而不是 RFC1123,对于新协议应该首选 RFC3339。 RFC822、RFC822Z、RFC1123 和 RFC1123Z 对格式化很有用;当与 time.Parse 一起使用时,它们不接受 RFC 允许的所有时间格式。

          cutOffTime, _ := time.Parse(time.RFC3339, "2017-08-30T13:35:00Z")
          // POSTDATE is a date time field in DB (datastore)
          query := datastore.NewQuery("db").Filter("POSTDATE >=", cutOffTime).
          

          【讨论】:

            【解决方案6】:

            如果您的间隔结束,它是没有小时的日期 “从 2017 年 1 月 1 日到 2017 年 1 月 16 日的全天”最好将间隔调整为 23 小时 59 分 59 秒,例如:

            end = end.Add(time.Duration(23*time.Hour) + time.Duration(59*time.Minute) + time.Duration(59*time.Second)) 
            
            if now.After(start) && now.Before(end) {
                ...
            }
            

            【讨论】:

            • 正是我需要将存储的时间戳与当前时间进行比较。
            【解决方案7】:

            两次比较使用time.Sub()

            // utc life
            loc, _ := time.LoadLocation("UTC")
            
            // setup a start and end time
            createdAt := time.Now().In(loc).Add(1 * time.Hour)
            expiresAt := time.Now().In(loc).Add(4 * time.Hour)
            
            // get the diff
            diff := expiresAt.Sub(createdAt)
            fmt.Printf("Lifespan is %+v", diff)
            

            程序输出:

            Lifespan is 3h0m0s
            

            http://play.golang.org/p/bbxeTtd4L6

            【讨论】:

            • 这是最好的答案。
            猜你喜欢
            • 2014-05-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-05-08
            • 1970-01-01
            • 2014-02-12
            • 2011-11-14
            • 1970-01-01
            相关资源
            最近更新 更多