节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
2.32 KiB
Stardust
package protocol

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// TimeLayout 是协议使用的 UTC 时间格式,对应 .NET 的 yyyy-MM-dd HH:mm:ss。
const TimeLayout = "2006-01-02 15:04:05"

// ZeroTimeText 是未设置时间时的协议文本。
const ZeroTimeText = "0001-01-01 00:00:00"

var parseLayouts = []string{
	TimeLayout,
	"2006-01-02 15:04:05.999999999",
	"2006-01-02T15:04:05Z07:00",
	"2006-01-02T15:04:05",
	"2006-01-02T15:04:05.999999999",
	time.RFC3339Nano,
}

// Time 包装 time.Time,按协议要求使用 UTC 的 yyyy-MM-dd HH:mm:ss 文本。
type Time struct {
	time.Time
}

// NewTime 创建协议时间。
func NewTime(t time.Time) Time {
	if t.IsZero() {
		return Time{}
	}

	return Time{Time: t.UTC()}
}

// TimeFromUnixMilli 从 Unix 毫秒创建协议时间。
func TimeFromUnixMilli(ms int64) Time {
	if ms == 0 {
		return Time{}
	}

	return NewTime(time.UnixMilli(ms))
}

// UnixMilli 返回 Unix 毫秒,零值返回 0。
func (t Time) UnixMilli() int64 {
	if t.IsZero() {
		return 0
	}

	return t.Time.UnixMilli()
}

// IsSet 判断时间是否已设置。协议用 0001-01-01 表示未设置。
func (t Time) IsSet() bool {
	return !t.IsZero() && t.Year() > 2000
}

// String 返回协议时间文本。
func (t Time) String() string {
	if t.IsZero() {
		return ZeroTimeText
	}

	return t.UTC().Format(TimeLayout)
}

// MarshalJSON 序列化为 yyyy-MM-dd HH:mm:ss。
func (t Time) MarshalJSON() ([]byte, error) {
	return json.Marshal(t.String())
}

// UnmarshalJSON 解析协议时间,兼容 null、空字符串与 RFC3339。
func (t *Time) UnmarshalJSON(data []byte) error {
	if len(data) == 0 || string(data) == "null" {
		t.Time = time.Time{}
		return nil
	}

	var text string
	if err := json.Unmarshal(data, &text); err != nil {
		return fmt.Errorf("parse protocol time: %w", err)
	}

	parsed, err := ParseTime(text)
	if err != nil {
		return err
	}

	t.Time = parsed
	return nil
}

// ParseTime 解析协议时间文本。
func ParseTime(text string) (time.Time, error) {
	text = strings.TrimSpace(text)
	if text == "" || text == ZeroTimeText || strings.HasPrefix(text, "0001-01-01T00:00:00") {
		return time.Time{}, nil
	}

	for _, layout := range parseLayouts {
		if t, err := time.Parse(layout, text); err == nil {
			return t.UTC(), nil
		}
	}

	return time.Time{}, fmt.Errorf("unsupported time format: %q", text)
}