Parsing date/time strings which are not 'standard' formats - time

How do I parse non-standard date/time strings in Go. In example if I wanted to convert the string 10/15/1983 into a time.Time? The time.Parse() function supposedly allows you to specify a format.
http://play.golang.org/p/v5DbowXt1x
package main
import "fmt"
import "time"
func main() {
test, err := time.Parse("10/15/1983", "10/15/1983")
if err != nil {
panic(err)
}
fmt.Println(test)
}
This results in a panic.
panic: parsing time "10/15/1983" as "10/15/1983": cannot parse "" as "0/"
Logically that makes sense because how is it supposed to know which is the day and which is the month.
Other languages have a function similar to the following:
parse("mm/dd/yyyy", "10/15/1983")
I cannot find such a function in the Go docs, is my only choice to regex?

There are some key values that the time.Parse is looking for.
By changing:
test, err := time.Parse("10/15/1983", "10/15/1983")
to
test, err := time.Parse("01/02/2006", "10/15/1983")
the parser will recognize it.
Here's the modified code on the playground.
package main
import "fmt"
import "time"
func main() {
test, err := time.Parse("01/02/2006", "10/15/1983")
if err != nil {
panic(err)
}
fmt.Println(test)
}
You can utilize the constants list in the src/pkg/time/format.go file to create your own parse formats.
const (
stdLongMonth = "January"
stdMonth = "Jan"
stdNumMonth = "1"
stdZeroMonth = "01"
stdLongWeekDay = "Monday"
stdWeekDay = "Mon"
stdDay = "2"
stdUnderDay = "_2"
stdZeroDay = "02"
stdHour = "15"
stdHour12 = "3"
stdZeroHour12 = "03"
stdMinute = "4"
stdZeroMinute = "04"
stdSecond = "5"
stdZeroSecond = "05"
stdLongYear = "2006"
stdYear = "06"
stdPM = "PM"
stdpm = "pm"
stdTZ = "MST"
stdISO8601TZ = "Z0700" // prints Z for UTC
stdISO8601ColonTZ = "Z07:00" // prints Z for UTC
stdNumTZ = "-0700" // always numeric
stdNumShortTZ = "-07" // always numeric
stdNumColonTZ = "-07:00" // always numeric
)
So anytime your format specifies a year, it should be done with "06" or "2006", seconds are specified by "05" or "5" and time zones are specified at "MST", "Z0700", "Z07:00", "-0700", "-07" or "-07:00". If you reference the constants list you can likely put together any standard format you'd need to parse.
For example, if you want to parse the date/time in the Common Log Format, the format Apache uses for its log files, you would do so by passing the following string to time.Parse() as the layout argument.
"02/Jan/2006:15:04:05 -0700"
"02" denotes the day of the month field, "Jan" denotes the month name field, "2006" denotes the year field, "15" denotes the hour of day field in 24 hour format, "04" denotes the minutes field, "05" denotes the seconds field and "-0700" denotes the time zone field.
That format would parse the current PST time: 31/Dec/2012:15:32:25 -0800
So the time.Parse() call would look like this:
test, err := time.Parse("02/Jan/2006:15:04:05 -0700", "31/Dec/2012:15:32:25 -0800")

If you can't remember the Numbers in the specifying layout ("2006-01-02T15:04:05.000Z"), you may use my simple date formatting library github.com/metakeule/fmtdate that uses MS Excel conventions, like Y,M,D,h and internally translates them to the number format:
package main
import (
"github.com/metakeule/fmtdate"
"fmt"
)
func main() {
test, err := fmtdate.Parse("MM/DD/YYYY", "10/15/1983")
if err != nil {
panic(err)
}
fmt.Println(test)
}

If you are looking for a C-Style formatting function: After reviewing some of the options I have chosen https://github.com/cactus/gostrftime as it generally follows the strfmt(3) notation.
To quote the example:
import (
"fmt"
"time"
"github.com/cactus/gostrftime"
)
func main() {
now := time.Now()
fmt.Println(gostrftime.Format("%Y-%m-%d", now))
}
If a date format has to be used by both C and Go and the C implementation is not to be touched there's no choice but to adapt on the Go end. The above package fulfills that need.

If you don't want bother remembering the magic numbers, you can do this:
package main
import (
"fmt"
"time"
)
func main() {
var (
y, d int
m time.Month
)
fmt.Sscanf("10/15/1983", "%v/%v/%v", &m, &d, &y)
t := time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
fmt.Println(t) // 1983-10-15 00:00:00 +0000 UTC
}
https://golang.org/pkg/fmt#Sscanf

Related

How can I clean the text for search using RegEx

I can use the below code to search if the text str contains any or both of the keys, i.e.if it contains "MS" or "dynamics" or both of them
package main
import (
"fmt"
"regexp"
)
func main() {
keys := []string{"MS", "dynamics"}
keysReg := fmt.Sprintf("(%s %s)|%s|%s", keys[0], keys[1], keys[0], keys[1]) // => "(MS dynamics)|MS|dynamics"
fmt.Println(keysReg)
str := "What is MS dynamics, is it a product from MS?"
re := regexp.MustCompile(`(?i)` + keysReg)
matches := re.FindAllString(str, -1)
fmt.Println("We found", len(matches), "matches, that are:", matches)
}
I want the user to enter his phrase, so I trim unwanted words and characters, then doing the search as per above.
Let's say the user input was: This,is,a,delimited,string and I need to build the keys variable dynamically to be (delimited string)|delimited|string so that I can search for my variable str for all the matches, so I wrote the below:
s := "This,is,a,delimited,string"
t := regexp.MustCompile(`(?i),|\.|this|is|a`) // backticks are used here to contain the expression, (?i) for case insensetive
v := t.Split(s, -1)
fmt.Println(len(v))
fmt.Println(v)
But I got the output as:
8
[ delimited string]
What is the wrong part in my cleaning of the input text, I'm expecting the output to be:
2
[delimited string]
Here is my playground
To quote the famous quip from Jamie Zawinski,
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Two things:
Instead of trying to weed out garbage from the string ("cleaning" it), extract complete words from it instead.
Unicode is a compilcated matter; so even after you have succeeded with extracting words, you have to make sure your words are properly "escaped" to not contain any characters which might be interpreted as RE syntax before building a regexp of them.
package main
import (
"errors"
"fmt"
"regexp"
"strings"
)
func build(words ...string) (*regexp.Regexp, error) {
var sb strings.Builder
switch len(words) {
case 0:
return nil, errors.New("empty input")
case 1:
return regexp.Compile(regexp.QuoteMeta(words[0]))
}
quoted := make([]string, len(words))
for i, w := range words {
quoted[i] = regexp.QuoteMeta(w)
}
sb.WriteByte('(')
for i, w := range quoted {
if i > 0 {
sb.WriteByte('\x20')
}
sb.WriteString(w)
}
sb.WriteString(`)|`)
for i, w := range quoted {
if i > 0 {
sb.WriteByte('|')
}
sb.WriteString(w)
}
return regexp.Compile(sb.String())
}
var words = regexp.MustCompile(`\pL+`)
func main() {
allWords := words.FindAllString("\tThis\v\x20\x20,\t\tis\t\t,?a!,¿delimited?,string‽", -1)
re, err := build(allWords...)
if err != nil {
panic(err)
}
fmt.Println(re)
}
Further reading:
https://pkg.go.dev/regexp/syntax
https://pkg.go.dev/regexp#QuoteMeta
https://pkg.go.dev/unicode#pkg-variables and https://pkg.go.dev/unicode#Categories

Go: CSV NewReader not getting the correct number of fields

How to get the correct number of fields when using NewReader ?
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`||""FOO""||`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v\n", len(record))
}
https://go.dev/play/p/gg-KYRciWFH
It should return 5, but instead I'm getting 3:
record length: 3
Program exited.
EDIT
I'm actually working with a big CSV file containing many double quotes.
After examining your code, I decided to modify it slightly and then print the results:
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`x||""FOO""|x|x\n`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v, Data: %v\n", len(record), strings.Join(record, ", "))
}
When you run this, the data is printed as x, , "FOO"||x|x\n". My thought is that when you end your entry with two double-quotes, the parser is assuming the string is still being quoted and therefore lumps the rest of the line into the third entry. This appears to be a bug with how lazy-quoting works in the csv package, however, when examining the documentation for LazyQuotes, you'll see this:
If LazyQuotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field.
This doesn't mention anything about finding double quotes within double quotes. To fix this, you should either remove the quotes altogether or replace the double double-quotes ("") with double quotes (").
One other thing you might consider would be using the gocsv package. I've worked with this package in the past and it's reasonably stable. I'm not sure how it would respond to this specific issue, but it might be worth your time checking it out.
Note:
The encoding/csv package implements the RFC 4180 standard. If you have such input, that's not an RFC 4180 compliant CSV file and encoding/csv will not parse it properly.
You're misusing the quotes. Quoting a single field FOO is like this:
parser := csv.NewReader(strings.NewReader(`||"FOO"||`))
If you want the field to have the "FOO" value, you have to use 2 double quotes in a quoted field, so it should be:
parser := csv.NewReader(strings.NewReader(`||"""FOO"""||`))
This will output 5. Try it on the Go Playground.
What you have is this:
parser := csv.NewReader(strings.NewReader(`||""FOO""||`))
Since the second " character is not followed by a separator character, the field is not interrupted and the rest is processed as the content of the quoted field (which will terminate at the end of the line).
If you print the record:
fmt.Println(record)
fmt.Printf("%#v", record)
Output will be (try it on the Go Playground):
[ "FOO"||]
[]string{"", "", "\"FOO\"||"}
Quotes are a part of csv format.
There is a problem with go/csv shielding, you can try something like this:
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`||FOO||`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v\n", len(record))
fmt.Println(strings.Join(record, " /SEP/ "))
}
or like this:
package main
import (
"encoding/csv"
"fmt"
"log"
"strings"
)
func main() {
parser := csv.NewReader(strings.NewReader(`||"""FOO"""||`))
parser.Comma = '|'
parser.LazyQuotes = true
record, err := parser.Read()
if err != nil {
log.Fatal(err)
}
fmt.Printf("record length: %v\n", len(record))
fmt.Println(strings.Join(record, " SEP "))
}

Unable to convert my string to hex message

Below has x which is my expected string
I am trying to recreate y myself to match my expected string. Basically trying to convert "01" to "\x01" so that I get the same byte when printed.
Now when I print []byte(x) and []byte(y) I want them to be the same but they aren't. Please help me recreate x with "01" as my input.
package main
import (
"fmt"
)
func main() {
//Expected string
x := "\x01"
//Trying to convert my 01 in string form to same as above - Basically recreate above string again
y := "\\x" + "01"
fmt.Println(x)
fmt.Println(y)
fmt.Println([]byte(x))
fmt.Println([]byte(y))
}
This is what i wanted - got my issue resolved ! :) Thanks all
import (
"fmt"
"encoding/hex"
)
func main() {
//Expected string
x := "\x01"
//Trying to convert my 01 in string form to same as above - Basically recreate above string again
y,err := hex.DecodeString("01")
if err != nil {
panic(err)
}
fmt.Println(x)
fmt.Println(y)
fmt.Println([]byte(x))
fmt.Println([]byte(y))
}
You cannot build a string from a byte sequence that way... \x01 is an escaping notation processed by the compiler when reading literal strings, you cannot use that processing at runtime.
To build a string with the bytes you want you can simply use
x := string([]byte{1, 2, 3, 4})

How to get time without timezone? [duplicate]

I'm trying to add some values from my database to a []string in Go. Some of these are timestamps.
I get the error:
cannot use U.Created_date (type time.Time) as type string in array element
Can I convert time.Time to string?
type UsersSession struct {
Userid int
Timestamp time.Time
Created_date time.Time
}
type Users struct {
Name string
Email string
Country string
Created_date time.Time
Id int
Hash string
IP string
}
-
var usersArray = [][]string{}
rows, err := db.Query("SELECT u.id, u.hash, u.name, u.email, u.country, u.IP, u.created_date, us.timestamp, us.created_date FROM usersSession AS us LEFT JOIN users AS u ON u.id = us.userid WHERE us.timestamp + interval 30 minute >= now()")
U := Users{}
US := UsersSession{}
for rows.Next() {
err = rows.Scan(&U.Id, &U.Hash, &U.Name, &U.Email, &U.Country, &U.IP, &U.Created_date, &US.Timestamp, &US.Created_date)
checkErr(err)
userid_string := strconv.Itoa(U.Id)
user := []string{userid_string, U.Hash, U.Name, U.Email, U.Country, U.IP, U.Created_date, US.Timestamp, US.Created_date}
// -------------
// ^ this is where the error occurs
// cannot use U.Created_date (type time.Time) as type string in array element (for US.Created_date and US.Timestamp aswell)
// -------------
usersArray = append(usersArray, user)
log.Print("usersArray: ", usersArray)
}
EDIT
I added the following. It works now, thanks.
userCreatedDate := U.Created_date.Format("2006-01-02 15:04:05")
userSessionCreatedDate := US.Created_date.Format("2006-01-02 15:04:05")
userSessionTimestamp := US.Timestamp.Format("2006-01-02 15:04:05")
You can use the Time.String() method to convert a time.Time to a string. This uses the format string "2006-01-02 15:04:05.999999999 -0700 MST".
If you need other custom format, you can use Time.Format(). For example to get the timestamp in the format of yyyy-MM-dd HH:mm:ss use the format string "2006-01-02 15:04:05".
Example:
t := time.Now()
fmt.Println(t.String())
fmt.Println(t.Format("2006-01-02 15:04:05"))
Output (try it on the Go Playground):
2009-11-10 23:00:00 +0000 UTC
2009-11-10 23:00:00
Note: time on the Go Playground is always set to the value seen above. Run it locally to see current date/time.
Also note that using Time.Format(), as the layout string you always have to pass the same time –called the reference time– formatted in a way you want the result to be formatted. This is documented at Time.Format():
Format returns a textual representation of the time value formatted according to layout, which defines the format by showing how the reference time, defined to be
Mon Jan 2 15:04:05 -0700 MST 2006
would be displayed if it were the value; it serves as an example of the desired output. The same display rules will then be applied to the time value.
package main
import (
"fmt"
"time"
)
// #link https://golang.org/pkg/time/
func main() {
//caution : format string is `2006-01-02 15:04:05.000000000`
current := time.Now()
fmt.Println("origin : ", current.String())
// origin : 2016-09-02 15:53:07.159994437 +0800 CST
fmt.Println("mm-dd-yyyy : ", current.Format("01-02-2006"))
// mm-dd-yyyy : 09-02-2016
fmt.Println("yyyy-mm-dd : ", current.Format("2006-01-02"))
// yyyy-mm-dd : 2016-09-02
// separated by .
fmt.Println("yyyy.mm.dd : ", current.Format("2006.01.02"))
// yyyy.mm.dd : 2016.09.02
fmt.Println("yyyy-mm-dd HH:mm:ss : ", current.Format("2006-01-02 15:04:05"))
// yyyy-mm-dd HH:mm:ss : 2016-09-02 15:53:07
// StampMicro
fmt.Println("yyyy-mm-dd HH:mm:ss: ", current.Format("2006-01-02 15:04:05.000000"))
// yyyy-mm-dd HH:mm:ss: 2016-09-02 15:53:07.159994
//StampNano
fmt.Println("yyyy-mm-dd HH:mm:ss: ", current.Format("2006-01-02 15:04:05.000000000"))
// yyyy-mm-dd HH:mm:ss: 2016-09-02 15:53:07.159994437
}
package main
import (
"fmt"
"time"
)
func main() {
v , _ := time.Now().UTC().MarshalText()
fmt.Println(string(v))
}
Output : 2009-11-10T23:00:00Z
Go Playground
Please find the simple solution to convete Date & Time Format in Go Lang. Please find the example below.
Package Link: https://github.com/vigneshuvi/GoDateFormat.
Please find the plackholders:https://medium.com/#Martynas/formatting-date-and-time-in-golang-5816112bf098
package main
// Import Package
import (
"fmt"
"time"
"github.com/vigneshuvi/GoDateFormat"
)
func main() {
fmt.Println("Go Date Format(Today - 'yyyy-MM-dd HH:mm:ss Z'): ", GetToday(GoDateFormat.ConvertFormat("yyyy-MM-dd HH:mm:ss Z")))
fmt.Println("Go Date Format(Today - 'yyyy-MMM-dd'): ", GetToday(GoDateFormat.ConvertFormat("yyyy-MMM-dd")))
fmt.Println("Go Time Format(NOW - 'HH:MM:SS'): ", GetToday(GoDateFormat.ConvertFormat("HH:MM:SS")))
fmt.Println("Go Time Format(NOW - 'HH:MM:SS tt'): ", GetToday(GoDateFormat.ConvertFormat("HH:MM:SS tt")))
}
func GetToday(format string) (todayString string){
today := time.Now()
todayString = today.Format(format);
return
}
strconv.Itoa(int(time.Now().Unix()))
Go Playground
http://play.golang.org/p/DN5Py5MxaB
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
// The Time type implements the Stringer interface -- it
// has a String() method which gets called automatically by
// functions like Printf().
fmt.Printf("%s\n", t)
// See the Constants section for more formats
// http://golang.org/pkg/time/#Time.Format
formatedTime := t.Format(time.RFC1123)
fmt.Println(formatedTime)
}

Generating Random Timestamps in Go

I'd like to generate a random timestamp within the last relative 3 years and have it be printed out with this format: %d/%b/%Y:%H:%M:%S %z
Here is what I have right now:
package main
import (
"strconv"
"time"
"math/rand"
"fmt"
)
func randomTimestamp() time.Time {
randomTime := rand.Int63n(time.Now().Unix() - 94608000) + 94608000
randomNow, err := time.Parse("10/Oct/2000:13:55:36 -0700", strconv.FormatInt(randomTime, 10))
if err != nil {
panic(err)
}
return randomNow
}
func main() {
fmt.Println(randomTimestamp().String())
}
This always throws: panic: parsing time "...": month out of range. How can I generate a random timestamp for a given range, then convert it to the string format I want with the standard library?
Don't use time.Parse. You have a Unix time, not a time string. Use the Unix() method instead. https://golang.org/pkg/time/#Unix. You can also choose a minimum time value, say 1/1/1900 and add a random Duration of seconds to the time using the Add method on Time and passing a Duration you made with the Ticks() method. https://golang.org/pkg/time/#Duration
Here's a Go Playground link. Just remember that the Go Playground doesn't support actual randomness. https://play.golang.org/p/qYTpnbml_N
package main
import (
"time"
"math/rand"
"fmt"
)
func randomTimestamp() time.Time {
randomTime := rand.Int63n(time.Now().Unix() - 94608000) + 94608000
randomNow := time.Unix(randomTime, 0)
return randomNow
}
func main() {
fmt.Println(randomTimestamp().String())
}

Resources