GHC 2020-06-15

4 comments.

, https://git.io/Jf7gD in lestrrat-go/strftime
Thanks for the quick review.

> The test tests against %L -- which is fine, because you're using 'L' as the symbol, but maybe it might throw the reader off why you're not using %f?

That was a copy-paste oversight. Fixed.

> "List of available extensions" in the README :)

Added. I also added links to autogenerated docs on pkg.go.dev, no sure if that's excessive for you.

, https://git.io/Jf7gy in lestrrat-go/strftime
Okay, I wrote some benchmarks

```diff
diff --git a/extension.go b/extension.go
index 77f947b..f1434a3 100644
--- a/extension.go
+++ b/extension.go
@@ -13,6 +13,7 @@ import (
 // milliseconds function
 var milliseconds Appender
 var microseconds Appender
+var microsecondsAltImpl Appender
 var unixseconds Appender
 
 func init() {
@@ -36,6 +37,25 @@ func init() {
 			return append(b, strconv.Itoa(microsecond)...)
 		}
 	})
+	microsecondsAltImpl = AppendFunc(func(b []byte, t time.Time) []byte {
+		microsecond := int(t.Nanosecond()) / int(time.Microsecond)
+		if microsecond < 100000 {
+			b = append(b, '0')
+		}
+		if microsecond < 10000 {
+			b = append(b, '0')
+		}
+		if microsecond < 1000 {
+			b = append(b, '0')
+		}
+		if microsecond < 100 {
+			b = append(b, '0')
+		}
+		if microsecond < 10 {
+			b = append(b, '0')
+		}
+		return append(b, strconv.Itoa(microsecond)...)
+	})
 	unixseconds = AppendFunc(func(b []byte, t time.Time) []byte {
 		return append(b, strconv.FormatInt(t.Unix(), 10)...)
 	})
@@ -53,6 +73,10 @@ func Microseconds() Appender {
 	return microseconds
 }
 
+func MicrosecondsAltImpl() Appender {
+	return microsecondsAltImpl
+}
+
 // UnixSeconds returns the Appender suitable for creating
 // unix timestamp textual representation.
 func UnixSeconds() Appender {
diff --git a/options.go b/options.go
index 8ef9d95..1dca0e3 100644
--- a/options.go
+++ b/options.go
@@ -58,6 +58,10 @@ func WithMicroseconds(b byte) Option {
 	return WithSpecification(b, Microseconds())
 }
 
+func WithMicrosecondsAltImpl(b byte) Option {
+	return WithSpecification(b, MicrosecondsAltImpl())
+}
+
 // WithUnixSeconds is similar to WithSpecification, and specifies that
 // the Strftime object should interpret the pattern `%b` (where b
 // is the byte that you specify as the argument)
diff --git a/strftime_test.go b/strftime_test.go
index 30775dc..cf3424a 100644
--- a/strftime_test.go
+++ b/strftime_test.go
@@ -259,3 +259,17 @@ func TestGHIssue9(t *testing.T) {
 		return
 	}
 }
+
+func BenchmarkMicrosecond(b *testing.B) {
+	p, _ := strftime.New(`%f`, strftime.WithMicroseconds('f'))
+	for n := 0; n < b.N; n++ {
+		p.FormatString(time.Now())
+	}
+}
+
+func BenchmarkMicrosecondAltImpl(b *testing.B) {
+	p, _ := strftime.New(`%f`, strftime.WithMicrosecondsAltImpl('f'))
+	for n := 0; n < b.N; n++ {
+		p.FormatString(time.Now())
+	}
+}
```

On my Mac:

```command
$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/lestrrat-go/strftime
BenchmarkMicrosecond-12           	 6844014	       177 ns/op
BenchmarkMicrosecondAltImpl-12    	 8407060	       142 ns/op
PASS
ok  	github.com/lestrrat-go/strftime	2.737s
```

On my Linux box:

```command
$ goos: linux
goarch: amd64
pkg: github.com/lestrrat-go/strftime
BenchmarkMicrosecond-2          	 8807217	       137 ns/op
BenchmarkMicrosecondAltImpl-2   	10977770	       115 ns/op
PASS
ok  	github.com/lestrrat-go/strftime	2.725s
```

So apparently using simple branches is still faster than floating point math (replacing the `bytes.Repeat` with a straightforward loop helps a little but is still slower). I'll be switching over.

, https://git.io/Jf7nn in lestrrat-go/strftime
This could also be implemented as a bunch of simple ifs like `milliseconds`. Not sure which style you prefer.

, https://git.io/Jf7nc in lestrrat-go/strftime
Implement a microsecond extension
=================================

The microsecond extension is available in e.g. Python's `strftime` as [`%f`](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes), and it is useful for e.g. profilers.