限制格式化持续时间中的有效数字
Limiting significant digits in formatted durations
我正在安排一些不可预测的时间 I/O。此代码
started := time.Now()
time.Sleep(123456789 * time.Nanosecond) // unpredictable process
fmt.Printf("%v", time.Since(started))
生产
123.456789ms
我喜欢自动选择和打印单位刻度(ms、μs、ns等),因为我事先不知道定时操作需要微秒、毫秒还是秒才能完成。
我不喜欢这种精确度 - 我宁愿只报告两位或三位有效数字。是否有 简单 方法来限制格式化指令 %v
或类似指令的精度?
%v
正在使用 Duration.String(),因此您要么必须编写自定义格式函数,例如:
func fmtTime(in time.Duration, prec int) string {
s := in.String()
ix := strings.IndexRune(s, '.')
if ix == -1 {
return s
}
unit:=len(s)
for i,x:=range s[:ix+1] {
if !unicode.IsDigit(x) {
unit=i+ix+1
break
}
}
if prec == 0 {
return s[:ix]+s[unit:]
}
if prec>len(s)-ix-(len(s)-unit)-1 {
prec=len(s)-ix-(len(s)-unit)-1
}
return s[:ix+prec+1]+s[unit:]
}
func main() {
...
fmt.Printf("%v\n", fmtTime(time.Since(started), 3))
}
或者你可以用格式化程序定义一个新类型,然后使用新类型打印:
type FmtDuration time.Duration
func (d FmtDuration) Format(f fmt.State, c rune) {
prec,_ := f.Precision()
f.Write([]byte(fmtTime(time.Duration(d), prec)))
}
func main() {
fmt.Printf("%.2v", FmtDuration(time.Since(started)))
}
前言: 我在 github.com/icza/gox
, see timex.Round()
.
发布了这个实用程序
我认为没有简单的方法,因为当使用默认格式(例如%v
)打印时,会调用Duration.String()
来生成字符串表示。它 returns 是一个 string
值,因此小数位数等格式化选项不再适用。
控制结果小数位数的一种方法是在打印前截断或舍入持续时间,使用 Duration.Truncate()
or Duration.Round()
。
当然,持续时间应截断或四舍五入的单位取决于持续时间的值,但逻辑并不难:
var divs = []time.Duration{
time.Duration(1), time.Duration(10), time.Duration(100), time.Duration(1000)}
func round(d time.Duration, digits int) time.Duration {
switch {
case d > time.Second:
d = d.Round(time.Second / divs[digits])
case d > time.Millisecond:
d = d.Round(time.Millisecond / divs[digits])
case d > time.Microsecond:
d = d.Round(time.Microsecond / divs[digits])
}
return d
}
让我们用不同的持续时间来测试它:
ds := []time.Duration{
time.Hour + time.Second + 123*time.Millisecond, // 1h0m1.123s
time.Hour + time.Second + time.Microsecond, // 1h0m1.000001s
123456789 * time.Nanosecond, // 123.456789ms
123456 * time.Nanosecond, // 123.456µs
123 * time.Nanosecond, // 123ns
}
for _, d := range ds {
fmt.Printf("%-15v", d)
for digits := 0; digits <= 3; digits++ {
fmt.Printf("%-15v", round(d, digits))
}
fmt.Println()
}
输出将是(在 Go Playground 上尝试):
duration 0 digits 1 digit 2 digits 3 digits
-----------------------------------------------------------------------
1h0m1.123s 1h0m1s 1h0m1.1s 1h0m1.12s 1h0m1.123s
1h0m1.000001s 1h0m1s 1h0m1s 1h0m1s 1h0m1s
123.456789ms 123ms 123.5ms 123.46ms 123.457ms
123.456µs 123µs 123.5µs 123.46µs 123.456µs
123ns 123ns 123ns 123ns 123ns
如果您只关心有效的 3 位数字:
// FormatDuration formats a duration with a precision of 3 digits
// if it is less than 100s.
func FormatDuration(d time.Duration) string {
scale := 100 * time.Second
// look for the max scale that is smaller than d
for scale > d {
scale = scale / 10
}
return d.Round(scale / 100).String()
}
func Test_FormatDuration(t *testing.T) {
for i := 0; i < 15; i++ {
dur := time.Duration(3.455555 * math.Pow(10, float64(i)))
t.Logf("%2d %12v %6s", i, dur, FormatDuration(dur))
}
}
// original formatted
// 0 3ns 3ns
// 1 34ns 34ns
// 2 345ns 345ns
// 3 3.455µs 3.46µs
// 4 34.555µs 34.6µs
// 5 345.555µs 346µs
// 6 3.455555ms 3.46ms
// 7 34.55555ms 34.6ms
// 8 345.5555ms 346ms
// 9 3.455555s 3.46s
// 10 34.55555s 34.6s
// 11 5m45.5555s 5m46s
// 12 57m35.555s 57m36s
// 13 9h35m55.55s 9h35m56s
// 14 95h59m15.5s 95h59m16s
我正在安排一些不可预测的时间 I/O。此代码
started := time.Now()
time.Sleep(123456789 * time.Nanosecond) // unpredictable process
fmt.Printf("%v", time.Since(started))
生产
123.456789ms
我喜欢自动选择和打印单位刻度(ms、μs、ns等),因为我事先不知道定时操作需要微秒、毫秒还是秒才能完成。
我不喜欢这种精确度 - 我宁愿只报告两位或三位有效数字。是否有 简单 方法来限制格式化指令 %v
或类似指令的精度?
%v
正在使用 Duration.String(),因此您要么必须编写自定义格式函数,例如:
func fmtTime(in time.Duration, prec int) string {
s := in.String()
ix := strings.IndexRune(s, '.')
if ix == -1 {
return s
}
unit:=len(s)
for i,x:=range s[:ix+1] {
if !unicode.IsDigit(x) {
unit=i+ix+1
break
}
}
if prec == 0 {
return s[:ix]+s[unit:]
}
if prec>len(s)-ix-(len(s)-unit)-1 {
prec=len(s)-ix-(len(s)-unit)-1
}
return s[:ix+prec+1]+s[unit:]
}
func main() {
...
fmt.Printf("%v\n", fmtTime(time.Since(started), 3))
}
或者你可以用格式化程序定义一个新类型,然后使用新类型打印:
type FmtDuration time.Duration
func (d FmtDuration) Format(f fmt.State, c rune) {
prec,_ := f.Precision()
f.Write([]byte(fmtTime(time.Duration(d), prec)))
}
func main() {
fmt.Printf("%.2v", FmtDuration(time.Since(started)))
}
前言: 我在 github.com/icza/gox
, see timex.Round()
.
我认为没有简单的方法,因为当使用默认格式(例如%v
)打印时,会调用Duration.String()
来生成字符串表示。它 returns 是一个 string
值,因此小数位数等格式化选项不再适用。
控制结果小数位数的一种方法是在打印前截断或舍入持续时间,使用 Duration.Truncate()
or Duration.Round()
。
当然,持续时间应截断或四舍五入的单位取决于持续时间的值,但逻辑并不难:
var divs = []time.Duration{
time.Duration(1), time.Duration(10), time.Duration(100), time.Duration(1000)}
func round(d time.Duration, digits int) time.Duration {
switch {
case d > time.Second:
d = d.Round(time.Second / divs[digits])
case d > time.Millisecond:
d = d.Round(time.Millisecond / divs[digits])
case d > time.Microsecond:
d = d.Round(time.Microsecond / divs[digits])
}
return d
}
让我们用不同的持续时间来测试它:
ds := []time.Duration{
time.Hour + time.Second + 123*time.Millisecond, // 1h0m1.123s
time.Hour + time.Second + time.Microsecond, // 1h0m1.000001s
123456789 * time.Nanosecond, // 123.456789ms
123456 * time.Nanosecond, // 123.456µs
123 * time.Nanosecond, // 123ns
}
for _, d := range ds {
fmt.Printf("%-15v", d)
for digits := 0; digits <= 3; digits++ {
fmt.Printf("%-15v", round(d, digits))
}
fmt.Println()
}
输出将是(在 Go Playground 上尝试):
duration 0 digits 1 digit 2 digits 3 digits
-----------------------------------------------------------------------
1h0m1.123s 1h0m1s 1h0m1.1s 1h0m1.12s 1h0m1.123s
1h0m1.000001s 1h0m1s 1h0m1s 1h0m1s 1h0m1s
123.456789ms 123ms 123.5ms 123.46ms 123.457ms
123.456µs 123µs 123.5µs 123.46µs 123.456µs
123ns 123ns 123ns 123ns 123ns
如果您只关心有效的 3 位数字:
// FormatDuration formats a duration with a precision of 3 digits
// if it is less than 100s.
func FormatDuration(d time.Duration) string {
scale := 100 * time.Second
// look for the max scale that is smaller than d
for scale > d {
scale = scale / 10
}
return d.Round(scale / 100).String()
}
func Test_FormatDuration(t *testing.T) {
for i := 0; i < 15; i++ {
dur := time.Duration(3.455555 * math.Pow(10, float64(i)))
t.Logf("%2d %12v %6s", i, dur, FormatDuration(dur))
}
}
// original formatted
// 0 3ns 3ns
// 1 34ns 34ns
// 2 345ns 345ns
// 3 3.455µs 3.46µs
// 4 34.555µs 34.6µs
// 5 345.555µs 346µs
// 6 3.455555ms 3.46ms
// 7 34.55555ms 34.6ms
// 8 345.5555ms 346ms
// 9 3.455555s 3.46s
// 10 34.55555s 34.6s
// 11 5m45.5555s 5m46s
// 12 57m35.555s 57m36s
// 13 9h35m55.55s 9h35m56s
// 14 95h59m15.5s 95h59m16s