Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"bits.go",
"utils.go",
],
importpath = "go-common/app/admin/main/up/util",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/up/util/timerqueue:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/time:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/up/util/databusutil:all-srcs",
"//app/admin/main/up/util/hbaseutil:all-srcs",
"//app/admin/main/up/util/mathutil:all-srcs",
"//app/admin/main/up/util/now:all-srcs",
"//app/admin/main/up/util/timerqueue:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["utils_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//vendor/github.com/smartystreets/goconvey/convey:go_default_library"],
)

View File

@@ -0,0 +1,25 @@
package util
//IsBitSet bit is from 0 to 31
func IsBitSet(attr int, bit uint) bool {
return IsBitSet64(int64(attr), bit)
}
// IsBitSet64 bit is from 0 to 63
func IsBitSet64(attr int64, bit uint) bool {
if bit >= 64 {
return false
}
return (attr & (1 << bit)) != 0
}
//SetBit64 set bit to 1
func SetBit64(attr int64, bit uint) int64 {
return attr | (1 << bit)
}
//UnSetBit64 set bit to 0
func UnSetBit64(attr int64, bit uint) int64 {
return attr & ^(1 << bit)
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["databushandler.go"],
importpath = "go-common/app/admin/main/up/util/databusutil",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,95 @@
package databusutil
import (
"github.com/pkg/errors"
"sync"
"go-common/library/log"
"go-common/library/queue/databus"
)
//NewDatabusHandler new handler
func NewDatabusHandler() *DatabusHandler {
return &DatabusHandler{
closeCh: make(chan struct{}),
databusMap: make(map[*databus.Databus]int, 10),
}
}
//DatabusHandler handler
type DatabusHandler struct {
closeCh chan struct{}
wg sync.WaitGroup
databusMap map[*databus.Databus]int
lock sync.Mutex
}
//Close close
func (s *DatabusHandler) Close() {
// close all databus
s.lock.Lock()
for k, v := range s.databusMap {
log.Info("closing databus, %v, routine=%d", k, v)
k.Close()
}
s.lock.Unlock()
close(s.closeCh)
s.wg.Wait()
}
func (s *DatabusHandler) incWatch(bus *databus.Databus) {
s.lock.Lock()
s.databusMap[bus] = s.databusMap[bus] + 1
s.lock.Unlock()
}
func (s *DatabusHandler) decWatch(bus *databus.Databus) {
s.lock.Lock()
s.databusMap[bus] = s.databusMap[bus] - 1
s.lock.Unlock()
}
//Watch watch without goroutine
func (s *DatabusHandler) Watch(bus *databus.Databus, handler func(msg *databus.Message) error) {
defer func() {
s.wg.Done()
s.decWatch(bus)
if r := recover(); r != nil {
r = errors.WithStack(r.(error))
log.Error("Runtime error caught, try recover: %+v", r)
s.wg.Add(1)
go s.Watch(bus, handler)
}
}()
var msgs = bus.Messages()
s.incWatch(bus)
log.Info("start watch databus, %+v", bus)
for {
var (
msg *databus.Message
ok bool
err error
)
select {
case msg, ok = <-msgs:
if !ok {
log.Error("s.archiveNotifyT.messages closed")
return
}
case <-s.closeCh:
log.Info("server close")
return
}
msg.Commit()
if err = handler(msg); err != nil {
log.Error("handle databus error topic(%s) key(%s) value(%s) err(%v)", msg.Topic, msg.Key, msg.Value, err)
continue
}
log.Info("handle databus topic(%s) key(%s) value(%s) finish", msg.Topic, msg.Key, msg.Value)
}
}
//GoWatch watch with goroutine
func (s *DatabusHandler) GoWatch(bus *databus.Databus, handler func(msg *databus.Message) error) {
go s.Watch(bus, handler)
}

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["parser_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//vendor/github.com/tsuna/gohbase/hrpc:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["parser.go"],
importpath = "go-common/app/admin/main/up/util/hbaseutil",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/log:go_default_library",
"//vendor/github.com/tsuna/gohbase/hrpc:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,289 @@
package hbaseutil
import (
"encoding/binary"
"fmt"
"github.com/tsuna/gohbase/hrpc"
"go-common/library/log"
"reflect"
"strconv"
)
/*
add tag for struct fields:
you can add tag:
family: for hbase family, can be omitted, if omitted, the qualifier would be set at whatever famliy
qualifier: for hbase qualifier
if omitted, the fields must be map[string]int or map[string]string
see parser_test.go for detail
*/
type field struct {
parser *Parser
name string
structField reflect.StructField
fieldValue reflect.Value
family string
}
func (f *field) isValid() bool {
return f.fieldValue.IsValid()
}
func (f *field) setValue(c *hrpc.Cell) (err error) {
if c == nil {
return
}
if f.fieldValue.Kind() == reflect.Ptr {
f.fieldValue.Set(reflect.New(f.fieldValue.Type().Elem()))
f.fieldValue = f.fieldValue.Elem()
}
switch f.fieldValue.Kind() {
case reflect.Map:
err = f.setMap(c)
default:
err = setBasicValue(c.Value, f.fieldValue, f.name, f.parser.ParseIntFunc)
}
return
}
func setBasicValue(value []byte, rv reflect.Value, name string, parsefunc ParseIntFunc) (err error) {
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
rv.Set(reflect.New(rv.Type().Elem()))
}
rv = rv.Elem()
}
switch rv.Kind() {
case reflect.String:
rv.Set(reflect.ValueOf(string(value)))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var i, e = parsefunc(value, rv, name)
if e != nil {
err = fmt.Errorf("field=%s, fail to convert: %s", name, e)
return
}
if rv.OverflowInt(int64(i)) {
log.Warn("field overflow, field=%s, value=%d, field type=%s", name, i, rv.Type().Name())
}
rv.SetInt(int64(i))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
var i, e = parsefunc(value, rv, name)
if e != nil {
err = fmt.Errorf("field=%s, fail to convert: %s", name, e)
return
}
if rv.OverflowUint(i) {
log.Warn("field overflow, field=%s, value=%d, field type=%s", name, i, rv.Type().Name())
}
rv.SetUint(i)
default:
err = fmt.Errorf("cannot convert type:%s, kind()=%v, field=%s", rv.Type().Name(), rv.Kind(), name)
}
return
}
func (f *field) setMap(c *hrpc.Cell) (err error) {
var fieldType = f.fieldValue.Type()
if f.fieldValue.IsNil() {
f.fieldValue.Set(reflect.MakeMap(fieldType))
fieldType = f.fieldValue.Type()
}
var keyType = fieldType.Key()
if keyType.Kind() != reflect.String {
err = fmt.Errorf("cannot convert to map, only support map key: (string), but get type=%s", keyType.Name())
return
}
var val = reflect.Indirect(reflect.New(fieldType.Elem()))
err = setBasicValue(c.Value, val, f.name, f.parser.ParseIntFunc)
if err != nil {
err = fmt.Errorf("cannot convert to map, only support map value: (integer, string), type=%s, err=%v", fieldType.Name(), err)
return
}
var key = indirect(reflect.New(fieldType.Key()))
key.SetString(string(c.Qualifier))
f.fieldValue.SetMapIndex(key, val)
return
}
// ParseIntFunc function to parse []byte to uint64
// if not set, will assume []byte is big endian form of integer, length of 1/2/4/8 bytes
type ParseIntFunc func(v []byte, rv reflect.Value, fieldname string) (result uint64, err error)
//Parser parser for hbase cell
type Parser struct {
ParseIntFunc ParseIntFunc
}
func getOrCreateFieldMapByFamily(familyMap map[string]map[string]field, key string) (result map[string]field) {
var ok bool
if result, ok = familyMap[key]; !ok {
result = make(map[string]field)
familyMap[key] = result
}
return result
}
func getField(familyMap map[string]map[string]field, family string, qualifier string) (result field) {
var ok bool
var qualifierMap map[string]field
if qualifierMap, ok = familyMap[family]; !ok {
qualifierMap, ok = familyMap[""]
if !ok {
return
}
}
if result, ok = qualifierMap[qualifier]; !ok {
qualifierMap, ok = familyMap[""]
if ok {
result, ok = qualifierMap[qualifier]
}
if !ok {
return
}
}
return
}
//Parse parse cell to struct
// supported type:
// integer from 16 ~ 64 bit, the cell's value must be big endian form of the integer, length could be 2 or 4 or 8 bytes
// string
func (p *Parser) Parse(cell []*hrpc.Cell, ptr interface{}) (err error) {
if len(cell) == 0 {
log.Warn("cell length = 0, nothing to parse")
return
}
var familyFieldMap = make(map[string]map[string]field)
// field only have family, and type is map[string]{integer,string}
var familyOnlyMap = make(map[string]field)
//var noFamilyFieldMap = make(map[string]reflect.Value)
var ptrType = reflect.TypeOf(ptr)
// if it's ptr
if ptrType.Kind() == reflect.Ptr {
var value = reflect.ValueOf(ptr)
value = indirect(value)
var valueType = value.Type()
var valueKind = valueType.Kind()
if valueKind == reflect.Struct {
for i := 0; i < value.NumField(); i++ {
fieldInfo := valueType.Field(i) // a reflect.StructField
tag := fieldInfo.Tag // a reflect.StructTag
//fmt.Printf("tag for field: %s, tag: %s\n", fieldInfo.Name, tag)
family := tag.Get("family")
qualifier := tag.Get("qualifier")
var field = field{
family: family,
name: fieldInfo.Name,
structField: fieldInfo,
fieldValue: value.Field(i),
parser: p,
}
// if no qualifier, or star, we create only family field
if qualifier == "" || qualifier == "*" {
if fieldInfo.Type.Kind() != reflect.Map {
log.Warn("%s.%s, family-only field only support map, but get(%s)", ptrType.Name(), fieldInfo.Name, fieldInfo.Type.Name())
continue
}
familyOnlyMap[family] = field
} else {
// save field info
var fieldMapForFamily = getOrCreateFieldMapByFamily(familyFieldMap, family)
fieldMapForFamily[qualifier] = field
}
}
} else {
log.Warn("cannot decode, unsupport type(%s)", valueKind.String())
}
}
if p.ParseIntFunc == nil {
p.ParseIntFunc = ByteBigEndianToUint64
}
// parse
for _, c := range cell {
var family = string(c.Family)
var qualifier = string(c.Qualifier)
//log.Info("parse cell, family=%s, qualifier=%s", family, qualifier)
var fieldValue = getField(familyFieldMap, family, qualifier)
if !fieldValue.isValid() {
fieldValue = familyOnlyMap[family]
if !fieldValue.isValid() {
//log.Warn("no field for cell, family=%s, qualifier=%s", family, qualifier)
continue
}
}
if e := fieldValue.setValue(c); e != nil {
log.Warn("fail to set value, err=%v", e)
continue
}
}
return
}
// indirect returns the value pointed to by a pointer.
// Pointers are followed until the value is not a pointer.
// New values are allocated for each nil pointer.
//
// An exception to this rule is if the value satisfies an interface of
// interest to us (like encoding.TextUnmarshaler).
func indirect(v reflect.Value) reflect.Value {
if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
return v
}
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
} else if v.Kind() == reflect.Interface {
v = v.Elem()
}
return indirect(reflect.Indirect(v))
}
//StringToUint parse string to uint
func StringToUint(value []byte, rv reflect.Value, fieldname string) (result uint64, err error) {
if len(value) == 0 {
return
}
if value[0] == '-' {
i64, e := strconv.ParseInt(string(value), 10, 64)
err = e
result = uint64(i64)
} else {
result, err = strconv.ParseUint(string(value), 10, 64)
}
return
}
//ByteBigEndianToUint64 convert big endian to uint64
func ByteBigEndianToUint64(value []byte, rv reflect.Value, fieldname string) (result uint64, err error) {
var length = len(value)
switch length {
case 4:
result = uint64(binary.BigEndian.Uint32(value))
case 8:
result = uint64(binary.BigEndian.Uint64(value))
case 2:
result = uint64(binary.BigEndian.Uint16(value))
case 1:
result = uint64(value[0])
default:
err = fmt.Errorf("cannot decode to integer, byteslen=%d, only support (1,2,4,8)", length)
}
if err == nil {
var vlen = len(value)
var rvType = rv.Type()
if rvType.Size() != uintptr(vlen) {
log.Error("field=%s type=%s length=%d, cell length=%d, doesn't match, may yield wrong value!",
fieldname, rvType.Name(), rvType.Size(), vlen)
}
}
return
}

View File

@@ -0,0 +1,538 @@
package hbaseutil
import (
"encoding/binary"
"encoding/json"
"github.com/tsuna/gohbase/hrpc"
"reflect"
"testing"
"time"
)
func uint64ToByte(value uint64) []byte {
var buf = make([]byte, 8)
binary.BigEndian.PutUint64(buf, value)
return buf
}
func uint32ToByte(value uint32) []byte {
var buf = make([]byte, 4)
binary.BigEndian.PutUint32(buf, value)
return buf
}
func uint16ToByte(value uint16) []byte {
var buf = make([]byte, 2)
binary.BigEndian.PutUint16(buf, value)
return buf
}
type testStruct struct {
A int64 `family:"f" qualifier:"q64"`
B *int32 `family:"f" qualifier:"q32"`
C int16 `family:"f" qualifier:"q16"`
D int `qualifier:"q"`
S *string `qualifier:"s"`
FailField bool `family:"f" qualifier:"fail"`
MapInt map[string]int `family:"m1"`
MapString map[string]string `family:"m2"`
}
func (t *testStruct) equal(o testStruct) bool {
return t.A == o.A &&
*t.B == *o.B &&
t.C == o.C &&
t.D == o.D &&
*t.S == *o.S &&
t.FailField == o.FailField &&
reflect.DeepEqual(t.MapInt, o.MapInt) &&
reflect.DeepEqual(t.MapString, o.MapString)
}
var testcase = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: uint32ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: uint16ToByte(100)},
{Family: []byte("f"), Qualifier: []byte("q"), Value: uint64ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("s"), Value: []byte("just test")},
{Family: []byte("f"), Qualifier: []byte("fail"), Value: []byte("1")},
{Family: []byte("m1"), Qualifier: []byte("k1"), Value: uint32ToByte(1)},
{Family: []byte("m1"), Qualifier: []byte("k2"), Value: uint16ToByte(2)},
{Family: []byte("m2"), Qualifier: []byte("k1"), Value: []byte("1")},
{Family: []byte("m2"), Qualifier: []byte("k2"), Value: []byte("2")},
},
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("s"), Value: []byte("just test2")},
{Family: []byte("f"), Qualifier: []byte("fail"), Value: []byte("1")},
},
}
var resultb = []int32{
1000000,
10000000000 & 0xffffffff,
}
var results = []string{
"just test",
"just test2",
}
var resultcase = []testStruct{
{A: 10000000000, B: &resultb[0], C: 100, D: 1000000, S: &results[0],
MapInt: map[string]int{"k1": 1, "k2": 2},
MapString: map[string]string{"k1": "1", "k2": "2"}},
{A: 10000000000, B: &resultb[1], C: -7168, D: int(10000000000), S: &results[1]},
}
func TestParser_Parse(t *testing.T) {
var parser = Parser{}
for i, cells := range testcase {
var result testStruct
var err = parser.Parse(cells, &result)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v", result, resultcase[i])
if !resultcase[i].equal(result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
var testcase2 = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: []byte("10000000000")},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: []byte("1000000")},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: []byte("100")},
{Family: []byte("f"), Qualifier: []byte("q"), Value: []byte("1000000")},
{Family: []byte("f"), Qualifier: []byte("s"), Value: []byte("just test")},
{Family: []byte("f"), Qualifier: []byte("fail"), Value: []byte("1")},
{Family: []byte("m1"), Qualifier: []byte("k1"), Value: []byte("1")},
{Family: []byte("m1"), Qualifier: []byte("k2"), Value: []byte("2")},
{Family: []byte("m2"), Qualifier: []byte("k1"), Value: []byte("1")},
{Family: []byte("m2"), Qualifier: []byte("k2"), Value: []byte("2")},
},
}
var resultcase2 = []testStruct{
{A: 10000000000, B: &resultb[0], C: 100, D: 1000000, S: &results[0],
MapInt: map[string]int{"k1": 1, "k2": 2},
MapString: map[string]string{"k1": "1", "k2": "2"}},
}
func TestParser_ParseCustomParseInt(t *testing.T) {
var parser = Parser{
ParseIntFunc: StringToUint,
}
for i, cells := range testcase2 {
var result testStruct
var err = parser.Parse(cells, &result)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v", result, resultcase[i])
if !resultcase2[i].equal(result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
func TestParser_StringInt(t *testing.T) {
var testcase2 = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: []byte("9223372036854775807")},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: []byte("1000000")},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: []byte("10000")},
{Family: []byte("f"), Qualifier: []byte("q"), Value: []byte("100")},
},
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: []byte("-9223372036854775808")},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: []byte("-2")},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: []byte("-3")},
{Family: []byte("f"), Qualifier: []byte("q"), Value: []byte("-4")},
{Family: []byte("f"), Qualifier: []byte("u64"), Value: []byte("18446744073709551615")},
{Family: []byte("f"), Qualifier: []byte("u32"), Value: []byte("2147483648")},
{Family: []byte("f"), Qualifier: []byte("u16"), Value: []byte("32768")},
{Family: []byte("f"), Qualifier: []byte("u8"), Value: []byte("128")},
},
}
type testStruct struct {
A int64 `family:"f" qualifier:"q64"`
B int32 `family:"f" qualifier:"q32"`
C int16 `family:"f" qualifier:"q16"`
D int8 `qualifier:"q"`
U64 uint64 `family:"f" qualifier:"u64"`
U32 uint32 `family:"f" qualifier:"u32"`
U16 uint16 `family:"f" qualifier:"u16"`
U8 uint8 `family:"f" qualifier:"u8"`
}
var expect = []testStruct{
{
A: 9223372036854775807, B: 1000000, C: 10000, D: 100,
},
{
A: -9223372036854775808, B: -2, C: -3, D: -4,
U64: 18446744073709551615, U32: 2147483648, U16: 32768, U8: 128,
},
}
var parser = Parser{
ParseIntFunc: StringToUint,
}
for i, cells := range testcase2 {
var result testStruct
var err = parser.Parse(cells, &result)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%+v, expect=%+v", result, expect[i])
if !reflect.DeepEqual(expect[i], result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
func TestParser_InterfaceInterface(t *testing.T) {
var parser = Parser{}
for i, cells := range testcase {
var st testStruct
var result interface{} = &st
var err = parser.Parse(cells, result)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v", result, resultcase[i])
if !resultcase[i].equal(*result.(*testStruct)) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
type testOnlyQualifier struct {
A int64 `qualifier:"q64"`
B *int32 `qualifier:"q32"`
C int16 `qualifier:"q16"`
D int `qualifier:"q"`
S *string `qualifier:"s"`
FailField bool `qualifier:"fail"`
MapInt map[string]int `family:"m1"`
MapString map[string]string `family:"m2"`
}
var testcase3 = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: uint32ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: uint16ToByte(100)},
{Family: []byte("f"), Qualifier: []byte("q"), Value: uint64ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("s"), Value: []byte("just test")},
{Family: []byte("f"), Qualifier: []byte("fail"), Value: []byte("1")},
{Family: []byte("m1"), Qualifier: []byte("k1"), Value: uint32ToByte(1)},
{Family: []byte("m1"), Qualifier: []byte("k2"), Value: uint16ToByte(2)},
{Family: []byte("m2"), Qualifier: []byte("k1"), Value: []byte("1")},
{Family: []byte("m2"), Qualifier: []byte("k2"), Value: []byte("2")},
},
}
var resultcase3 = []testOnlyQualifier{
{A: 10000000000, B: &resultb[0], C: 100, D: 1000000, S: &results[0],
MapInt: map[string]int{"k1": 1, "k2": 2},
MapString: map[string]string{"k1": "1", "k2": "2"}},
}
func (t *testOnlyQualifier) equal(o testOnlyQualifier) bool {
return t.A == o.A &&
*t.B == *o.B &&
t.C == o.C &&
t.D == o.D &&
*t.S == *o.S &&
t.FailField == o.FailField &&
reflect.DeepEqual(t.MapInt, o.MapInt) &&
reflect.DeepEqual(t.MapString, o.MapString)
}
func TestParser_OnlyQualifier(t *testing.T) {
var parser = Parser{}
for i, cells := range testcase3 {
var result testOnlyQualifier
var err = parser.Parse(cells, &result)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v", result, resultcase3[i])
if !resultcase3[i].equal(result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
func TestParser_PartialFamilyPartPartialQualifier(t *testing.T) {
var testcase = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: uint32ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: uint16ToByte(100)},
{Family: []byte("f"), Qualifier: []byte("q"), Value: uint64ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("s"), Value: []byte("just test")},
{Family: []byte("f"), Qualifier: []byte("fail"), Value: []byte("1")},
},
}
type testStruct struct {
A int `family:"f" qualifier:"q64"`
Map map[string]int `family:"f"`
}
var resultcase = []testStruct{
{A: 10000000000,
Map: map[string]int{
"q32": 1000000,
"q16": 100,
"q": 1000000,
"fail": int('1'),
}},
}
var parser = Parser{}
for i, cells := range testcase {
var result testStruct
var resInterface interface{} = &result
var start = time.Now()
var err = parser.Parse(cells, &resInterface)
var elapse = time.Since(start)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v, parse time=%v", resInterface, resultcase[i], elapse)
if !reflect.DeepEqual(resultcase[i], result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
func TestParser_SetBasicValue(t *testing.T) {
var (
i32s int32 = -2
i64s int64 = -3
i16s int16 = -4
i32d int32
i64d int64
i16d int16
i64Bytes = uint64ToByte(uint64(i64s))
i32Bytes = uint32ToByte(uint32(i32s))
i16Bytes = uint16ToByte(uint16(i16s))
)
var rvi32 = reflect.ValueOf(&i32d)
var e = setBasicValue(i32Bytes, rvi32, "rvi32", ByteBigEndianToUint64)
if e != nil {
t.Errorf("fail, err=%+v", e)
t.FailNow()
}
if i32s != i32d {
t.Errorf("fail,expect=%d, got=%d", i32d, i32s)
t.FailNow()
}
var rvi64 = reflect.ValueOf(&i64d)
e = setBasicValue(i64Bytes, rvi64, "rvi64", ByteBigEndianToUint64)
if e != nil {
t.Errorf("fail, err=%+v", e)
t.FailNow()
}
if i64s != i64d {
t.Errorf("fail,expect=%d, got=%d", i64d, i64s)
t.FailNow()
}
var rvi16 = reflect.ValueOf(&i16d)
e = setBasicValue(i16Bytes, rvi16, "rvi16", ByteBigEndianToUint64)
if e != nil {
t.Errorf("fail, err=%+v", e)
t.FailNow()
}
if i16s != i16d {
t.Errorf("fail,expect=%d, got=%d", i16d, i16s)
t.FailNow()
}
}
func TestParser_Minus(t *testing.T) {
var i64 int64 = -2
var i32 int32 = -3
var i16 int16 = -4
var i8 int8 = -128
var testcase = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("i64"), Value: uint64ToByte(uint64(i64))},
{Family: []byte("f"), Qualifier: []byte("i32"), Value: uint32ToByte(uint32(i32))},
{Family: []byte("f"), Qualifier: []byte("i16"), Value: uint16ToByte(uint16(i16))},
{Family: []byte("f"), Qualifier: []byte("i8"), Value: []byte{uint8(i8)}},
},
}
type testStruct struct {
I64 int64 `family:"f" qualifier:"i64"`
I32 int32 `family:"f" qualifier:"i32"`
I16 int16 `family:"f" qualifier:"i16"`
I8 int8 `family:"f" qualifier:"i8"`
}
var resultcase = []testStruct{
{
I64: i64,
I32: i32,
I16: i16,
I8: i8,
},
}
var parser = Parser{}
for i, cells := range testcase {
var result testStruct
var resInterface interface{} = &result
var start = time.Now()
var err = parser.Parse(cells, &resInterface)
var elapse = time.Since(start)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v, parse time=%v", resInterface, resultcase[i], elapse)
if !reflect.DeepEqual(resultcase[i], result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
func TestParser_Overflow(t *testing.T) {
var testcase = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("i64"), Value: uint64ToByte(0xff01020304050607)},
{Family: []byte("f"), Qualifier: []byte("i32"), Value: uint64ToByte(0xff01020304050607)},
{Family: []byte("f"), Qualifier: []byte("i16"), Value: uint64ToByte(0xff01020304050607)},
{Family: []byte("f"), Qualifier: []byte("i8"), Value: uint64ToByte(0xff01020304050607)},
},
}
type testStruct struct {
I64 int64 `family:"f" qualifier:"i64"`
I32 int32 `family:"f" qualifier:"i32"`
I16 int16 `family:"f" qualifier:"i16"`
I8 int8 `family:"f" qualifier:"i8"`
}
var resultcase = []testStruct{
{
I64: -(0xffffffffffffffff - 0xff01020304050607 + 1),
I32: 0xff01020304050607 & 0xffffffff,
I16: 0xff01020304050607 & 0xffff,
I8: 0xff01020304050607 & 0xff,
},
}
var parser = Parser{}
for i, cells := range testcase {
var result testStruct
var resInterface interface{} = &result
var start = time.Now()
var err = parser.Parse(cells, &resInterface)
var elapse = time.Since(start)
if err != nil {
t.Logf("err=%v", err)
t.Fail()
}
t.Logf("result=%v, expect=%v, parse time=%v", resInterface, resultcase[i], elapse)
if !reflect.DeepEqual(resultcase[i], result) {
t.Logf("fail case: index=%d", i)
t.Fail()
}
}
}
func BenchmarkParser(b *testing.B) {
var testcase = [][]*hrpc.Cell{
{
{Family: []byte("f"), Qualifier: []byte("q64"), Value: uint64ToByte(10000000000)},
{Family: []byte("f"), Qualifier: []byte("q32"), Value: uint32ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("q16"), Value: uint16ToByte(100)},
{Family: []byte("f"), Qualifier: []byte("q"), Value: uint64ToByte(1000000)},
{Family: []byte("f"), Qualifier: []byte("fail"), Value: uint64ToByte(100)},
},
}
type testStruct struct {
A int `family:"f" qualifier:"q64"`
B int32 `family:"f" qualifier:"q32"`
C int16 `family:"f" qualifier:"q16"`
D int `qualifier:"q"`
S string `qualifier:"s"`
Map map[string]int `family:"f"`
}
var parser Parser
b.Logf("bench parser")
for i := 0; i < b.N; i++ {
var result testStruct
parser.Parse(testcase[0], &result)
}
}
func BenchmarkJson(b *testing.B) {
var text = []byte("{\"a\": 123, \"b\": \"1234\", \"c\": \"1234\", \"d\": \"1234\", \"e\" :{\"a\": 123, \"b\": \"1234\"}}")
var result struct {
A int `json:"a"`
B string `json:"b"`
C string `json:"c"`
D string `json:"d"`
E struct {
A int `json:"a"`
B string `json:"b"`
} `json:"e"`
}
b.Logf("bench json")
for i := 0; i < b.N; i++ {
if err := json.Unmarshal(text, &result); err != nil {
b.FailNow()
}
}
}

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["base_utils.go"],
importpath = "go-common/app/admin/main/up/util/mathutil",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,58 @@
package mathutil
//EPSILON very small number
var EPSILON float32 = 0.00000001
//FloatEquals float equals
func FloatEquals(a, b float32) bool {
if (a-b) < EPSILON && (b-a) < EPSILON {
return true
}
return false
}
//EPSILON64 very small number
var EPSILON64 = 0.00000001
//Float64Equals float equal
func Float64Equals(a, b float64) bool {
if (a-b) < EPSILON64 && (b-a) < EPSILON64 {
return true
}
return false
}
//Min min
func Min(a, b int) int {
if a < b {
return a
}
if b < a {
return b
}
return a
}
//Max max
func Max(a, b int) int {
if a > b {
return a
}
if b > a {
return b
}
return a
}
// IntersectInt64 intersect map
func IntersectInt64(as, bs []int64) []int64 {
i := make([]int64, 0, Max(len(as), len(bs)))
for _, a := range as {
for _, b := range bs {
if a == b {
i = append(i, a)
}
}
}
return i
}

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["now_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = [
"main.go",
"now.go",
],
importpath = "go-common/app/admin/main/up/util/now",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,3 @@
guard 'gotest' do
watch(%r{\.go$})
end

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-NOW Jinzhu <wosmvp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,116 @@
## Now
Now is a time toolkit for golang
[![wercker status](https://app.wercker.com/status/a350da4eae6cb28a35687ba41afb565a/s/master "wercker status")](https://app.wercker.com/project/byKey/a350da4eae6cb28a35687ba41afb565a)
## Install
```
go get -u github.com/jinzhu/now
```
## Usage
Calculating time based on current time
```go
import "github.com/jinzhu/now"
time.Now() // 2013-11-18 17:51:49.123456789 Mon
now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon
now.BeginningOfHour() // 2013-11-18 17:00:00 Mon
now.BeginningOfDay() // 2013-11-18 00:00:00 Mon
now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun
now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri
now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue
now.BeginningOfYear() // 2013-01-01 00:00:00 Tue
now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday
now.BeginningOfWeek() // 2013-11-18 00:00:00 Mon
now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon
now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon
now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon
now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat
now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat
now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue
now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue
now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday
now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun
```
Calculating time based on another time
```go
t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.Now().Location())
now.New(t).EndOfMonth() // 2013-02-28 23:59:59.999999999 Thu
```
### Monday/Sunday
Don't be bothered with the `WeekStartDay` setting, you can use `Monday`, `Sunday`
```go
now.Monday() // 2013-11-18 00:00:00 Mon
now.Sunday() // 2013-11-24 00:00:00 Sun (Next Sunday)
now.EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of next Sunday)
t := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.Now().Location()) // 2013-11-24 17:51:49.123456789 Sun
now.New(t).Monday() // 2013-11-18 00:00:00 Sun (Last Monday if today is Sunday)
now.New(t).Sunday() // 2013-11-24 00:00:00 Sun (Beginning Of Today if today is Sunday)
now.New(t).EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of Today if today is Sunday)
```
### Parse String to Time
```go
time.Now() // 2013-11-18 17:51:49.123456789 Mon
// Parse(string) (time.Time, error)
t, err := now.Parse("2017") // 2017-01-01 00:00:00, nil
t, err := now.Parse("2017-10") // 2017-10-01 00:00:00, nil
t, err := now.Parse("2017-10-13") // 2017-10-13 00:00:00, nil
t, err := now.Parse("1999-12-12 12") // 1999-12-12 12:00:00, nil
t, err := now.Parse("1999-12-12 12:20") // 1999-12-12 12:20:00, nil
t, err := now.Parse("1999-12-12 12:20:21") // 1999-12-12 12:20:00, nil
t, err := now.Parse("10-13") // 2013-10-13 00:00:00, nil
t, err := now.Parse("12:20") // 2013-11-18 12:20:00, nil
t, err := now.Parse("12:20:13") // 2013-11-18 12:20:13, nil
t, err := now.Parse("14") // 2013-11-18 14:00:00, nil
t, err := now.Parse("99:99") // 2013-11-18 12:20:00, Can't parse string as time: 99:99
// MustParse must parse string to time or it will panic
now.MustParse("2013-01-13") // 2013-01-13 00:00:00
now.MustParse("02-17") // 2013-02-17 00:00:00
now.MustParse("2-17") // 2013-02-17 00:00:00
now.MustParse("8") // 2013-11-18 08:00:00
now.MustParse("2002-10-12 22:14") // 2002-10-12 22:14:00
now.MustParse("99:99") // panic: Can't parse string as time: 99:99
```
Extend `now` to support more formats is quite easy, just update `now.TimeFormats` with other time layouts, e.g:
```go
now.TimeFormats = append(now.TimeFormats, "02 Jan 2006 15:04")
```
Please send me pull requests if you want a format to be supported officially
## Contributing
You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do.
# Author
**jinzhu**
* <http://github.com/jinzhu>
* <wosmvp@gmail.com>
* <http://twitter.com/zhangjinzhu>
## License
Released under the [MIT License](http://www.opensource.org/licenses/MIT).

View File

@@ -0,0 +1,138 @@
// Package now is a time toolkit for golang.
//
// More details README here: https://github.com/jinzhu/now
//
// import "github.com/jinzhu/now"
//
// now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon
// now.BeginningOfDay() // 2013-11-18 00:00:00 Mon
// now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon
package now
import "time"
// WeekStartDay set week start day, default is sunday
var WeekStartDay = time.Sunday
// TimeFormats default time formats will be parsed as
var TimeFormats = []string{"1/2/2006", "1/2/2006 15:4:5", "2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2", "15:4:5", "15:4", "15", "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST"}
// Now now struct
type Now struct {
time.Time
}
// New initialize Now with time
func New(t time.Time) *Now {
return &Now{t}
}
// BeginningOfMinute beginning of minute
func BeginningOfMinute() time.Time {
return New(time.Now()).BeginningOfMinute()
}
// BeginningOfHour beginning of hour
func BeginningOfHour() time.Time {
return New(time.Now()).BeginningOfHour()
}
// BeginningOfDay beginning of day
func BeginningOfDay() time.Time {
return New(time.Now()).BeginningOfDay()
}
// BeginningOfWeek beginning of week
func BeginningOfWeek() time.Time {
return New(time.Now()).BeginningOfWeek()
}
// BeginningOfMonth beginning of month
func BeginningOfMonth() time.Time {
return New(time.Now()).BeginningOfMonth()
}
// BeginningOfQuarter beginning of quarter
func BeginningOfQuarter() time.Time {
return New(time.Now()).BeginningOfQuarter()
}
// BeginningOfYear beginning of year
func BeginningOfYear() time.Time {
return New(time.Now()).BeginningOfYear()
}
// EndOfMinute end of minute
func EndOfMinute() time.Time {
return New(time.Now()).EndOfMinute()
}
// EndOfHour end of hour
func EndOfHour() time.Time {
return New(time.Now()).EndOfHour()
}
// EndOfDay end of day
func EndOfDay() time.Time {
return New(time.Now()).EndOfDay()
}
// EndOfWeek end of week
func EndOfWeek() time.Time {
return New(time.Now()).EndOfWeek()
}
// EndOfMonth end of month
func EndOfMonth() time.Time {
return New(time.Now()).EndOfMonth()
}
// EndOfQuarter end of quarter
func EndOfQuarter() time.Time {
return New(time.Now()).EndOfQuarter()
}
// EndOfYear end of year
func EndOfYear() time.Time {
return New(time.Now()).EndOfYear()
}
// Monday monday
func Monday() time.Time {
return New(time.Now()).Monday()
}
// Sunday sunday
func Sunday() time.Time {
return New(time.Now()).Sunday()
}
// EndOfSunday end of sunday
func EndOfSunday() time.Time {
return New(time.Now()).EndOfSunday()
}
// Parse parse string to time
func Parse(strs ...string) (time.Time, error) {
return New(time.Now()).Parse(strs...)
}
// ParseInLocation parse string to time in location
func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) {
return New(time.Now().In(loc)).Parse(strs...)
}
// MustParse must parse string to time or will panic
func MustParse(strs ...string) time.Time {
return New(time.Now()).MustParse(strs...)
}
// MustParseInLocation must parse string to time in location or will panic
func MustParseInLocation(loc *time.Location, strs ...string) time.Time {
return New(time.Now().In(loc)).MustParse(strs...)
}
// Between check now between the begin, end time or not
func Between(time1, time2 string) bool {
return New(time.Now()).Between(time1, time2)
}

View File

@@ -0,0 +1,203 @@
package now
import (
"errors"
"regexp"
"time"
)
// BeginningOfMinute beginning of minute
func (now *Now) BeginningOfMinute() time.Time {
return now.Truncate(time.Minute)
}
// BeginningOfHour beginning of hour
func (now *Now) BeginningOfHour() time.Time {
y, m, d := now.Date()
return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location())
}
// BeginningOfDay beginning of day
func (now *Now) BeginningOfDay() time.Time {
y, m, d := now.Date()
return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location())
}
// BeginningOfWeek beginning of week
func (now *Now) BeginningOfWeek() time.Time {
t := now.BeginningOfDay()
weekday := int(t.Weekday())
if WeekStartDay != time.Sunday {
weekStartDayInt := int(WeekStartDay)
if weekday < weekStartDayInt {
weekday = weekday + 7 - weekStartDayInt
} else {
weekday = weekday - weekStartDayInt
}
}
return t.AddDate(0, 0, -weekday)
}
// BeginningOfMonth beginning of month
func (now *Now) BeginningOfMonth() time.Time {
y, m, _ := now.Date()
return time.Date(y, m, 1, 0, 0, 0, 0, now.Location())
}
// BeginningOfQuarter beginning of quarter
func (now *Now) BeginningOfQuarter() time.Time {
month := now.BeginningOfMonth()
offset := (int(month.Month()) - 1) % 3
return month.AddDate(0, -offset, 0)
}
// BeginningOfYear BeginningOfYear beginning of year
func (now *Now) BeginningOfYear() time.Time {
y, _, _ := now.Date()
return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location())
}
// EndOfMinute end of minute
func (now *Now) EndOfMinute() time.Time {
return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond)
}
// EndOfHour end of hour
func (now *Now) EndOfHour() time.Time {
return now.BeginningOfHour().Add(time.Hour - time.Nanosecond)
}
// EndOfDay end of day
func (now *Now) EndOfDay() time.Time {
y, m, d := now.Date()
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location())
}
// EndOfWeek end of week
func (now *Now) EndOfWeek() time.Time {
return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond)
}
// EndOfMonth end of month
func (now *Now) EndOfMonth() time.Time {
return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond)
}
// EndOfQuarter end of quarter
func (now *Now) EndOfQuarter() time.Time {
return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond)
}
// EndOfYear end of year
func (now *Now) EndOfYear() time.Time {
return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond)
}
// Monday monday
func (now *Now) Monday() time.Time {
t := now.BeginningOfDay()
weekday := int(t.Weekday())
if weekday == 0 {
weekday = 7
}
return t.AddDate(0, 0, -weekday+1)
}
// Sunday sunday
func (now *Now) Sunday() time.Time {
t := now.BeginningOfDay()
weekday := int(t.Weekday())
if weekday == 0 {
return t
}
return t.AddDate(0, 0, (7 - weekday))
}
// EndOfSunday end of sunday
func (now *Now) EndOfSunday() time.Time {
return New(now.Sunday()).EndOfDay()
}
func parseWithFormat(str string) (t time.Time, err error) {
for _, format := range TimeFormats {
t, err = time.Parse(format, str)
if err == nil {
return
}
}
err = errors.New("Can't parse string as time: " + str)
return
}
var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*)\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`) // match 15:04:05, 15:04:05.000, 15:04:05.000000 15, 2017-01-01 15:04, etc
var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`) // match 15:04:05, 15, 15:04:05.000, 15:04:05.000000, etc
// Parse parse string to time
func (now *Now) Parse(strs ...string) (t time.Time, err error) {
var (
setCurrentTime bool
parseTime []int
currentTime = []int{now.Nanosecond(), now.Second(), now.Minute(), now.Hour(), now.Day(), int(now.Month()), now.Year()}
currentLocation = now.Location()
onlyTimeInStr = true
)
for _, str := range strs {
hasTimeInStr := hasTimeRegexp.MatchString(str) // match 15:04:05, 15
onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str)
if t, err = parseWithFormat(str); err == nil {
location := t.Location()
if location.String() == "UTC" {
location = currentLocation
}
parseTime = []int{t.Nanosecond(), t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()}
for i, v := range parseTime {
// Don't reset hour, minute, second if current time str including time
if hasTimeInStr && i <= 3 {
continue
}
// If value is zero, replace it with current time
if v == 0 {
if setCurrentTime {
parseTime[i] = currentTime[i]
}
} else {
setCurrentTime = true
}
// if current time only includes time, should change day, month to current time
if onlyTimeInStr {
if i == 4 || i == 5 {
parseTime[i] = currentTime[i]
continue
}
}
}
t = time.Date(parseTime[6], time.Month(parseTime[5]), parseTime[4], parseTime[3], parseTime[2], parseTime[1], parseTime[0], location)
currentTime = []int{t.Nanosecond(), t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()}
}
}
return
}
// MustParse must parse string to time or it will panic
func (now *Now) MustParse(strs ...string) (t time.Time) {
t, err := now.Parse(strs...)
if err != nil {
panic(err)
}
return t
}
// Between check time between the begin, end time or not
func (now *Now) Between(begin, end string) bool {
beginTime := now.MustParse(begin)
endTime := now.MustParse(end)
return now.After(beginTime) && now.Before(endTime)
}

View File

@@ -0,0 +1,344 @@
package now
import (
"testing"
"time"
)
var (
format = "2006-01-02 15:04:05.999999999"
locationCaracas *time.Location
locationBerlin *time.Location
timeCaracas time.Time
)
func init() {
var err error
if locationCaracas, err = time.LoadLocation("America/Caracas"); err != nil {
panic(err)
}
if locationBerlin, err = time.LoadLocation("Europe/Berlin"); err != nil {
panic(err)
}
timeCaracas = time.Date(2016, 1, 1, 12, 10, 0, 0, locationCaracas)
}
func assertT(t *testing.T) func(time.Time, string, string) {
return func(actual time.Time, expected string, msg string) {
actualStr := actual.Format(format)
if actualStr != expected {
t.Errorf("Failed %s: actual: %v, expected: %v", msg, actualStr, expected)
}
}
}
func TestBeginningOf(t *testing.T) {
assert := assertT(t)
n := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.UTC)
assert(New(n).BeginningOfMinute(), "2013-11-18 17:51:00", "BeginningOfMinute")
WeekStartDay = time.Monday
assert(New(n).BeginningOfWeek(), "2013-11-18 00:00:00", "BeginningOfWeek, FirstDayMonday")
WeekStartDay = time.Tuesday
assert(New(n).BeginningOfWeek(), "2013-11-12 00:00:00", "BeginningOfWeek, FirstDayTuesday")
WeekStartDay = time.Wednesday
assert(New(n).BeginningOfWeek(), "2013-11-13 00:00:00", "BeginningOfWeek, FirstDayWednesday")
WeekStartDay = time.Thursday
assert(New(n).BeginningOfWeek(), "2013-11-14 00:00:00", "BeginningOfWeek, FirstDayThursday")
WeekStartDay = time.Friday
assert(New(n).BeginningOfWeek(), "2013-11-15 00:00:00", "BeginningOfWeek, FirstDayFriday")
WeekStartDay = time.Saturday
assert(New(n).BeginningOfWeek(), "2013-11-16 00:00:00", "BeginningOfWeek, FirstDaySaturday")
WeekStartDay = time.Sunday
assert(New(n).BeginningOfWeek(), "2013-11-17 00:00:00", "BeginningOfWeek, FirstDaySunday")
assert(New(n).BeginningOfHour(), "2013-11-18 17:00:00", "BeginningOfHour")
// Truncate with hour bug
assert(New(timeCaracas).BeginningOfHour(), "2016-01-01 12:00:00", "BeginningOfHour Caracas")
assert(New(n).BeginningOfDay(), "2013-11-18 00:00:00", "BeginningOfDay")
location, err := time.LoadLocation("Japan")
if err != nil {
t.Fatalf("Error loading location: %v", err)
}
beginningOfDay := time.Date(2015, 05, 01, 0, 0, 0, 0, location)
assert(New(beginningOfDay).BeginningOfDay(), "2015-05-01 00:00:00", "BeginningOfDay")
// DST
dstBeginningOfDay := time.Date(2017, 10, 29, 10, 0, 0, 0, locationBerlin)
assert(New(dstBeginningOfDay).BeginningOfDay(), "2017-10-29 00:00:00", "BeginningOfDay DST")
assert(New(n).BeginningOfWeek(), "2013-11-17 00:00:00", "BeginningOfWeek")
dstBegginingOfWeek := time.Date(2017, 10, 30, 12, 0, 0, 0, locationBerlin)
assert(New(dstBegginingOfWeek).BeginningOfWeek(), "2017-10-29 00:00:00", "BeginningOfWeek")
dstBegginingOfWeek = time.Date(2017, 10, 29, 12, 0, 0, 0, locationBerlin)
assert(New(dstBegginingOfWeek).BeginningOfWeek(), "2017-10-29 00:00:00", "BeginningOfWeek")
WeekStartDay = time.Monday
assert(New(n).BeginningOfWeek(), "2013-11-18 00:00:00", "BeginningOfWeek, FirstDayMonday")
dstBegginingOfWeek = time.Date(2017, 10, 24, 12, 0, 0, 0, locationBerlin)
assert(New(dstBegginingOfWeek).BeginningOfWeek(), "2017-10-23 00:00:00", "BeginningOfWeek, FirstDayMonday")
dstBegginingOfWeek = time.Date(2017, 10, 29, 12, 0, 0, 0, locationBerlin)
assert(New(dstBegginingOfWeek).BeginningOfWeek(), "2017-10-23 00:00:00", "BeginningOfWeek, FirstDayMonday")
WeekStartDay = time.Sunday
assert(New(n).BeginningOfMonth(), "2013-11-01 00:00:00", "BeginningOfMonth")
// DST
dstBeginningOfMonth := time.Date(2017, 10, 31, 0, 0, 0, 0, locationBerlin)
assert(New(dstBeginningOfMonth).BeginningOfMonth(), "2017-10-01 00:00:00", "BeginningOfMonth DST")
assert(New(n).BeginningOfQuarter(), "2013-10-01 00:00:00", "BeginningOfQuarter")
// DST
assert(New(dstBeginningOfMonth).BeginningOfQuarter(), "2017-10-01 00:00:00", "BeginningOfQuarter DST")
dstBeginningOfQuarter := time.Date(2017, 11, 24, 0, 0, 0, 0, locationBerlin)
assert(New(dstBeginningOfQuarter).BeginningOfQuarter(), "2017-10-01 00:00:00", "BeginningOfQuarter DST")
assert(New(n.AddDate(0, -1, 0)).BeginningOfQuarter(), "2013-10-01 00:00:00", "BeginningOfQuarter")
assert(New(n.AddDate(0, 1, 0)).BeginningOfQuarter(), "2013-10-01 00:00:00", "BeginningOfQuarter")
// DST
assert(New(dstBeginningOfQuarter).BeginningOfYear(), "2017-01-01 00:00:00", "BeginningOfYear DST")
assert(New(timeCaracas).BeginningOfYear(), "2016-01-01 00:00:00", "BeginningOfYear Caracas")
}
func TestEndOf(t *testing.T) {
assert := assertT(t)
n := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.UTC)
assert(New(n).EndOfMinute(), "2013-11-18 17:51:59.999999999", "EndOfMinute")
assert(New(n).EndOfHour(), "2013-11-18 17:59:59.999999999", "EndOfHour")
assert(New(timeCaracas).EndOfHour(), "2016-01-01 12:59:59.999999999", "EndOfHour Caracas")
assert(New(n).EndOfDay(), "2013-11-18 23:59:59.999999999", "EndOfDay")
dstEndOfDay := time.Date(2017, 10, 29, 1, 0, 0, 0, locationBerlin)
assert(New(dstEndOfDay).EndOfDay(), "2017-10-29 23:59:59.999999999", "EndOfDay DST")
WeekStartDay = time.Tuesday
assert(New(n).EndOfWeek(), "2013-11-18 23:59:59.999999999", "EndOfWeek, FirstDayTuesday")
WeekStartDay = time.Wednesday
assert(New(n).EndOfWeek(), "2013-11-19 23:59:59.999999999", "EndOfWeek, FirstDayWednesday")
WeekStartDay = time.Thursday
assert(New(n).EndOfWeek(), "2013-11-20 23:59:59.999999999", "EndOfWeek, FirstDayThursday")
WeekStartDay = time.Friday
assert(New(n).EndOfWeek(), "2013-11-21 23:59:59.999999999", "EndOfWeek, FirstDayFriday")
WeekStartDay = time.Saturday
assert(New(n).EndOfWeek(), "2013-11-22 23:59:59.999999999", "EndOfWeek, FirstDaySaturday")
WeekStartDay = time.Sunday
assert(New(n).EndOfWeek(), "2013-11-23 23:59:59.999999999", "EndOfWeek, FirstDaySunday")
WeekStartDay = time.Monday
assert(New(n).EndOfWeek(), "2013-11-24 23:59:59.999999999", "EndOfWeek, FirstDayMonday")
dstEndOfWeek := time.Date(2017, 10, 24, 12, 0, 0, 0, locationBerlin)
assert(New(dstEndOfWeek).EndOfWeek(), "2017-10-29 23:59:59.999999999", "EndOfWeek, FirstDayMonday")
dstEndOfWeek = time.Date(2017, 10, 29, 12, 0, 0, 0, locationBerlin)
assert(New(dstEndOfWeek).EndOfWeek(), "2017-10-29 23:59:59.999999999", "EndOfWeek, FirstDayMonday")
WeekStartDay = time.Sunday
assert(New(n).EndOfWeek(), "2013-11-23 23:59:59.999999999", "EndOfWeek")
dstEndOfWeek = time.Date(2017, 10, 29, 0, 0, 0, 0, locationBerlin)
assert(New(dstEndOfWeek).EndOfWeek(), "2017-11-04 23:59:59.999999999", "EndOfWeek")
dstEndOfWeek = time.Date(2017, 10, 29, 12, 0, 0, 0, locationBerlin)
assert(New(dstEndOfWeek).EndOfWeek(), "2017-11-04 23:59:59.999999999", "EndOfWeek")
assert(New(n).EndOfMonth(), "2013-11-30 23:59:59.999999999", "EndOfMonth")
assert(New(n).EndOfQuarter(), "2013-12-31 23:59:59.999999999", "EndOfQuarter")
assert(New(n.AddDate(0, -1, 0)).EndOfQuarter(), "2013-12-31 23:59:59.999999999", "EndOfQuarter")
assert(New(n.AddDate(0, 1, 0)).EndOfQuarter(), "2013-12-31 23:59:59.999999999", "EndOfQuarter")
assert(New(n).EndOfYear(), "2013-12-31 23:59:59.999999999", "EndOfYear")
n1 := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.UTC)
assert(New(n1).EndOfMonth(), "2013-02-28 23:59:59.999999999", "EndOfMonth for 2013/02")
n2 := time.Date(1900, 02, 18, 17, 51, 49, 123456789, time.UTC)
assert(New(n2).EndOfMonth(), "1900-02-28 23:59:59.999999999", "EndOfMonth")
}
func TestMondayAndSunday(t *testing.T) {
assert := assertT(t)
n := time.Date(2013, 11, 19, 17, 51, 49, 123456789, time.UTC)
n2 := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.UTC)
nDst := time.Date(2017, 10, 29, 10, 0, 0, 0, locationBerlin)
assert(New(n).Monday(), "2013-11-18 00:00:00", "Monday")
assert(New(n2).Monday(), "2013-11-18 00:00:00", "Monday")
assert(New(timeCaracas).Monday(), "2015-12-28 00:00:00", "Monday Caracas")
assert(New(nDst).Monday(), "2017-10-23 00:00:00", "Monday DST")
assert(New(n).Sunday(), "2013-11-24 00:00:00", "Sunday")
assert(New(n2).Sunday(), "2013-11-24 00:00:00", "Sunday")
assert(New(timeCaracas).Sunday(), "2016-01-03 00:00:00", "Sunday Caracas")
assert(New(nDst).Sunday(), "2017-10-29 00:00:00", "Sunday DST")
assert(New(n).EndOfSunday(), "2013-11-24 23:59:59.999999999", "EndOfSunday")
assert(New(timeCaracas).EndOfSunday(), "2016-01-03 23:59:59.999999999", "EndOfSunday Caracas")
assert(New(nDst).EndOfSunday(), "2017-10-29 23:59:59.999999999", "EndOfSunday DST")
assert(New(n).BeginningOfWeek(), "2013-11-17 00:00:00", "BeginningOfWeek, FirstDayMonday")
WeekStartDay = time.Monday
assert(New(n).BeginningOfWeek(), "2013-11-18 00:00:00", "BeginningOfWeek, FirstDayMonday")
}
func TestParse(t *testing.T) {
assert := assertT(t)
n := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.UTC)
assert(New(n).MustParse("2002"), "2002-01-01 00:00:00", "Parse 2002")
assert(New(n).MustParse("2002-10"), "2002-10-01 00:00:00", "Parse 2002-10")
assert(New(n).MustParse("2002-10-12"), "2002-10-12 00:00:00", "Parse 2002-10-12")
assert(New(n).MustParse("2002-10-12 22"), "2002-10-12 22:00:00", "Parse 2002-10-12 22")
assert(New(n).MustParse("2002-10-12 22:14"), "2002-10-12 22:14:00", "Parse 2002-10-12 22:14")
assert(New(n).MustParse("2002-10-12 2:4"), "2002-10-12 02:04:00", "Parse 2002-10-12 2:4")
assert(New(n).MustParse("2002-10-12 02:04"), "2002-10-12 02:04:00", "Parse 2002-10-12 02:04")
assert(New(n).MustParse("2002-10-12 22:14:56"), "2002-10-12 22:14:56", "Parse 2002-10-12 22:14:56")
assert(New(n).MustParse("2002-10-12 00:14:56"), "2002-10-12 00:14:56", "Parse 2002-10-12 00:14:56")
assert(New(n).MustParse("2013-12-19 23:28:09.999999999 +0800 CST"), "2013-12-19 23:28:09.999999999", "Parse two strings 2013-12-19 23:28:09.999999999 +0800 CST")
assert(New(n).MustParse("10-12"), "2013-10-12 00:00:00", "Parse 10-12")
assert(New(n).MustParse("18"), "2013-11-18 18:00:00", "Parse 18 as hour")
assert(New(n).MustParse("18:20"), "2013-11-18 18:20:00", "Parse 18:20")
assert(New(n).MustParse("00:01"), "2013-11-18 00:01:00", "Parse 00:01")
assert(New(n).MustParse("00:00:00"), "2013-11-18 00:00:00", "Parse 00:00:00")
assert(New(n).MustParse("18:20:39"), "2013-11-18 18:20:39", "Parse 18:20:39")
assert(New(n).MustParse("18:20:39", "2011-01-01"), "2011-01-01 18:20:39", "Parse two strings 18:20:39, 2011-01-01")
assert(New(n).MustParse("2011-1-1", "18:20:39"), "2011-01-01 18:20:39", "Parse two strings 2011-01-01, 18:20:39")
assert(New(n).MustParse("2011-01-01", "18"), "2011-01-01 18:00:00", "Parse two strings 2011-01-01, 18")
TimeFormats = append(TimeFormats, "02 Jan 15:04")
assert(New(n).MustParse("04 Feb 12:09"), "2013-02-04 12:09:00", "Parse 04 Feb 12:09 with specified format")
assert(New(n).MustParse("23:28:9 Dec 19, 2013 PST"), "2013-12-19 23:28:09", "Parse 23:28:9 Dec 19, 2013 PST")
if New(n).MustParse("23:28:9 Dec 19, 2013 PST").Location().String() != "PST" {
t.Errorf("Parse 23:28:9 Dec 19, 2013 PST shouldn't lose time zone")
}
n2 := New(n).MustParse("23:28:9 Dec 19, 2013 PST")
if New(n2).MustParse("10:20").Location().String() != "PST" {
t.Errorf("Parse 10:20 shouldn't change time zone")
}
TimeFormats = append(TimeFormats, "2006-01-02T15:04:05.0")
if MustParseInLocation(time.UTC, "2018-02-13T15:17:06.0").String() != "2018-02-13 15:17:06 +0000 UTC" {
t.Errorf("ParseInLocation 2018-02-13T15:17:06.0")
}
TimeFormats = append(TimeFormats, "2006-01-02 15:04:05.000")
assert(New(n).MustParse("2018-04-20 21:22:23.473"), "2018-04-20 21:22:23.473", "Parse 2018/04/20 21:22:23.473")
TimeFormats = append(TimeFormats, "15:04:05.000")
assert(New(n).MustParse("13:00:01.365"), "2013-11-18 13:00:01.365", "Parse 13:00:01.365")
TimeFormats = append(TimeFormats, "2006-01-02 15:04:05.000000")
assert(New(n).MustParse("2010-01-01 07:24:23.131384"), "2010-01-01 07:24:23.131384", "Parse 2010-01-01 07:24:23.131384")
assert(New(n).MustParse("00:00:00.182736"), "2013-11-18 00:00:00.182736", "Parse 00:00:00.182736")
}
func TestBetween(t *testing.T) {
tm := time.Date(2015, 06, 30, 17, 51, 49, 123456789, time.Now().Location())
if !New(tm).Between("23:28:9 Dec 19, 2013 PST", "23:28:9 Dec 19, 2015 PST") {
t.Errorf("Between")
}
if !New(tm).Between("2015-05-12 12:20", "2015-06-30 17:51:50") {
t.Errorf("Between")
}
}
func Example() {
time.Now() // 2013-11-18 17:51:49.123456789 Mon
BeginningOfMinute() // 2013-11-18 17:51:00 Mon
BeginningOfHour() // 2013-11-18 17:00:00 Mon
BeginningOfDay() // 2013-11-18 00:00:00 Mon
BeginningOfWeek() // 2013-11-17 00:00:00 Sun
WeekStartDay = time.Monday // Set Monday as first day
BeginningOfWeek() // 2013-11-18 00:00:00 Mon
BeginningOfMonth() // 2013-11-01 00:00:00 Fri
BeginningOfQuarter() // 2013-10-01 00:00:00 Tue
BeginningOfYear() // 2013-01-01 00:00:00 Tue
EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon
EndOfHour() // 2013-11-18 17:59:59.999999999 Mon
EndOfDay() // 2013-11-18 23:59:59.999999999 Mon
EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat
WeekStartDay = time.Monday // Set Monday as first day
EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun
EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat
EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue
EndOfYear() // 2013-12-31 23:59:59.999999999 Tue
// Use another time
t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.UTC)
New(t).EndOfMonth() // 2013-02-28 23:59:59.999999999 Thu
Monday() // 2013-11-18 00:00:00 Mon
Sunday() // 2013-11-24 00:00:00 Sun
EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun
}

View File

@@ -0,0 +1,23 @@
box: golang
build:
steps:
- setup-go-workspace
# Gets the dependencies
- script:
name: go get
code: |
go get
# Build the project
- script:
name: go build
code: |
go build ./...
# Test the project
- script:
name: go test
code: |
go test ./...

View File

@@ -0,0 +1,16 @@
language: go
sudo: false
go:
- 1.4.2
- 1.5.1
- 1.6
- tip
matrix:
allow_failures:
- go: tip
script:
- go vet ./...
- go test -v ./...

View File

@@ -0,0 +1,44 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["queue_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["queue.go"],
importpath = "go-common/app/admin/main/up/util/timerqueue",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_xtest",
srcs = ["example_test.go"],
tags = ["automanaged"],
deps = ["//app/admin/main/up/util/timerqueue:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,91 @@
[![Build Status](https://travis-ci.org/beevik/timerqueue.svg?branch=master)](https://travis-ci.org/beevik/timerqueue)
[![GoDoc](https://godoc.org/github.com/beevik/timerqueue?status.svg)](https://godoc.org/github.com/beevik/timerqueue)
timerqueue
==========
The timerqueue package implements a priority queue for objects scheduled to
perform actions at clock times.
See http://godoc.org/github.com/beevik/timerqueue for godoc-formatted API
documentation.
###Example: Scheduling timers
The following code declares an object implementing the Timer interface,
creates a timerqueue, and adds three events to the timerqueue.
```go
type event int
func (e event) OnTimer(t time.Time) {
fmt.Printf("event.OnTimer %d fired at %v\n", int(e), t)
}
queue := timerqueue.New()
queue.Schedule(event(1), time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC))
queue.Schedule(event(2), time.Date(2015, 1, 3, 0, 0, 0, 0, time.UTC))
queue.Schedule(event(3), time.Date(2015, 1, 2, 0, 0, 0, 0, time.UTC))
```
###Example: Peeking at the next timer to be scheduled
Using the queue initialized in the first example, the following code
examines the head of the timerqueue and outputs the id and time of
the event found there.
```go
e, t := queue.PeekFirst()
if e != nil {
fmt.Printf("Event %d will be first to fire at %v.\n", int(e.(event)), t)
fmt.Printf("%d events remain in the timerqueue.", queue.Len())
}
```
Output:
```
Event 1 will be first to fire at 2015-01-01 00:00:00 +0000 UTC.
3 events remain in the timerqueue.
```
###Example: Popping the next timer to be scheduled
Using the queue initialized in the first example, this code
removes the next timer to be executed until the queue is empty.
```go
for queue.Len() > 0 {
e, t := queue.PopFirst()
fmt.Printf("Event %d fires at %v.\n", int(e.(event)), t)
}
```
Output:
```
Event 1 fires at 2015-01-01 00:00:00 +0000 UTC.
Event 3 fires at 2015-01-02 00:00:00 +0000 UTC.
Event 2 fires at 2015-01-03 00:00:00 +0000 UTC.
```
###Example: Issuing OnTimer callbacks with Advance
The final example shows how to dispatch OnTimer callbacks to
timers using the timerqueue's Advance method.
Advance calls the OnTimer method for each timer scheduled
before the requested time. Timers are removed from the timerqueue
in order of their scheduling.
```go
// Call the OnTimer method for each event scheduled before
// January 10, 2015. Pop the called timer from the queue.
queue.Advance(time.Date(2015, 1, 10, 0, 0, 0, 0, time.UTC))
```
Output:
```
event.OnTimer 1 fired at 2015-01-01 00:00:00 +0000 UTC.
event.OnTimer 3 fired at 2015-01-02 00:00:00 +0000 UTC.
event.OnTimer 2 fired at 2015-01-03 00:00:00 +0000 UTC.
```

View File

@@ -0,0 +1,44 @@
package timerqueue_test
import (
"fmt"
"time"
"go-common/app/admin/main/up/util/timerqueue"
)
type event int
func (e event) OnTimer(t time.Time) {
fmt.Printf(" Event %d executed at %v\n", int(e), t)
}
// Schedule several events with a timerqueue, and dispatch
// them by calling Advance.
func ExampleQueue() {
queue := timerqueue.New()
// Schedule an event each day from Jan 1 to Jan 7, 2015.
tm := time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
for i := 1; i <= 7; i++ {
queue.Schedule(event(i), tm)
tm = tm.Add(24 * time.Hour)
}
fmt.Println("Advancing to Jan 4...")
queue.Advance(time.Date(2015, 1, 4, 0, 0, 0, 0, time.UTC))
fmt.Println("Advancing to Jan 10...")
queue.Advance(time.Date(2015, 1, 10, 0, 0, 0, 0, time.UTC))
// Output:
// Advancing to Jan 4...
// Event 1 executed at 2015-01-01 00:00:00 +0000 UTC
// Event 2 executed at 2015-01-02 00:00:00 +0000 UTC
// Event 3 executed at 2015-01-03 00:00:00 +0000 UTC
// Event 4 executed at 2015-01-04 00:00:00 +0000 UTC
// Advancing to Jan 10...
// Event 5 executed at 2015-01-05 00:00:00 +0000 UTC
// Event 6 executed at 2015-01-06 00:00:00 +0000 UTC
// Event 7 executed at 2015-01-07 00:00:00 +0000 UTC
}

View File

@@ -0,0 +1,181 @@
// Package timerqueue implements a priority queue for objects scheduled at a
// particular time.
package timerqueue
import (
"container/heap"
"errors"
"time"
)
// Timer is an interface that types implement to schedule and receive OnTimer
// callbacks.
type Timer interface {
OnTimer(t time.Time)
}
//NewTimerWrapper util struct
func NewTimerWrapper(fun TimerFunc) (result *TimerWrapper) {
result = &TimerWrapper{fun}
return
}
//TimerFunc timer function
type TimerFunc func(t time.Time)
//TimerWrapper just a time wrapper
type TimerWrapper struct {
fun TimerFunc
}
//OnTimer ontimer
func (t *TimerWrapper) OnTimer(tm time.Time) {
t.fun(tm)
}
// Queue is a time-sorted collection of Timer objects.
type Queue struct {
heap timerHeap
table map[Timer]*timerData
}
type timerData struct {
timer Timer
time time.Time
index int
period time.Duration // if > 0, this will be a periodically event
}
// New creates a new timer priority queue.
func New() *Queue {
return &Queue{
table: make(map[Timer]*timerData),
}
}
// Len returns the current number of timer objects in the queue.
func (q *Queue) Len() int {
return len(q.heap)
}
// Schedule schedules a timer for exectuion at time tm. If the
// timer was already scheduled, it is rescheduled.
func (q *Queue) Schedule(t Timer, tm time.Time) {
q.ScheduleRepeat(t, tm, 0)
}
// ScheduleRepeat give 0 duration, will not be repeatedly event
func (q *Queue) ScheduleRepeat(t Timer, tm time.Time, period time.Duration) {
if data, ok := q.table[t]; !ok {
data = &timerData{t, tm, 0, period}
heap.Push(&q.heap, data)
q.table[t] = data
} else {
data.time = tm
heap.Fix(&q.heap, data.index)
}
}
// Unschedule unschedules a timer's execution.
func (q *Queue) Unschedule(t Timer) {
if data, ok := q.table[t]; ok {
heap.Remove(&q.heap, data.index)
delete(q.table, t)
}
}
// GetTime returns the time at which the timer is scheduled.
// If the timer isn't currently scheduled, an error is returned.
func (q *Queue) GetTime(t Timer) (tm time.Time, err error) {
if data, ok := q.table[t]; ok {
return data.time, nil
}
return time.Time{}, errors.New("timerqueue: timer not scheduled")
}
// IsScheduled returns true if the timer is currently scheduled.
func (q *Queue) IsScheduled(t Timer) bool {
_, ok := q.table[t]
return ok
}
// Clear unschedules all currently scheduled timers.
func (q *Queue) Clear() {
q.heap, q.table = nil, make(map[Timer]*timerData)
}
// PopFirst removes and returns the next timer to be scheduled and
// the time at which it is scheduled to run.
func (q *Queue) PopFirst() (t Timer, tm time.Time) {
if len(q.heap) > 0 {
data := heap.Pop(&q.heap).(*timerData)
delete(q.table, data.timer)
return data.timer, data.time
}
return nil, time.Time{}
}
// PeekFirst returns the next timer to be scheduled and the time
// at which it is scheduled to run. It does not modify the contents
// of the timer queue.
func (q *Queue) PeekFirst() (t Timer, tm time.Time) {
if len(q.heap) > 0 {
return q.heap[0].timer, q.heap[0].time
}
return nil, time.Time{}
}
// Advance executes OnTimer callbacks for all timers scheduled to be
// run before the time 'tm'. Executed timers are removed from the
// timer queue.
func (q *Queue) Advance(tm time.Time) {
for len(q.heap) > 0 && !tm.Before(q.heap[0].time) {
data := q.heap[0]
heap.Remove(&q.heap, data.index)
if data.period > 0 {
data.time = data.time.Add(data.period)
heap.Push(&q.heap, data)
} else {
delete(q.table, data.timer)
}
data.timer.OnTimer(data.time)
}
}
/*
* timerHeap
*/
type timerHeap []*timerData
//Len len interface
func (h timerHeap) Len() int {
return len(h)
}
//Less less interface
func (h timerHeap) Less(i, j int) bool {
return h[i].time.Before(h[j].time)
}
//Swap swap interface
func (h timerHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
h[i].index, h[j].index = i, j
}
//Push push interface
func (h *timerHeap) Push(x interface{}) {
data := x.(*timerData)
*h = append(*h, data)
data.index = len(*h) - 1
}
//Pop pop interface
func (h *timerHeap) Pop() interface{} {
n := len(*h)
data := (*h)[n-1]
*h = (*h)[:n-1]
data.index = -1
return data
}

View File

@@ -0,0 +1,75 @@
package timerqueue
import (
"math/rand"
"testing"
"time"
)
type object struct {
value int
}
var executed int
func (o *object) OnTimer(t time.Time) {
executed++
}
func populateQueue(t *testing.T, now time.Time) *Queue {
q := New()
count := 200
objects := make([]*object, count)
// Add a bunch of objects to the queue in random order.
for i, j := range rand.Perm(count) {
tm := now.Add(time.Duration(i+1) * time.Hour)
objects[j] = &object{j}
q.Schedule(objects[j], tm)
}
if q.Len() != count {
t.Error("invalid queue length:", q.Len())
}
return q
}
func TestQueue(t *testing.T) {
for iter := 0; iter < 100; iter++ {
now := time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
queue := populateQueue(t, now)
// Make sure objects are removed from the queue in order.
for prev := now; queue.Len() > 0; {
_, tm := queue.PopFirst()
if tm.Sub(prev) != time.Hour {
t.Errorf("Invalid queue ordering.\n"+
" Got: %v\n"+
"Expected: %v\n", tm, prev.Add(time.Hour))
}
prev = tm
}
}
}
func TestAdvance(t *testing.T) {
for iter := 0; iter < 100; iter++ {
now := time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)
queue := populateQueue(t, now)
executed = 0
count := queue.Len()
lastTime := now.Add(time.Duration(count) * time.Hour)
for adv := 0; adv < 5; adv++ {
queue.Advance(lastTime)
if executed != count {
t.Errorf("Advance failed.\n"+
"Should have executed %d times.\n"+
"Only executed %d times.\n", count, executed)
}
}
}
}

View File

@@ -0,0 +1,122 @@
package util
import (
"strconv"
"strings"
"time"
"go-common/app/admin/main/up/util/timerqueue"
"go-common/library/log"
"go-common/library/net/http/blademaster"
xtime "go-common/library/time"
)
var (
//GlobalTimer timer queue
GlobalTimer = timerqueue.New()
)
//ParseCommonTime parse to library common time
func ParseCommonTime(layout string, value string) (t xtime.Time, err error) {
date, e := time.ParseInLocation(layout, value, time.Local)
err = e
if err == nil {
t = xtime.Time(date.Unix())
}
return
}
//Unique unique the slice
func Unique(intSlice []int64) []int64 {
keys := make(map[int64]bool)
var list []int64
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
//GetContextValueInt64 get context int64
func GetContextValueInt64(c *blademaster.Context, key string) (v int64, ok bool) {
var vtemp, o = c.Get(key)
ok = o
if ok {
v, _ = vtemp.(int64)
}
return
}
//GetContextValueString get context string
func GetContextValueString(c *blademaster.Context, key string) (v string, ok bool) {
var vtemp, o = c.Get(key)
ok = o
if ok {
v, _ = vtemp.(string)
}
return
}
const (
trimSet = "\r\n "
)
//ExplodeInt64 explode string to slice
func ExplodeInt64(str string, seperator string) (result []int64) {
var strMids = strings.Split(str, seperator)
for _, v := range strMids {
mid, e := strconv.ParseInt(strings.Trim(v, trimSet), 10, 64)
if e != nil {
continue
}
result = append(result, mid)
}
return
}
//ExplodeUint32 explode string to slice
func ExplodeUint32(str string, seperator string) (result []uint32) {
var strMids = strings.Split(str, seperator)
for _, v := range strMids {
mid, e := strconv.ParseInt(strings.Trim(v, trimSet), 10, 64)
if e != nil {
continue
}
result = append(result, uint32(mid))
}
return
}
//GetNextPeriodTime get next period time from current time
// clock, like "03:05:00"
// period, like 24h
// currentTime, like now
// return the next alarm time for this clock
func GetNextPeriodTime(clock string, period time.Duration, currentTime time.Time) (next time.Time, err error) {
var now = currentTime
var startTime, e = time.Parse("15:04:05", clock)
err = e
if err != nil {
log.Error("clock is not right, config=%s, should like '12:00:00'")
startTime = time.Date(2000, 1, 1, 3, 0, 0, 0, now.Location())
}
next = time.Date(now.Year(), now.Month(), now.Day(), startTime.Hour(), startTime.Minute(), startTime.Second(), 0, now.Location())
d := next.Sub(now)
for d < 0 {
next = next.Add(period)
d = next.Sub(now)
}
for d > period {
next = next.Add(-period)
d = next.Sub(now)
}
return
}
//TruncateDate 截取到整天,舍去时分秒
func TruncateDate(tm time.Time) time.Time {
var y, m, d = tm.Date()
return time.Date(y, m, d, 0, 0, 0, 0, tm.Location())
}

View File

@@ -0,0 +1,63 @@
package util
import (
. "github.com/smartystreets/goconvey/convey"
"testing"
"time"
)
var periodTimeTest = [][]string{
// time, period, now, expected
{"11:00:00", "1h", "10:00:00", "0000-01-01 11:00:00"},
{"11:00:00", "1m", "10:00:00", "0000-01-01 10:01:00"},
{"11:00:00", "1s", "10:00:00", "0000-01-01 10:00:01"},
{"11:00:00", "8h", "10:00:00", "0000-01-01 11:00:00"},
{"11:00:00", "12h", "10:00:00", "0000-01-01 11:00:00"},
{"11:00:00", "23h", "10:00:00", "0000-01-01 11:00:00"},
{"09:00:10", "1h", "10:00:00", "0000-01-01 10:00:10"},
{"09:00:10", "1m", "10:00:00", "0000-01-01 10:00:10"},
{"09:00:10", "1s", "10:00:00", "0000-01-01 10:00:00"},
{"09:00:10", "8h", "10:00:00", "0000-01-01 17:00:10"},
{"09:00:10", "12h", "10:00:00", "0000-01-01 21:00:10"},
{"09:00:10", "4h", "10:00:00", "0000-01-01 13:00:10"},
{"09:00:10", "24h", "10:00:00", "0000-01-02 09:00:10"},
{"09:00:10", "8h", "10:00:00", "0000-01-01 17:00:10"},
}
func TestGetNextPeriodTime(t *testing.T) {
Convey("test get period", t, func() {
for i, v := range periodTimeTest {
//needTime, _ := time.Parse("15:04:05", v[0])
period, _ := time.ParseDuration(v[1])
now, _ := time.Parse("15:04:05", v[2])
expected, _ := time.Parse("2006-01-02 15:04:05", v[3])
actual, err := GetNextPeriodTime(v[0], period, now)
So(err, ShouldEqual, nil)
t.Logf("[%d]actual:+%v, expected:+%v", i, actual, expected)
So(actual.Equal(expected), ShouldEqual, true)
}
})
}
func TestUnSetBit64(t *testing.T) {
var (
testCase = [][]int64{
// attr, bit, result
{1, 0, 0},
{2, 0, 2},
{2, 1, 0},
{3, 1, 1},
{3, 64, 3},
}
)
Convey("test for unset bit 64", t, func() {
for _, v := range testCase {
So(UnSetBit64(v[0], uint(v[1])), ShouldEqual, v[2])
}
})
}