提交 183e061b authored 作者: mooncake9527's avatar mooncake9527

update

上级 62016f9a
......@@ -175,15 +175,15 @@ func Logging(opts ...Option) gin.HandlerFunc {
reqID := ""
if o.requestIDFrom == 1 {
if v, isExist := c.Get(ctxUtil.ContextRequestIDKey); isExist {
if v, isExist := c.Get(ctxUtil.ContextTraceIDKey); isExist {
if requestID, ok := v.(string); ok {
reqID = requestID
fields = append(fields, zap.String(ctxUtil.ContextRequestIDKey, reqID))
fields = append(fields, zap.String(ctxUtil.ContextTraceIDKey, reqID))
}
}
} else if o.requestIDFrom == 2 {
reqID = c.Request.Header.Get(ctxUtil.HeaderXRequestIDKey)
fields = append(fields, zap.String(ctxUtil.ContextRequestIDKey, reqID))
fields = append(fields, zap.String(ctxUtil.ContextTraceIDKey, reqID))
}
o.log.Info("<<<<<<<<<req", fields...)
......@@ -211,7 +211,7 @@ func Logging(opts ...Option) gin.HandlerFunc {
zap.ByteString("body", getResponseBody(newWriter.body, o.maxLength)),
}
if reqID != "" {
fields = append(fields, zap.String(ctxUtil.ContextRequestIDKey, reqID))
fields = append(fields, zap.String(ctxUtil.ContextTraceIDKey, reqID))
}
o.log.Info(">>>>>>>>>rsp", fields...)
}
......@@ -233,7 +233,7 @@ func SimpleLog(opts ...Option) gin.HandlerFunc {
reqID := ""
if o.requestIDFrom == 1 {
if v, isExist := c.Get(ctxUtil.ContextRequestIDKey); isExist {
if v, isExist := c.Get(ctxUtil.ContextTraceIDKey); isExist {
if requestID, ok := v.(string); ok {
reqID = requestID
}
......@@ -254,7 +254,7 @@ func SimpleLog(opts ...Option) gin.HandlerFunc {
zap.Int("size", c.Writer.Size()),
}
if reqID != "" {
fields = append(fields, zap.String(ctxUtil.ContextRequestIDKey, reqID))
fields = append(fields, zap.String(ctxUtil.ContextTraceIDKey, reqID))
}
o.log.Info("Gin msg", fields...)
}
......
......@@ -44,7 +44,7 @@ func runLogHTTPServer() string {
//))
helloFun := func(c *gin.Context) {
logger.Info("test request id", ctxUtil.GCtxRequestIDField(c))
logger.Info("test request id", ctxUtil.GinTraceIDField(c))
response.Success(c, "hello world")
}
......
package middleware
import (
ctxUtil "gitlab.wanzhuangkj.com/tush/xpkg/gin/xctx"
"net/http"
ctxUtil "gitlab.wanzhuangkj.com/tush/xpkg/gin/xctx"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
......@@ -16,7 +17,7 @@ func CustomRecoveryWithLogger(logger *zap.Logger) gin.HandlerFunc {
zap.String("client_ip", c.ClientIP()),
zap.Any("error", err),
zap.Stack("stack"),
ctxUtil.GCtxRequestIDField(c),
ctxUtil.GinTraceIDField(c),
)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"code": 500,
......
......@@ -2,9 +2,10 @@ package middleware
import (
"context"
ctxUtil "gitlab.wanzhuangkj.com/tush/xpkg/gin/xctx"
"net/http"
ctxUtil "gitlab.wanzhuangkj.com/tush/xpkg/gin/xctx"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
......@@ -19,7 +20,7 @@ type requestIDOptions struct {
func defaultRequestIDOptions() *requestIDOptions {
return &requestIDOptions{
contextRequestIDKey: ctxUtil.ContextRequestIDKey,
contextRequestIDKey: ctxUtil.ContextTraceIDKey,
headerXRequestIDKey: ctxUtil.HeaderXRequestIDKey,
}
}
......@@ -31,8 +32,8 @@ func (o *requestIDOptions) apply(opts ...RequestIDOption) {
}
func (o *requestIDOptions) setRequestIDKey() {
if o.contextRequestIDKey != ctxUtil.ContextRequestIDKey {
ctxUtil.ContextRequestIDKey = o.contextRequestIDKey
if o.contextRequestIDKey != ctxUtil.ContextTraceIDKey {
ctxUtil.ContextTraceIDKey = o.contextRequestIDKey
}
if o.headerXRequestIDKey != ctxUtil.HeaderXRequestIDKey {
ctxUtil.HeaderXRequestIDKey = o.headerXRequestIDKey
......@@ -63,7 +64,7 @@ func WithHeaderRequestIDKey(key string) RequestIDOption {
type CtxKeyString string
// RequestIDKey request_id
var RequestIDKey = CtxKeyString(ctxUtil.ContextRequestIDKey)
var RequestIDKey = CtxKeyString(ctxUtil.ContextTraceIDKey)
// -------------------------------------------------------------------------------------------
......@@ -85,7 +86,7 @@ func RequestID(opts ...RequestIDOption) gin.HandlerFunc {
}
// Expose it for use in the application
c.Set(ctxUtil.ContextRequestIDKey, requestID)
c.Set(ctxUtil.ContextTraceIDKey, requestID)
// Set X-Request-Id header
c.Writer.Header().Set(ctxUtil.HeaderXRequestIDKey, requestID)
......@@ -111,7 +112,7 @@ var RequestHeaderKey = "request_header_key"
// WrapCtx wrap context, put the Keys and Header of gin.Context into context
func WrapCtx(c *gin.Context) context.Context {
ctx := context.WithValue(c.Request.Context(), ctxUtil.ContextRequestIDKey, c.GetString(ctxUtil.ContextRequestIDKey)) //nolint
ctx := context.WithValue(c.Request.Context(), ctxUtil.ContextTraceIDKey, c.GetString(ctxUtil.ContextTraceIDKey)) //nolint
for k, v := range c.Keys {
ctx = context.WithValue(ctx, k, v) //nolint
}
......
......@@ -58,7 +58,7 @@ func TestFieldRequestIDFromContext(t *testing.T) {
c.Set("foo", "bar")
ctx := WrapCtx(c)
t.Log(ctx.Value(ctxUtil.ContextRequestIDKey))
t.Log(ctx.Value(ctxUtil.ContextTraceIDKey))
t.Log(GetFromCtx(ctx, "foo"))
t.Log(ctxUtil.CtxTraceIDField(ctx))
t.Log(GetFromCtx(ctx, "not-exist"))
......@@ -71,7 +71,7 @@ func TestFieldRequestIDFromContext(t *testing.T) {
cctx := c
c2, ctx2 := AdaptCtx(cctx)
t.Log(ctxUtil.CtxRequestID(c2))
t.Log(ctx2.Value(ctxUtil.ContextRequestIDKey))
t.Log(ctx2.Value(ctxUtil.ContextTraceIDKey))
})
_, err := http.Get(requestAddr + "/ping")
......@@ -106,8 +106,8 @@ func TestRequestIDKeyOptions(t *testing.T) {
o.apply(opts...)
o.setRequestIDKey()
t.Log(ctxUtil.ContextRequestIDKey, ctxUtil.HeaderXRequestIDKey)
t.Log(ctxUtil.ContextTraceIDKey, ctxUtil.HeaderXRequestIDKey)
assert.Equal(t, "my_req_id", ctxUtil.ContextRequestIDKey)
assert.Equal(t, "my_req_id", ctxUtil.ContextTraceIDKey)
assert.Equal(t, "My-X-Req-Id", ctxUtil.HeaderXRequestIDKey)
}
......@@ -178,10 +178,10 @@ func Error(c *gin.Context, err error) {
msg = err.Error()
if xerr, ok := err.(*xerror.Error); ok {
if !errcode.IsSysDefinedError(xerr.Code()) {
logger.Error("api failed.", logger.Err(err), ctxUtil.GCtxRequestIDField(c))
logger.Error("api failed.", logger.Err(err), ctxUtil.GinTraceIDField(c))
}
} else {
logger.Error("api failed.", logger.Err(err), ctxUtil.GCtxRequestIDField(c))
logger.Error("api failed.", logger.Err(err), ctxUtil.GinTraceIDField(c))
}
}
// 使用respJSONWith200函数返回错误信息。
......
......@@ -2,6 +2,7 @@ package xctx
import (
"context"
"errors"
xsf "gitlab.wanzhuangkj.com/tush/xpkg/utils/xsf"
......@@ -13,26 +14,31 @@ import (
var (
GinContextKey = "gin_context"
// ContextRequestIDKey request id for context
ContextRequestIDKey = "traceID"
KeyConfigFile = "config_file"
KeyConfigCenter = "config_center"
ContextTraceIDKey = "traceID"
KeyConfigFile = "config_file"
KeyConfigCenter = "config_center"
// HeaderXRequestIDKey header request id key
HeaderXRequestIDKey = "X-Request-ID"
KeyTid = "tid"
KeyIP = "ip"
KeyUID = "uid"
KeyShopID = "shopID"
KeyUName = "uname"
KeyToken = "token"
KeyUser = "user"
KeyTid = "tid"
KeyIP = "IP"
KeyUID = "userId"
KeyUType = "userType"
KeyCompanyID = "companyId"
KeyShopID = "shopID"
KeyUName = "uname"
KeyToken = "token"
KeyUser = "user"
)
// GCtxRequestID get request id from gin.Context
func GCtxRequestID(c *gin.Context) string {
if v, isExist := c.Get(ContextRequestIDKey); isExist {
var (
ErrorGinContextNotFound = errors.New("gin context not found")
)
// GetGinCtxTraceID get request id from gin.Context
func GetGinCtxTraceID(c *gin.Context) string {
if v, isExist := c.Get(ContextTraceIDKey); isExist {
if requestID, ok := v.(string); ok {
return requestID
}
......@@ -40,14 +46,14 @@ func GCtxRequestID(c *gin.Context) string {
return ""
}
// GCtxRequestIDField get request id field from gin.Context
func GCtxRequestIDField(c *gin.Context) zap.Field {
return zap.String(ContextRequestIDKey, GCtxRequestID(c))
// GinTraceIDField get request id field from gin.Context
func GinTraceIDField(c *gin.Context) zap.Field {
return zap.String(ContextTraceIDKey, GetGinCtxTraceID(c))
}
// CtxRequestID get request id from context.Context
func CtxRequestID(c context.Context) string {
v := c.Value(ContextRequestIDKey)
v := c.Value(ContextTraceIDKey)
if str, ok := v.(string); ok {
return str
}
......@@ -72,7 +78,7 @@ func CtxGetGinCtx(c context.Context) *gin.Context {
// CtxTraceIDField get request id field from context.Context
func CtxTraceIDField(c context.Context) zap.Field {
return zap.String(ContextRequestIDKey, CtxRequestID(c))
return zap.String(ContextTraceIDKey, CtxRequestID(c))
}
func GetGinUserID(c *gin.Context) xsf.ID {
......@@ -162,10 +168,48 @@ func GetIP(ctx context.Context) string {
}
func WrapCtx(c *gin.Context) context.Context {
ctx := context.WithValue(c.Request.Context(), ContextRequestIDKey, c.GetString(ContextRequestIDKey)) //nolint
ctx := context.WithValue(c.Request.Context(), ContextTraceIDKey, c.GetString(ContextTraceIDKey)) //nolint
for k, v := range c.Keys {
ctx = context.WithValue(ctx, k, v) //nolint
}
ctx = context.WithValue(ctx, GinContextKey, c) //nolint
return ctx
}
func ToGinCtx(ctx context.Context) (*gin.Context, error) {
if c, ok := ctx.Value(GinContextKey).(*gin.Context); ok {
return c, nil
}
return nil, ErrorGinContextNotFound
}
func GetCtxUID(c context.Context) uint {
uid := c.Value(KeyUID)
if uid != nil {
if v, ok := uid.(string); ok {
return cast.ToUint(v)
}
}
return 0
}
func GetCtxUType(c context.Context) int {
val := c.Value(KeyUType)
if val != nil {
if v, ok := val.(string); ok {
return cast.ToInt(v)
}
}
return 0
}
func GetCtxCompanyID(c context.Context) uint {
val := c.Value(KeyCompanyID)
if val != nil {
v, ok := val.(string)
if ok {
return cast.ToUint(v)
}
}
return 0
}
......@@ -13,9 +13,9 @@ import (
"strings"
"time"
ctxUtil "gitlab.wanzhuangkj.com/tush/xpkg/gin/xctx"
"gitlab.wanzhuangkj.com/tush/xpkg/logger"
"gitlab.wanzhuangkj.com/tush/xpkg/gin/xctx"
"gitlab.wanzhuangkj.com/tush/xpkg/utils/xjson"
"gitlab.wanzhuangkj.com/tush/xpkg/logger"
"gitlab.wanzhuangkj.com/tush/xpkg/xerrors/xerror"
"github.com/duke-git/lancet/v2/retry"
......@@ -250,6 +250,7 @@ func (req *Request) push(ctx context.Context) (*Response, error) {
buf = bytes.NewBufferString(req.body)
}
logger.Info("httpcli req", logger.String("body", buf.String()), xctx.CtxTraceIDField(ctx))
return req.send(ctx, buf, buf)
}
......@@ -285,6 +286,7 @@ func getRequestBody(buf *bytes.Buffer, maxLen int) []byte {
}
func (req *Request) send(ctx context.Context, body io.Reader, buf *bytes.Buffer) (*Response, error) {
logger.Info("httpcli req", logger.String("url", req.url), xctx.CtxTraceIDField(ctx))
req.request, req.err = http.NewRequest(req.method, req.url, body)
if req.err != nil {
return nil, req.err
......@@ -300,7 +302,7 @@ func (req *Request) send(ctx context.Context, body io.Reader, buf *bytes.Buffer)
}
}
req.request.Header.Add(ctxUtil.HeaderXRequestIDKey, ctxUtil.CtxRequestID(ctx))
req.request.Header.Add(xctx.HeaderXRequestIDKey, xctx.CtxRequestID(ctx))
if req.timeout < 1 {
req.timeout = defaultTimeout
......@@ -313,34 +315,28 @@ func (req *Request) send(ctx context.Context, body io.Reader, buf *bytes.Buffer)
ctx, cancel := context.WithTimeout(ctx, time.Second*20)
defer cancel()
err := retry.Retry(func() error {
logger.Info("http call req", logger.String("req", req.ReqString(ctx)), ctxUtil.CtxTraceIDField(ctx))
var response *http.Response
response, resp.err = client.Do(req.request)
if resp.err != nil {
return xerror.New(resp.err.Error())
response, err := client.Do(req.request)
if err != nil {
logger.Info("httpcli fail", logger.Any("err", err), xctx.CtxTraceIDField(ctx))
return xerror.New(err.Error())
}
resp.Status = response.Status
resp.StatusCode = response.StatusCode
for k, v := range response.Header {
resp.Header[k] = v
}
logger.Info("http call rsp", logger.Any("status", resp.Status), logger.Any("statusCode", resp.StatusCode), logger.Any("header", response.Header), ctxUtil.CtxTraceIDField(ctx))
if resp.err != nil {
logger.Info("http call", logger.Any("err", resp.err), ctxUtil.CtxTraceIDField(ctx))
return resp.err
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
return xerror.New(err.Error())
}
logger.Info("http call rsp", logger.Any("rsp body", bytes.NewBuffer(body).String()), ctxUtil.CtxTraceIDField(ctx))
logger.Info("httpcli rsp", logger.Any("body", bytes.NewBuffer(body).String()), xctx.CtxTraceIDField(ctx))
resp.Body = io.NopCloser(bytes.NewBuffer(body))
return nil
}, retry.RetryWithLinearBackoff(time.Second), retry.Context(ctx))
if err != nil {
err = xerror.New(err.Error())
logger.Error("http fail", logger.Any("err", err), ctxUtil.CtxTraceIDField(ctx))
logger.Error("httpcli fail", logger.Any("err", err), xctx.CtxTraceIDField(ctx))
}
req.response = resp
......@@ -590,5 +586,12 @@ type StdResult struct {
Data interface{} `json:"data,omitempty"`
}
// StdResult standard return data
type StdResult2 struct {
Code int `json:"code"`
Msg string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// KV string:interface{}
type KV = map[string]interface{}
......@@ -14,9 +14,9 @@ import (
"strings"
"time"
"gopkg.in/natefinch/lumberjack.v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
const (
......@@ -213,3 +213,12 @@ func (w *ZapWriter) Write(p []byte) (n int, err error) {
}
return len(p), nil
}
type ConfLog struct {
Level string
Path string
Max_size int
Max_backups int
Max_days int
Compress bool
}
......@@ -2,6 +2,8 @@ package xjson
import (
"encoding/json"
"gitlab.wanzhuangkj.com/tush/xpkg/xerrors/xerror"
)
func ToJsonString(v any) string {
......@@ -15,3 +17,11 @@ func ToJsonBytes(v any) []byte {
bytes, _ := json.Marshal(v)
return bytes
}
func Unmarshal(data []byte, v any) error {
err := json.Unmarshal(data, v)
if err != nil {
return xerror.New(err.Error())
}
return nil
}
......@@ -247,3 +247,32 @@ func CompareSlice[T IIDTable, BizSlice ~[]*T](ids []xsf.ID, bizSlice BizSlice, e
}
return nil
}
func ArrayUnique[T comparable](array []T) []T {
mp := make(map[T]struct{})
idx := 0
for _, value := range array {
if _, ok := mp[value]; ok {
continue
}
array[idx] = value
idx = idx + 1
mp[value] = struct{}{}
}
return array[:idx]
}
type IId interface {
GetID() uint
}
func GetIds[T IId](rs []*T) []uint {
if len(rs) == 0 {
return nil
}
set := xset.NewSet[uint]()
for _, r := range rs {
set.Add((*r).GetID())
}
return set.ToList()
}
......@@ -4,6 +4,17 @@ import (
"strings"
)
func CamelToUnderscore(s string) string {
var builder strings.Builder
for i, c := range s {
if i > 0 && c >= 'A' && c <= 'Z' {
builder.WriteByte('_')
}
builder.WriteRune(c)
}
return strings.ToLower(builder.String())
}
func UnderscoreToCamel(s string, isUpperFirst bool) string {
var builder strings.Builder
words := strings.Split(s, "_")
......
......@@ -163,3 +163,215 @@ func ParseExtendedDuration(s string) (time.Duration, error) {
return total, nil
}
const (
Layout_YYYY = "2006"
Layout_YYYYMM = "2006-01"
Layout_YYYYMMDD = "2006-01-02"
Layout_YYYYMMDD2 = "2006/01/02"
Layout_YYYYMMDD3 = "20060102"
Layout_YYYYMMDDHHmmSS = "2006-01-02 15:04:05"
)
var (
LocBeiJing, _ = time.LoadLocation("Asia/Shanghai")
)
func (x DateTime) Format(layout string) string {
return x.Time().Format(layout)
}
func (x DateTime) FormatYYYYMMDDHHmmSS() string {
return x.Time().Format(Layout_YYYYMMDDHHmmSS)
}
// const TimeFormat = "2006-01-02 15:04:05"
// // Value insert timestamp into mysql need this function.
// func (t DateTime) Value() (driver.Value, error) {
// var zeroTime time.Time
// if time.Time(t).UnixNano() == zeroTime.UnixNano() {
// return nil, nil
// }
// return time.Time(t), nil
// }
// // Scan value of time.Time
// func (t *DateTime) Scan(v interface{}) error {
// value, ok := v.(time.Time)
// if ok {
// *t = DateTime(value)
// return nil
// }
// return fmt.Errorf("can not convert %v to timestamp", v)
// }
func (x DateTime) Date() DateTime {
y, m, d := x.Time().Date()
trimmedTime := time.Date(y, m, d, 0, 0, 0, 0, x.Time().Location())
return NewDateTime(trimmedTime)
}
func (x DateTime) WeekStart() DateTime {
return x.WeekEnd().AddDate(0, 0, -6)
}
func (x DateTime) WeekEnd() DateTime {
weekday := int(x.Time().Weekday())
if weekday == 0 {
weekday = 7
}
return x.AddDate(0, 0, +7-weekday)
}
func (t0 DateTime) MonthStart() DateTime {
year, month, _ := t0.Time().Date()
t1 := time.Date(year, month, 1, 0, 0, 0, 0, t0.Time().Location())
return DateTime(t1)
}
func (t0 DateTime) MonthEnd() DateTime {
year, month, _ := t0.Time().Date()
t1 := time.Date(year, month+1, 0, 0, 0, 0, 0, t0.Time().Location())
return DateTime(t1)
}
func (t DateTime) AddDate(y, m, d int) DateTime {
return DateTime(t.Time().AddDate(y, m, d))
}
// WeekDay 1-7 周一到周日
func (x DateTime) WeekDay() int {
return ((int(x.Time().Weekday()) + 6) % 7) + 1
}
// 1-31 1号到31号
func (x DateTime) Day() int {
return int(x.Time().Day())
}
// 1-12 1月到12月
func (x DateTime) Month() int {
return int(x.Time().Month())
}
// 1970年到现在第几年
func (x DateTime) Year() int {
return int(x.Time().Year())
}
func (x DateTime) After(t1 DateTime) bool {
return x.Time().After(t1.Time())
}
// 转为北京时间
func (x DateTime) LocalBeiJing() DateTime {
return DateTime(x.Time().In(LocBeiJing))
}
func (x DateTime) Local() DateTime {
return DateTime(x.Time().In(time.Local))
}
func (x DateTime) Quarter() int {
return (int(x.Time().Month())-1)/3 + 1
}
func ParseYYYYMMDD(value string) DateTime {
t, err := time.Parse(Layout_YYYYMMDD, value)
if err != nil {
return DateTime{}
}
return DateTime(t)
}
func ParseYYYYMMDDHHmmSS(value string) DateTime {
t, err := time.Parse(Layout_YYYYMMDDHHmmSS, value)
if err != nil {
return DateTime{}
}
return DateTime(t)
}
func ParseTime(layout, value string) (DateTime, error) {
t, err := time.Parse(layout, value)
if err != nil {
return DateTime{}, err
}
return DateTime(t), nil
}
func IsSameDay(t1, t2 DateTime) bool {
t1Local := t1.Local()
t2Local := t2.Local()
return t1Local.Year() == t2Local.Year() &&
t1Local.Month() == t2Local.Month() &&
t1Local.Day() == t2Local.Day()
}
// 是否同一周
func IsSameWeek(t0, t1 DateTime) bool {
y1, w1 := t0.Time().Local().ISOWeek()
y2, w2 := t1.Time().Local().ISOWeek()
return y1 == y2 && w1 == w2
}
func NotSameWeek(t0, t1 DateTime) bool {
return !IsSameWeek(t0, t1)
}
// 是否同一个月
func IsSameMonth(t0, t1 DateTime) bool {
return t0.Month() == t1.Month()
}
func NotSameMonth(t0, t1 DateTime) bool {
return !IsSameMonth(t0, t1)
}
// 是否同一季度
func IsSameQuarter(t0, t1 DateTime) bool {
return t0.Quarter() == t1.Quarter()
}
func NotSameSeason(t0, t1 DateTime) bool {
return !IsSameQuarter(t0, t1)
}
// 是否同一年
func IsSameYear(t0, t1 DateTime) bool {
return t0.Time().Year() == t1.Time().Year()
}
func NotSameYear(t0, t1 DateTime) bool {
return !IsSameYear(t0, t1)
}
func DayBeforeYesterday() DateTime {
return DateTime(time.Now().AddDate(0, 0, -2))
}
func Yesterday() DateTime {
return DateTime(time.Now().AddDate(0, 0, -1))
}
func Today() DateTime {
return DateTime(time.Now())
}
func (t0 DateTime) PreviousQuarter(n int) DateTime {
month := t0.Month()
quarterStartMonth := time.Month((int(month)-1)/3*3 + 1)
currentQuarterStart := time.Date(
t0.Year(), quarterStartMonth, 1,
0, 0, 0, 0, t0.Time().Location(),
)
return DateTime(currentQuarterStart.AddDate(0, -3*n, 0))
}
func (t0 DateTime) FormatYYYYQT() string {
month := t0.Month()
quarter := (int(month)-1)/3 + 1
return fmt.Sprintf("%s_%d", t0.Format(Layout_YYYY), quarter)
}
func DaysBetween(t0, t1 DateTime) int {
t2 := time.Date(t0.Year(), t0.Time().Month(), t0.Day(), 0, 0, 0, 0, t0.Time().Location())
t3 := time.Date(t1.Year(), t1.Time().Month(), t1.Day(), 0, 0, 0, 0, t1.Time().Location())
diff := t2.Sub(t3)
days := int(diff.Hours() / 24)
return days
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论