Ограничение значащих цифр в отформатированных длительностях - PullRequest
2 голосов
/ 16 октября 2019

Я рассчитываю на непредсказуемый ввод-вывод. Этот код

started := time.Now()
time.Sleep(123456789 * time.Nanosecond) // unpredictable process    
fmt.Printf("%v", time.Since(started))

Производит

123.456789ms

Мне нравится автоматический выбор и печать единичного масштаба (мс, мкс, нс и т. Д.), Потому что я заранее не знаю, установлен ли таймероперация занимает микросекунды, миллисекунды или секунды.

Мне не нравится точность - я бы предпочел сообщать только две или три значащие цифры. Существует ли простой способ ограничения точности в директиве форматирования %v или аналогичный?

Ответы [ 2 ]

2 голосов
/ 16 октября 2019

Я не думаю, что есть простой способ, потому что при печати с использованием формата по умолчанию (например, %v), Duration.String() вызывается для создания строкового представления. Он возвращает значение string, поэтому параметры форматирования, такие как количество цифр дроби, больше не применимы.

Один из способов управления результирующими цифрами дроби - это усечь или округлить длительность перед печатью, используя Duration.Truncate() или 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       
2 голосов
/ 16 октября 2019

%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)))
}
...