Escaping numbers when using Go's time.Format - go

I'm currently trying to take a time.Time object and go and produce a formatted string that happens to include some numbers that I do NOT want to be parsed as a time. For example, consider the following program:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
msg := now.Format("Encountered a 502 error on 2006-01-02 15:02 MST")
fmt.Println(msg)
}
Unfortunately, the text "502" is interpreted as a time here: running this code will produce output like Encountered a 1112 error on 2018-07-12 9:12 UTC.
Is there any way to escape the 502 numbers so they aren't interpreted as numbers? E.g. similar to how you can escape the % meta-character by using %% in languages that implement strftime-style formatting logic?
Or is my only option to just split this up and use two formatting operations instead of one?
nowString := now.Format("2006-01-02 15:02 MST")
msg := fmt.Sprintf("Encountered 502 error on %s", nowString)

No, there is no escape for numbers in time.Format. The purpose of that method is for formatting time, not for formatting strings in general.
If this is used from multiple locations, the usual solution would be to make a simple function to do the formatting
func nowMessage(msg string) string {
const layout = "2006-01-02 15:02 MST"
return fmt.Sprintf("%s %s", msg, time.Now().Format(layout))
}

Related

How to convert the string representation of a Terraform set of strings to a slice of strings

I've a terratest where I get an output from terraform like so s := "[a b]". The terraform output's value = toset([resource.name]), it's a set of strings.
Apparently fmt.Printf("%T", s) returns string. I need to iterate to perform further validation.
I tried the below approach but errors!
var v interface{}
if err := json.Unmarshal([]byte(s), &v); err != nil {
fmt.Println(err)
}
My current implementation to convert to a slice is:
s := "[a b]"
s1 := strings.Fields(strings.Trim(s, "[]"))
for _, v:= range s1 {
fmt.Println("v -> " + v)
}
Looking for suggestions to current approach or alternative ways to convert to arr/slice that I should be considering. Appreciate any inputs. Thanks.
Actually your current implementation seems just fine.
You can't use JSON unmarshaling because JSON strings must be enclosed in double quotes ".
Instead strings.Fields does just that, it splits a string on one or more characters that match unicode.IsSpace, which is \t, \n, \v. \f, \r and .
Moeover this works also if terraform sends an empty set as [], as stated in the documentation:
returning [...] an empty slice if s contains only white space.
...which includes the case of s being empty "" altogether.
In case you need additional control over this, you can use strings.FieldsFunc, which accepts a function of type func(rune) bool so you can determine yourself what constitutes a "space". But since your input string comes from terraform, I guess it's going to be well-behaved enough.
There may be third-party packages that already implement this functionality, but unless your program already imports them, I think the native solution based on the standard lib is always preferrable.
unicode.IsSpace actually includes also the higher runes 0x85 and 0xA0, in which case strings.Fields calls FieldsFunc(s, unicode.IsSpace)
package main
import (
"fmt"
"strings"
)
func main() {
src := "[a b]"
dst := strings.Split(src[1:len(src)-1], " ")
fmt.Println(dst)
}
https://play.golang.org/p/KVY4r_8RWv6

How to parse timestamp with underscores in Golang

I'm trying to parse access log timestamp like "2020/11/06_18:17:25_455" in Filebeat according to Golang spec.
Here is my test program to verify layout:
package main
import (
"fmt"
"log"
"time"
)
func main() {
eventDateLayout := "2006/01/02_15:04:05_000"
eventCheckDate, err := time.Parse(eventDateLayout, "2020/11/06_18:17:25_455")
if err != nil {
log.Fatal(err)
}
fmt.Println(eventCheckDate)
}
Result:
2009/11/10 23:00:00 parsing time "2020/11/06_18:17:25_455" as
"2006/01/02_15:04:05_000": cannot parse "455" as "_000"
As I understand underscore has a special meaning in Golang, but from documentation it's not clear how to escape it.
Any ideas, please?
It doesn't seem possible to use any escape characters for the time layout (e.g. "\\_" doesn't work), so one would have to do something different.
This issue describes the same problem, but it was solved in a very non-general way that doesn't seem to apply to your format.
So your best bet seems to be replacing _ with something else/stripping it from the string, then using a layout without it. To make sure that the millisecond part ist also parsed, it must be separated with a . instead of _, then it's recognized as part of the seconds (05) format.
eventDateLayout := "2006/01/02.15:04:05"
val := strings.Replace("2020/11/06_18:17:25_455", "_", ".", 2)
eventCheckDate, err := time.Parse(eventDateLayout, val)
if err != nil {
panic(err)
}
fmt.Println(eventCheckDate)
Playground link
From time.Format
A fractional second is represented by adding a period and zeros to the
end of the seconds section of layout string, as in "15:04:05.000" to
format a time stamp with millisecond precision.
You cannot specify millisecond precision with an underscore you need 05.000 instead:
// eventDateLayout := "2006/01/02_15:04:05_000" // invalid format
eventDateLayout := "2006/01/02_15:04:05.000"
eventCheckDate, err := time.Parse(eventDateLayout, "2020/11/06_18:17:25.455")
So basically use a simple translate function to convert the final _ to a . and use the above parser.
https://play.golang.org/p/POPgXC_qe81

Parsefloat give output in scientific format in golang

i am trying to parse this string "7046260" using Parsefloat function in golang , but i am getting output in scientific format 7.04626e+06. i want the output in the format 7046260. how to get this?
package main
import (
"fmt"
"strconv"
)
func main() {
Value := "7046260"
Fval, err := strconv.ParseFloat(Value, 64)
if err == nil {
fmt.Println(Fval)
}
}
ouput :- 7.04626e+06
Parsefloat give output in scientific format in golang
i am trying to parse this string "7046260" using Parsefloat function in golang , but i am getting output in scientific format 7.04626e+06. i want the output in the format 7046260
You're confusing the floating-point value's (default) formatted output with its internal representation.
ParseFloat is working fine.
You just need to specify an output format:
See the fmt package documentation.
Use Printf to specify a format-string.
Use the format %.0f to instruct Go to print the value as-follows:
% marks the start of a placeholder.
. denotes default width (i.e. don't add leading or trailing zeroes).
0 denotes zero radix precision (i.e. don't print any decimal places, even if the value has them)
f denotes the end of the placeholder, and that the placeholder is for a floating-point value.
I have a few other recommendations:
Local variables in Go should use camelCase, not PascalCase. Go does not encourage the use of snake_case.
You should check err != nil after each nil-returning function returns and either fail-fast (if appropriate), pass the error up (and optionally log it), or handle it gracefully.
When working with floating-point numbers, you should be aware of NaN's special status. The IsNaN function is the only way to correctly check for NaN values (because ( aNaNValue1 == math.NaN ) == false).
The same applies in all languages that implement IEEE-754, including Java, JavaScript, C, C#.NET and Go.
Like so:
package main
import (
"fmt"
"strconv"
"math"
"log"
)
func main() {
numberText := "7046260"
numberFloat, err := strconv.ParseFloat(numberText, 64)
if err != nil {
log.Fatal(err)
}
if math.IsNaN(numberFloat) {
log.Fatal("NaN value encountered")
}
fmt.Printf("%.0f",numberFloat)
fmt.Println()
}

String format with errors with %e

I've encountered some go code that appears to use %e for formatting an error for display to the screen. A simplified version would be code like this
err := errors.New("La de da")
fmt.Printf("%e\n", err)
outputs
&{%!e(string=La de da)}
However, if I look at the go manual, it says %e is for formatting floating point numbers in scientific notation. That output doesn't look like scientific notation, so I'm wondering
If this is a specific notation, what is it? (i.e. is there a %. formatting option I could use to get that format)
If it's not a specific notation, what weird thing is going on under the hood that leads to an error being rendered in this way?
What silly, obvious thing am I missing that renders most of what I've said in this post wrong?
Read the Go documentation.
Package fmt
Printing
Format errors:
If an invalid argument is given for a verb, such as providing a string
to %d, the generated string will contain a description of the problem,
as in these examples:
Wrong type or unknown verb: %!verb(type=value)
Printf("%d", hi): %!d(string=hi)
Too many arguments: %!(EXTRA type=value)
Printf("hi", "guys"): hi%!(EXTRA string=guys)
Too few arguments: %!verb(MISSING)
Printf("hi%d"): hi%!d(MISSING)
Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi
Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
Invalid or invalid use of argument index: %!(BADINDEX)
Printf("%*[2]d", 7): %!d(BADINDEX)
Printf("%.[2]d", 7): %!d(BADINDEX)
All errors begin with the string "%!" followed sometimes by a single
character (the verb) and end with a parenthesized description.
For your example,
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("La de da")
fmt.Printf("%e\n", err)
}
Playground: https://play.golang.org/p/NKC6WWePyxM
Output:
&{%!e(string=La de da)}
Documentation:
All errors begin with the string "%!" followed sometimes by a single
character (the verb) and end with a parenthesized description.
Wrong type or unknown verb: %!verb(type=value)
Printf("%d", hi): %!d(string=hi)
When formatting errors into strings using fmt.Printf()/Println(), you can do the following:
err := fnThatReturnsErr()
if err != nil {
fmt.Println("The error is %v", err)
}
I believe %v is the formatting option you were looking for.

Sscanf of multiple string fields in golang

I am trying to use sscanf to parse multiple string fields. Here is an example code snippet:
package main
import "fmt"
func main() {
var name, currency string
_, err := fmt.Sscanf("transaction benson: dollars", "transaction %s: %s", &name, &currency)
fmt.Println(err, name, currency)
}
The output is
input does not match format benson:
Program exited.
%s is greedy and gobbles up to the next space, which means it eats up the colon. After processing the %s, it then tries to scan in the colon, but wait, that’s already been consumed, and the next character is actually a space, not a colon! So it fails.
In C you’d get around this by using %[^:] rather than %s, but it appears Go doesn’t support this. You might need to find some way to parse your string without Sscanf.

Resources