Сравнение даты и времени в Голанге
Есть ли варианты в сравнении дат в Голанге? Я должен сортировать данные по дате и времени - независимо. Таким образом, я мог бы разрешить объект, который встречается в диапазоне дат, при условии, что он также встречается в пределах диапазона времени. В этой модели я не мог просто выбрать самую старую дату, младшее время / последнюю дату, самое позднее время и секунды 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(диапазон, время), но она работает не совсем корректно. (На самом деле, в основном, только во втором случае, когда временной диапазон пересекает несколько дней, нарушен. Первоначальная часть работала, я просто понял, что должен учитывать это при выполнении преобразований в 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;
}
Однако я действительно хочу сделать этот фильтр на стороне сервера.
10 ответов
Используйте пакет времени для работы с информацией о времени в Go.
Воспроизвести пример:
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, ".")
}
}
Для сравнения двух раз используйте 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
Для случая, когда заканчивается ваш интервал, это дата без часов, например "с 2017-01-01 по целый день 2017-01-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) {
...
}
Можно сравнить дату, используя
int64
эпохи Unix с точностью до секунд. Если вам нужно более точное сравнение, например, миллисекунды или микросекунды и т. Д. Я думаю, что ответ @Oleg Neumyvakin идеален.
if expirationDate.Unix() > time.Now().Unix() {
...
}
Если вам интересно сравнить, близко ли время к другому для целей тестирования, вы можете использовать для этого testify . Например:
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
}
За время предложения : добавьте Time.Compare и соответствующий коммит ,time.Compare
будет добавлено в новом релизе (Go 1.20)
// Compare compares the time instant t with u. If t is before u, it returns -1;
// if t is after u, it returns +1; if they're the same, it returns 0.
func (t Time) Compare(u Time) int {
Образец
var t1, t2 Time
result := t1.Compare(t2)
Последние протоколы предпочитают использовать документацию RFC3339 на каждый пакет времени.
В общем случае 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).
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Hello World")
maxRep := 5
repPeroid := 6
expiry := maxRep * repPeroid
fmt.Println("Expiry: ", expiry)
fmt.Println(time.Now())
CorrIdtime := time.Now().Add(time.Second * time.Duration(expiry)).Format(time.RFC3339)
Notifytime := time.Now().Add(2 * time.Second * time.Duration(expiry)).Format(time.RFC3339)
fmt.Println(CorrIdtime)
fmt.Println(Notifytime)
if CorrIdtime < Notifytime {
fmt.Println("Discarded")
} else {
fmt.Println("Accepted")
}
}
Как объясняется в статье, мы могли бы использовать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)
}
}
Следующее решило мою проблему преобразования строки в дату
основной пакет
import (
"fmt"
"time"
)
func main() {
value := "Thu, 05/19/11, 10:47PM"
// Writing down the way the standard time would look like formatted our way
layout := "Mon, 01/02/06, 03:04PM"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
// => "Thu May 19 22:47:00 +0000 2011"