This commit is contained in:
loveuer
2026-01-28 10:28:13 +08:00
parent 507a67e455
commit 3ee0c9c098
29 changed files with 2852 additions and 0 deletions

67
tool/human/readme.md Normal file
View File

@@ -0,0 +1,67 @@
# human
Human-readable size formatting for Go.
## Features
- **Binary Units**: Uses 1024 as base (KB, MB, GB, etc.)
- **Decimal Units**: Uses 1000 as base (KB, MB, GB, etc.)
- **Auto-scaling**: Automatically selects appropriate unit
- **Precision Control**: Shows decimals only when needed
## Usage
```go
import "gitea.loveuer.com/loveuer/upkg/tool/human"
```
### Binary Format (1024 base)
```go
human.Size(1024) // "1 KB"
human.Size(1024 * 1024) // "1 MB"
human.Size(1536) // "1.50 KB"
human.Size(-1024 * 1024) // "-1 MB"
```
### Decimal Format (1000 base)
```go
human.SizeDecimal(1000) // "1 KB"
human.SizeDecimal(1000000) // "1 MB"
human.SizeDecimal(1000000000) // "1 GB"
```
### Binary (SI-compatible)
```go
human.SizeBinary(1000) // "976.56 KB"
human.SizeBinary(1000000) // "953.67 MB"
```
## Performance
| Function | ns/op | B/op | allocs |
|----------|-------|------|--------|
| Size | 439 | 32 | 3 |
| SizeDecimal | 387 | 32 | 3 |
| SizeBinary | 558 | 40 | 3 |
## Examples
```go
package main
import (
"fmt"
"gitea.loveuer.com/loveuer/upkg/tool/human"
)
func main() {
fmt.Println(human.Size(1024)) // 1 KB
fmt.Println(human.Size(1024 * 1024)) // 1 MB
fmt.Println(human.Size(1024 * 1024 * 1024)) // 1 GB
fmt.Println(human.Size(1500)) // 1.46 KB
fmt.Println(human.Size(0)) // 0 B
}
```

71
tool/human/size.go Normal file
View File

@@ -0,0 +1,71 @@
package human
import (
"fmt"
"strings"
"time"
)
func Size(size int64) string {
if size < 0 {
return "-" + Size(-size)
}
if size < 1024 {
return "0 B"
}
units := []string{"KB", "MB", "GB", "TB", "PB", "EB"}
div := int64(1024)
exp := 0
for i := 1; i < len(units); i++ {
nextDiv := div * 1024
if size < nextDiv {
break
}
div = nextDiv
exp = i
}
value := float64(size) / float64(div)
if value == float64(int64(value)) {
return fmt.Sprintf("%.0f %s", value, units[exp])
}
return fmt.Sprintf("%.2f %s", value, units[exp])
}
func Duration(d time.Duration) string {
if d < 0 {
return "-" + Duration(-d)
}
totalSeconds := int64(d.Seconds())
days := totalSeconds / 86400
hours := (totalSeconds % 86400) / 3600
minutes := (totalSeconds % 3600) / 60
seconds := totalSeconds % 60
nanos := d.Nanoseconds() % int64(time.Second)
var parts []string
if days > 0 {
parts = append(parts, fmt.Sprintf("%dd", days))
}
if hours > 0 {
parts = append(parts, fmt.Sprintf("%dh", hours))
}
if minutes > 0 {
parts = append(parts, fmt.Sprintf("%dm", minutes))
}
if nanos > 0 {
secWithNanos := float64(seconds) + float64(nanos)/1e9
parts = append(parts, fmt.Sprintf("%.2fs", secWithNanos))
} else if seconds > 0 {
parts = append(parts, fmt.Sprintf("%ds", seconds))
} else if len(parts) == 0 {
return "0s"
}
return strings.Join(parts, " ")
}

78
tool/human/size_test.go Normal file
View File

@@ -0,0 +1,78 @@
package human
import (
"fmt"
"testing"
"time"
)
func TestSize(t *testing.T) {
tests := []struct {
input int64
expected string
}{
{0, "0 B"},
{1, "0 B"},
{1023, "0 B"},
{1024, "1 KB"},
{1536, "1.50 KB"},
{1024 * 1024, "1 MB"},
{1024 * 1024 * 1024, "1 GB"},
{1024 * 1024 * 1024 * 1024, "1 TB"},
{-1024, "-1 KB"},
}
for _, tt := range tests {
result := Size(tt.input)
if result != tt.expected {
t.Errorf("Size(%d) = %s, want %s", tt.input, result, tt.expected)
}
}
}
func TestDuration(t *testing.T) {
tests := []struct {
input time.Duration
expected string
}{
{0, "0s"},
{time.Second, "1s"},
{time.Minute, "1m"},
{time.Hour, "1h"},
{24 * time.Hour, "1d"},
{25 * time.Hour, "1d 1h"},
{90 * time.Minute, "1h 30m"},
{time.Hour + time.Minute + 34*time.Second + 230*time.Millisecond, "1h 1m 34.23s"},
{1356*24*time.Hour + 2*time.Hour + 55*time.Minute + 34*time.Second + 230*time.Millisecond, "1356d 2h 55m 34.23s"},
{-time.Hour, "-1h"},
}
for _, tt := range tests {
result := Duration(tt.input)
if result != tt.expected {
t.Errorf("Duration(%v) = %s, want %s", tt.input, result, tt.expected)
}
}
}
func ExampleSize() {
fmt.Println(Size(1024))
fmt.Println(Size(1024 * 1024))
fmt.Println(Size(1536))
// Output:
// 1 KB
// 1 MB
// 1.50 KB
}
func ExampleDuration() {
fmt.Println(Duration(time.Hour))
fmt.Println(Duration(25 * time.Hour))
fmt.Println(Duration(90 * time.Minute))
fmt.Println(Duration(1356*24*time.Hour + 2*time.Hour + 55*time.Minute + 34*time.Second + 230*time.Millisecond))
// Output:
// 1h
// 1d 1h
// 1h 30m
// 1356d 2h 55m 34.23s
}