Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make RFC3339Nano match "time" implementation #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,10 @@ BenchmarkRFC3339Fasttime-4 6393230 189.1 ns/op 0 B/op
| `%g` | Like %G, but without century, that is, with a 2-digit year (00-99). |
| `%h` | Equivalent to %b. |
| `%H` | The hour as a decimal number using a 24-hour clock (range 00 to 23). |
| `%i` | '.' + Microsecond as a decimal number, trailing zeros dropped. |
| `%I` | The hour as a decimal number using a 12-hour clock (range 01 to 12). |
| `%j` | The day of the year as a decimal number (range 001 to 366). |
| `%J` | '.' + Nanosecond as a decimal number, trailing zeros dropped. |
| `%k` | The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) |
| `%l` | The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) |
| `%m` | The month as a decimal number (range 01 to 12). |
Expand All @@ -161,6 +163,7 @@ BenchmarkRFC3339Fasttime-4 6393230 189.1 ns/op 0 B/op
| `%N` | Nanosecond as a decimal number, zero-padded on the left. |
| `%p` | Either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale. Noon is treated as "PM" and midnight as "AM". |
| `%P` | Like %p but in lowercase: "am" or "pm" or a corresponding string for the current locale. |
| `%q` | '.' + Millisecond as a decimal number, trailing zeros dropped. |
| `%Q` | Millisecond as a decimal number, zero-padded on the left. |
| `%r` | The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to %I:%M:%S %p. |
| `%R` | The time in 24-hour notation (%H:%M). For a version including the seconds, see %T below. |
Expand Down
6 changes: 3 additions & 3 deletions layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const (
RFC1123 = "%a, %d %b %Y %H:%M:%S %Z" // Mon, 02 Jan 2006 15:04:05 MST
RFC1123Z = "%a, %d %b %Y %H:%M:%S %z" // Mon, 02 Jan 2006 15:04:05 -0700
RFC3339 = "%Y-%m-%dT%H:%M:%S%:z" // 2006-01-02T15:04:05Z07:00
RFC3339Milli = "%Y-%m-%dT%H:%M:%S.%Q%:z" // 2006-01-02T15:04:05.000Z07:00
RFC3339Micro = "%Y-%m-%dT%H:%M:%S.%f%:z" // 2006-01-02T15:04:05.000000Z07:00
RFC3339Nano = "%Y-%m-%dT%H:%M:%S.%N%:z" // 2006-01-02T15:04:05.000000000Z07:00
RFC3339Milli = "%Y-%m-%dT%H:%M:%S%q%:z" // 2006-01-02T15:04:05.999Z07:00
RFC3339Micro = "%Y-%m-%dT%H:%M:%S%i%:z" // 2006-01-02T15:04:05.999999Z07:00
RFC3339Nano = "%Y-%m-%dT%H:%M:%S%J%:z" // 2006-01-02T15:04:05.999999999Z07:00
Kitchen = "%-I:%M%p" // 3:04PM
// Handy time stamps.
Stamp = "%b %e %H:%M:%S" // Jan _2 15:04:05
Expand Down
14 changes: 8 additions & 6 deletions layout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ func TestLayout(t *testing.T) {
{RFC1123, time.RFC1123},
{RFC1123Z, time.RFC1123Z},
{RFC3339, time.RFC3339},
{RFC3339Milli, "2006-01-02T15:04:05.000Z07:00"},
{RFC3339Micro, "2006-01-02T15:04:05.000000Z07:00"},
{RFC3339Nano, "2006-01-02T15:04:05.000000000Z07:00"},
// {RFC3339Nano, time.RFC3339Nano},
{RFC3339Milli, "2006-01-02T15:04:05.999Z07:00"},
{RFC3339Micro, "2006-01-02T15:04:05.999999Z07:00"},
{RFC3339Nano, time.RFC3339Nano},
{Kitchen, time.Kitchen},
{Stamp, time.Stamp},
{StampMilli, time.StampMilli},
Expand All @@ -34,8 +33,11 @@ func TestLayout(t *testing.T) {
now := time.Now()

for _, c := range cases {
if got, want := Strftime(c.Layout, now), now.Format(c.StdLayout); got != want {
t.Errorf("Strftime(%#v, %#v) want=%v got=%v", c.Layout, atime, want, got)
for _, rounding := range []time.Duration{time.Nanosecond, time.Nanosecond * 10, time.Nanosecond * 100, time.Microsecond, time.Microsecond * 10, time.Microsecond * 100, time.Millisecond, time.Millisecond * 10, time.Millisecond * 100, time.Second, time.Minute} {
now := now.Round(rounding)
if got, want := Strftime(c.Layout, now), now.Format(c.StdLayout); got != want {
t.Errorf("Strftime(%#v, %#v) want=%v got=%v", c.Layout, atime, want, got)
}
}
}
}
103 changes: 102 additions & 1 deletion strftime.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ func AppendStrftime(dst []byte, format string, t time.Time) []byte {
case 'E':
panic("not implemented")
case 'f':
var tmp [6]byte
a := t.Nanosecond() / 1000
var tmp [6]byte
b := a % 100 * 2
tmp[5] = tab[b+1]
tmp[4] = tab[b]
Expand All @@ -83,6 +83,8 @@ func AppendStrftime(dst []byte, format string, t time.Time) []byte {
dst = append(dst, tab[a], tab[a+1])
case 'H':
dst = append(dst, tab[hour*2], tab[hour*2+1])
case 'i':
dst = significantOnly(dst, t.Nanosecond()/int(time.Microsecond), 6)
case 'I':
a := hour % 12
if a == 0 {
Expand All @@ -94,6 +96,8 @@ func AppendStrftime(dst []byte, format string, t time.Time) []byte {
a := t.YearDay() / 100
b := t.YearDay() % 100 * 2
dst = append(dst, byte(a)+'0', tab[b], tab[b+1])
case 'J':
dst = significantOnly(dst, t.Nanosecond(), 9)
case 'k':
if hour >= 10 {
dst = append(dst, tab[hour*2], tab[hour*2+1])
Expand Down Expand Up @@ -150,6 +154,8 @@ func AppendStrftime(dst []byte, format string, t time.Time) []byte {
} else {
dst = append(dst, "pm"...)
}
case 'q':
dst = significantOnly(dst, t.Nanosecond()/int(time.Millisecond), 3)
case 'Q':
a := t.Nanosecond() / 1000000
b := a % 100 * 2
Expand Down Expand Up @@ -417,6 +423,101 @@ func AppendStrftime(dst []byte, format string, t time.Time) []byte {
return dst
}

func significantOnly(dst []byte, a, digits int) []byte {
if a == 0 {
return dst
}
for digits > 3 && a%1000 == 0 {
a /= 1000
digits -= 3
}
// at most two more digits to remove
if a%10 == 0 {
a /= 10
digits--
if a%10 == 0 {
a /= 10
digits--
}
}
switch digits {
case 9:
var tmp [10]byte
b := a % 100 * 2
tmp[8], tmp[9] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[6], tmp[7] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[4], tmp[5] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[0], tmp[1], tmp[2], tmp[3] = '.', byte(a/100)+'0', tab[b], tab[b+1]
return append(dst, tmp[:]...)
case 8:
var tmp [9]byte
b := a % 100 * 2
tmp[7], tmp[8] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[5], tmp[6] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[3], tmp[4] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[0], tmp[1], tmp[2] = '.', tab[b], tab[b+1]
return append(dst, tmp[:]...)
case 7:
var tmp [8]byte
b := a % 100 * 2
tmp[6], tmp[7] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[4], tmp[5] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[0], tmp[1], tmp[2], tmp[3] = '.', byte(a/100)+'0', tab[b], tab[b+1]
return append(dst, tmp[:]...)
case 6:
var tmp [7]byte
b := a % 100 * 2
tmp[5], tmp[6] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[3], tmp[4] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[0], tmp[1], tmp[2] = '.', tab[b], tab[b+1]
return append(dst, tmp[:]...)
case 5:
var tmp [6]byte
b := a % 100 * 2
tmp[4], tmp[5] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[0], tmp[1], tmp[2], tmp[3] = '.', byte(a/100)+'0', tab[b], tab[b+1]
return append(dst, tmp[:]...)
case 4:
var tmp [5]byte
b := a % 100 * 2
tmp[3], tmp[4] = tab[b], tab[b+1]
a /= 100
b = a % 100 * 2
tmp[0], tmp[1], tmp[2] = '.', tab[b], tab[b+1]
return append(dst, tmp[:]...)
case 3:
b := a % 100 * 2
return append(dst, '.', byte(a/100)+'0', tab[b], tab[b+1])
case 2:
b := a * 2
return append(dst, '.', tab[b], tab[b+1])
default:
return append(dst, '.', byte(a)+'0')
}
}

func appendUpper(dst []byte, s string) []byte {
for _, c := range []byte(s) {
if 'a' <= c && c <= 'z' {
Expand Down