Files
wehub-resource-sync bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:54 +08:00

174 lines
4.3 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package common
import (
"fmt"
"io"
"strings"
"time"
"github.com/larksuite/cli/internal/output"
"github.com/larksuite/cli/internal/util"
)
func FormatSize(bytes int64) string {
if bytes < 1024 {
return fmt.Sprintf("%d B", bytes)
}
if bytes < 1024*1024 {
return fmt.Sprintf("%.1f KB", float64(bytes)/1024)
}
if bytes < 1024*1024*1024 {
return fmt.Sprintf("%.1f MB", float64(bytes)/1024/1024)
}
return fmt.Sprintf("%.1f GB", float64(bytes)/1024/1024/1024)
}
func MaskToken(token string) string {
if len(token) < 2 {
return "***"
}
if len(token) <= 8 {
return token[:2] + "***"
}
return token[:4] + "..." + token[len(token)-4:]
}
// ParseTime converts time expressions to Unix seconds string.
//
// Optional hint: "end" makes day-granularity inputs snap to 23:59:59 instead of 00:00:00.
//
// ParseTime("2026-01-01") → 2026-01-01 00:00:00
// ParseTime("2026-01-01", "end") → 2026-01-01 23:59:59
//
// Supported formats: ISO 8601 (with or without time/timezone), date-only, Unix timestamp.
func ParseTime(input string, hint ...string) (string, error) {
input = strings.TrimSpace(input)
isEnd := len(hint) > 0 && hint[0] == "end"
// snapDay aligns to start-of-day or end-of-day based on hint.
snapDay := func(t time.Time) time.Time {
if isEnd {
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location())
}
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
// ISO 8601 with timezone (precise)
tzFormats := []string{
time.RFC3339,
"2006-01-02T15:04Z07:00",
"2006-01-02T15:04:05Z07:00",
}
for _, f := range tzFormats {
if t, err := time.Parse(f, input); err == nil {
return fmt.Sprintf("%d", t.Unix()), nil
}
}
// ISO 8601 without timezone — with time component (precise)
preciseFormats := []string{
"2006-01-02T15:04:05",
"2006-01-02 15:04:05",
"2006-01-02T15:04",
"2006-01-02 15:04",
}
for _, f := range preciseFormats {
if t, err := time.ParseInLocation(f, input, time.Local); err == nil {
return fmt.Sprintf("%d", t.Unix()), nil
}
}
// Date-only (day-granularity)
if t, err := time.ParseInLocation("2006-01-02", input, time.Local); err == nil {
return fmt.Sprintf("%d", snapDay(t).Unix()), nil
}
// Unix timestamp (precise, passed through as-is) — must be purely numeric
var ts int64
if n, err := fmt.Sscanf(input, "%d", &ts); err == nil && n == 1 && ts > 0 && fmt.Sprintf("%d", ts) == input {
return input, nil
}
return "", fmt.Errorf("cannot parse time %q (supported: ISO 8601 e.g. 2026-01-01 / 2026-01-01T15:04:05+08:00, Unix timestamp)", input)
}
// FormatTimeWithSeconds converts Unix seconds/ms string to local time string with seconds precision.
func FormatTimeWithSeconds(ts interface{}) string {
if ts == nil {
return ""
}
s := fmt.Sprintf("%v", ts)
if s == "" {
return ""
}
var n int64
fmt.Sscanf(s, "%d", &n)
if n == 0 {
return s
}
if n > 1e12 {
n = n / 1000
}
t := time.Unix(n, 0)
return t.Local().Format("2006-01-02 15:04:05")
}
// FormatTime converts Unix seconds/ms string to local time string.
func FormatTime(ts interface{}) string {
if ts == nil {
return ""
}
s := fmt.Sprintf("%v", ts)
if s == "" {
return ""
}
var n int64
fmt.Sscanf(s, "%d", &n)
if n == 0 {
return s
}
// Detect ms vs seconds
if n > 1e12 {
n = n / 1000
}
t := time.Unix(n, 0)
return t.Local().Format("2006-01-02 15:04")
}
// SplitCSV 解析逗号分隔的列表,忽略空项并去除空格
func SplitCSV(input string) []string {
if input == "" {
return nil
}
parts := strings.Split(input, ",")
var result []string
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
result = append(result, p)
}
}
return result
}
// CheckApiError checks if API result is an error and prints it to w.
func CheckApiError(w io.Writer, result interface{}, action string) bool {
if resultMap, ok := result.(map[string]interface{}); ok {
code, _ := util.ToFloat64(resultMap["code"])
if code != 0 {
msg, _ := resultMap["msg"].(string)
output.PrintError(w, fmt.Sprintf("%s: [%.0f] %s", action, code, msg))
return true
}
}
return false
}
// TruncateStr truncates s to at most n runes.
func TruncateStr(s string, n int) string {
r := []rune(s)
if len(r) <= n {
return s
}
return string(r[:n])
}