Parsing Dynamic Time Format in Go - go

I'm working with an external API that will sometimes shorten the returned timestamp if the last values are zero.
layout := "2006-01-02T15:04:05.000"
opened, err := time.Parse(layout, externallyFormattedTimestamp)
if err != nil {
fmt.Println("something went wrong parsing the timestamp")
fmt.Println(err.Error())
}
If the returned externallyFormattedTimestamp is 2018-11-05T06:19:59.827, this will parse without issue.
But if the time returned ends with zeros, it'll be shortened like 2018-11-05T06:19:59.8 instead of 2018-11-05T06:19:59.800, and break. Do I need to manually append zeros to the string before parsing, or is there something I missed built into Go that will handle this for me?

Use this layout instead: 2006-01-02T15:04:05.999

Related

I add file to my API and got invalid character '-' in numeric literal in POST API

I know this code need to send a JSON instead of form data in the API
err := ctx.ShouldBindJSON(&modelAdd)
if err != nil {
return err
}
But I need to add file, is there anything like ShouldBindJSON but for FormData?
You can use ShouldBind to get data from form data as the documentation says
https://github.com/gin-gonic/gin#model-binding-and-validation

Work out if 2022-01-14T20:56:55Z is a valid date time in Go

I am attempting to create a function that tells me if a timestamp is valid or not.
My function looks like
// IsTimestamp checks if a string contains a timestamp.
func IsTimestamp(str string) bool {
_, err := time.Parse("2006-01-02 15:04:05.999", str)
if err != nil {
return false
}
return true
}
However, passing in 2022-01-14T20:56:55Z returns false when is it a valid timestamp.
I'm thinking this might be something to do with the layout I am using in time.Parse but I've tried just using the date with no luck.
Your layout doesn't match your input string, so it's expected that it isn't parsed successfully.
The docs say:
Parse parses a formatted string and returns the time value it represents. See the documentation for the constant called Layout to see how to represent the format. The second argument must be parseable using the format string (layout) provided as the first argument.
Therefore, you should use a layout that's matching your input. Below, I am using RFC3339, which is the layout of your input string.
if _, err := time.Parse(time.RFC3339, str); err != nil {
...
}
https://go.dev/play/p/_Q26NS2wwfy
2022-01-14T20:56:55Z does not match the layout 2006-01-02 15:04:05.999 because:
the layout expects a whitespace after day, not T
the layout expects exactly three digits for milliseconds (only two are given 55)
the layout does not expect the timezone to be specified. Z is not a valid.
You can match 2022-01-14T20:56:55Z with the layout 2006-01-02T15:04:05.99Z, or 2006-01-02T15:04:05Z. Or even better, use The Fool's answer.

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

Is it possible to dump golang db.Query() output to a string?

I have a small Heroku app in which i print out name and age from each rows after query execution.
I want to avoid looping rows.Next(),Scan().. and just want to show what database returned after query execution which may be some data or error.
Can we directly dump data to a string for printing?
rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)
if err != nil {
log.Fatal(err)
}
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
Pretty much: No.
The Query method is going to return a pointer to a Rows struct:
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
If you print that (fmt.Printf("%#v\n", rows)) you'll see something such as:
&sql.Rows{dc:(*sql.driverConn)(0xc8201225a0), releaseConn:(func(error)(0x4802c0), rowsi:(*pq.rows)(0xc820166700), closed:false, lastcols:[]driver.Value(nil), lasterr:error(nil), closeStmt:driver.Stmt(nil)}
...probably not what you want.
Those correspond to the Rows struct from the sql package (you'll notice the fields are not exported):
type Rows struct {
dc *driverConn // owned; must call releaseConn when closed to release
releaseConn func(error)
rowsi driver.Rows
closed bool
lastcols []driver.Value
lasterr error // non-nil only if closed is true
closeStmt driver.Stmt // if non-nil, statement to Close on close
}
You'll see []driver.Value (an interface from the driver package), that looks like where we can expect to find some useful, maybe even human readable data. But when directly printed it doesn't appear useful, it's even empty... So you have to somehow get at the underlying information. The sql package gives us the Next method to start with:
Next prepares the next result row for reading with the Scan method.
It returns true on success, or false if there is no next
result row or an error happened while preparing it. Err
should be consulted to distinguish between the two cases.
Every call to Scan, even the first one, must be preceded by a call to Next.
Next is going to make a []driver.Value the same size as the number of columns I have, which is accessible (within the sql package) through driver.Rows (the rowsi field) and populate it with values from the query.
After calling rows.Next() if you did the same fmt.Printf("%#v\n", rows) you should now see that []diver.Value is no longer empty but it's still not going to be anything that you can read, more likely something resembling:[]diver.Value{[]uint8{0x47, 0x65...
And since the field isn't exported you can't even try and convert it to something more meaningful. But the sql package gives us a means to do something with the data, which is Scan.
The Scan method is pretty concise, with lengthy comments that I won't paste here, but the really important bit is that it ranges over the columns in the current row you get from the Next method and calls convertAssign(dest[i], sv), which you can see here:
https://golang.org/src/database/sql/convert.go
It's pretty long but actually relatively simple, it essentially switches on the type of the source and destination and converts where it can, and copies from source to destination; the function comments tell us:
convertAssign copies to dest the value in src, converting it if possible. An error is returned if the copy would result in loss of information. dest should be a pointer type.
So now you have a method (Scan) which you can call directly and which hands you back converted values. Your code sample above is fine (except maybe the call to Fatal() on a Scan error).
It's important to realize that the sql package has to work with a specific driver, which is in turn implemented for specific database software, so there is quite some work going on behind the scenes.
I think your best bet if you want to hide/generalize the whole Query() ---> Next() ---> Scan() idiom is to drop it into another function which does it behind the scenes... write a package in which you abstract away that higher level implementation, as the sql package abstracts away some of the driver-specific details, the converting and copying, populating the Rows, etc.

Scandinavian characters not working in go-lang go-instagram API bindings

Hi I'm trying to wrap my head around what seems to be a problem with multibyte support in this open source library (https://github.com/carbocation/go-instagram/). I am using the code below to retrieve information about the tag blue in swedish. How ever I get an empty array when trying.
fmt.Println("Starting instagram download.")
client := instagram.NewClient(nil)
client.ClientID = "myid"
media, _, _ := client.Tags.RecentMedia("blÄ", nil)
fmt.Println(media)
I have tried using the api trough the browser and there are several pictures tagged with the tag. I have also tried using the code snippet with tags in English like blue and that returns the latest pictures as well. I would be glad if any one could explain why this might happen. Id like to update the lib so it supports multi-byte but I haven't got the go knowledge required. Is this a go problem or a problem with the library?
Thank you
The problem is in validTagName():
// Strip out things we know Instagram won't accept. For example, hyphens.
func validTagName(tagName string) (bool, error) {
//\W matches any non-word character
reg, err := regexp.Compile(`\W`)
if err != nil {
return false, err
}
if reg.MatchString(tagName) {
return false, nil
}
return true, nil
}
In Go, \W matches precisely [^0-9A-Za-z_]. This validation check is incorrect.

Resources