golang conditionals on the time difference (sub) - go

I have two times, t1 and t2.
To calculate the difference, I use,
diff := t2.sub(t1)
The above code returns the difference like 10m30s, 1h3m59s etc.
I need to create some conditions for the difference.
For example,
if diff <= 5m {
do this
}
if diff > 20m {
do that
}
My question is if there is any built-in way to compare the time difference.
My other option would be to parse returned diff with regex and then compare. But I was looking for some efficient ways like the sub which the time package offers.

t2.Sub(t1) returns a duration, and you can simply use the comparison operators, for example:
d, _ := time.ParseDuration("4m4s")
if d <= 5 * time.Second {
fmt.Println("<= than limit")
} else {
fmt.Println("> than limit")
}

Way 1: When we use sub to get the duration
A very simple alternative is to directly use the output of the sub function. The func sub returns time.Duration type. So just adding .Minutes() method with it would serve the purpose in my case.
t1 := time.Now()
time.Sleep(60011 * time.Millisecond)
t2 := time.Now()
timeDiff := t2.Sub(t1)
fmt.Println(timeDiff)
fmt.Printf("\nIn just minites: %.f\n", timeDiff.Minutes())
Playground
Way 2: When we have the duration in string
If we would have the "difference" as a string ("10m2s") type then I believe we need to use the ParseDuration function. From the godocs ParseDuration
From the doc,
ParseDuration parses a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
I am thinking of using it like the following,
t = "10h10m6s"
timeDiff, _ := time.ParseDuration(t)
numberOfHours := timeDiff.Hours()
numberOfMinutes := timeDiff.Minutes()
numberOfSeconds := timeDiff.Seconds()
numberofNanosec := timeDiff.Nanoseconds()
Find the example snippet on the playground
So in any of the above cases, we can use time.Minutes() to compare the duration.. As #gopher pointed out that to compare with any time range we do not need to convert it to any period of time (e,g Mintues(), Seconds()) but we can just compare with a required time period. As #Haris Osmanagić pointed out that this works for both of the output of ParseDuration and time.Sub() as they both returns time.Duration type. So we can do something like the following,
if timeDiff < 5 * time.Minutes() {
do something
} else if timeDiff > 5 * time.Minutes(){
do something else
} else {
do nothing
}
An example is on the playground.

Related

Unit of time for duration

I have in the code
fmt.Println("... ", time.Since(s1))
fmt.Println(".... ", time.Since(s2))
The results for the first is always in µs and for the second in ns (for example 7.081µs, respectively 365ns).
What causes this? How can I control it? I'd like 7081ns to be displayed, always ns/
I looked at the function; how could I interpret it?
// Since returns the time elapsed since t.
// It is shorthand for time.Now().Sub(t).
func Since(t Time) Duration {
var now Time
if t.wall&hasMonotonic != 0 {
// Common case optimization: if t has monotonic time, then Sub will use only it.
now = Time{hasMonotonic, runtimeNano() - startNano, nil}
} else {
now = Now()
}
return now.Sub(t)
}
The fmt package calls the time.Duration.String() method (because time.Duration implements the fmt.Stringer interface) which will use smaller units (milli-, micro-, or nanoseconds) if the duration is less than one second. You cannot control this directly.
You can however convert the number of nanoseconds returned from duration.Nanoseconds() to a string using Itoa, e.g. like this:
formatted := strconv.Itoa(int(time.Since(s2).Nanoseconds())) + "ns"
You can also see this example on the playground

How to convert string "210.0n" to float

I have the string value in the following format:
"210.0n" and I need to compare it with the value "2.1e-07". Direct comparison, of course, will fail.
Is there any way how to convert such strings like this "210.0n", "0.7m", "10.0K" (with units metrics) to the normal float values? Maybe dedicated Go module is available? I can't find it.
You can write this in a simple switch case yourself. There is no need for a library.
var str = "100k"
mFloat, err := strconv.ParseFloat(str[:len(str)-1], 64)
if err != nil{
//handle error
}
switch string(str[len(str)-1]){
case "k":
mFloat = mFloat * 1000
fmt.Printf("%e", )
case "m":
mFloat = mFloat * 1000000
fmt.Printf("%e", mFloat * 1000000)
// etc ....
}
return mFloat
Different suffixes: https://www.mathsisfun.com/metric-numbers.html
If this answer will help somebody then I have found this package on Github github.com/dustin/go-humanize which contains the necessary code to translate such kind of strings like "240n" "1p" and etc to the float.
It contains even more possibilities for data converting. Hope it will help some newbies who work with floats in Go.

How to write a vector

I am using the Go flatbuffers interface for the first time. I find the instructions sparse.
I would like to write a vector of uint64s into a table. Ideally, I would like to store numbers directly in a vector without knowing how many there are up front (I'm reading them from sql.Rows iterator). I see the generated code for the table has functions:
func DatasetGridAddDates(builder *flatbuffers.Builder, dates flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(2, flatbuffers.UOffsetT(dates), 0)
}
func DatasetGridStartDatesVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT {
return builder.StartVector(8, numElems, 8)
}
Can I first write the vector using (??), then use DatasetGridAddDates to record the resulting vector in the containing "DatasetGrid" table?
(caveat: I have not heard of FlatBuffers prior to reading your question)
If you do know the length in advance, storing a vector is done as explained in the tutorial:
name := builder.CreateString("hello")
q55310927.DatasetGridStartDatesVector(builder, len(myDates))
for i := len(myDates) - 1; i >= 0; i-- {
builder.PrependUint64(myDates[i])
}
dates := builder.EndVector(len(myDates))
q55310927.DatasetGridStart(builder)
q55310927.DatasetGridAddName(builder, name)
q55310927.DatasetGridAddDates(builder, dates)
grid := q55310927.DatasetGridEnd(builder)
builder.Finish(grid)
Now what if you don’t have len(myDates)? On a toy example I get exactly the same output if I replace StartDatesVector(builder, len(myDates)) with StartDatesVector(builder, 0). Looking at the source code, it seems like the numElems may be necessary for alignment and for growing the buffer. I imagine alignment might be moot when you’re dealing with uint64, and growing seems to happen automatically on PrependUint64, too.
So, try doing it without numElems:
q55310927.DatasetGridStartDatesVector(builder, 0)
var n int
for rows.Next() { // use ORDER BY to make them go in reverse order
var date uint64
if err := rows.Scan(&date); err != nil {
// ...
}
builder.PrependUint64(date)
n++
}
dates := builder.EndVector(n)
and see if it works on your data.

Parsing a time with the format HHMMSS00

I'm working with some data from multiple sources and one of these sources is a Sage ERP system.
I am trying to reference two files in Sage in particular, an audit date and audit time (AUDTDATE and AUDTTIME).
I need to parse this and store it as a DATETIME in a Microsoft SQL Server database.
Currently, I am just trying to figure out the best way to parse this.
An example of what the data might look like is below:
+----------+----------+
| AUDTDATE | AUDTTIME |
+----------+----------+
| 20170228 | 5013756 |
+----------+----------+
AUDTDATE is a yyyymmdd format and the AUDTTIME is HHMMSS00.
So I tried the below as a test:
func main() {
value := "20170228 5013756"
layout := "20060102 15040500"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
This doesn't work, it just returns 0001-01-01 00:00:00 +0000 UTC when run.
If I change the time to this 050137 and the layout to 150405 then this works fine:
func main() {
value := "20170228 050137"
layout := "20060102 150405"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
One way that I can think of to deal with this is to strip the milliseconds off from the end and then check the length and add a zero to the beginning if it needs one.
This seems like a pretty ugly solution and would involve doing something like this:
func main() {
date := "20170228"
timeString := "5013756"
value := date + prepareTime(timeString)
layout := "20060102150405"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
func prepareTime(time string) string {
if len(time) == 7 {
time = "0" + time
}
return time[:6]
}
Is there a way to do this without going through the above? Perhaps natively with the time package?
Assuming that you're pulling back 2 separate values from the DB, you can use fmt.Sprintf to 0 pad timeString. Combining it with the date string, you can use the following:
value := fmt.Sprintf("%s %08s", date, timeString)[:15]
In your code:
func main() {
date := "20170228"
timeString := "5013756"
value := fmt.Sprintf("%s %08s", date, timeString)[:15]
layout := "20060102 150405"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
Results:
2017-02-28 05:01:37 +0000 UTC
This approach is useful because it will also correctly pad any shorter value of time, e.g. 13756 will be converted to 00013756.
The fmt.Sprintf function is useful to format arguments into a string using the formatting you desire as specified by a format string and a list of arguments (...interface{}). The format string tells the function how to render the arguments.
This format string uses two items of note:
String verb (%s): The format string uses a variety of verbs that are used for string substitutions. %s is specifically to render a string or a slice. Other popular verbs include %d for base 10 integer and %f for float with a complete list in the docs. The %v verb is very useful can also be used here as it will render an argument's default value.
0 left padding: To 0 left pad an argument, use 0 followed by a length number in the verb after the %. This will prepended the argument with a maximum number of 0s specified in the length number. For example, %08s will render a string with up to 8 prepended zeros. This means a string "" will be "00000000" while a string "1234567" will result in "01234567". If the string is longer than the length, nothing will be prepended.
From the documentation:
%s the uninterpreted bytes of the string or slice
0 pad with leading zeros rather than spaces;
for numbers, this moves the padding after the sign
More detailed is available in the documentation: https://golang.org/pkg/fmt/

How to build a tick for loop without using the loop variable

I have a for loop that continually runs over an interval, however I am only using it as a function that I want running every 10 Minutes. How can I declare this for-loop without having to use 'x' somewhere inside of the loop
interval := time.Tick(10 * time.Minute)
for x := range interval {
...code that does not use x
}
I have tried restructuring the for loop but nothing results in it running without specifically using 'x', I know I could just simply do something with 'x' inside of the loop, but I would rather learn how to properly implement this for loop then make a hack.
Either
for {
<-time.After(someTime)
// ...
}
or
interval := time.Tick(someTime)
for ; ; <-interval { // First interval == 0
// ...
}
or
interval := time.Tick(someTime)
for {
<-interval
// ...
}
You can use _ to denote variables that you will ignore:
interval := time.Tick(10 * time.Minute)
for _ = range interval {
...
}
The spec says:
The blank identifier, represented by the underscore character _, may
be used in a declaration like any other identifier but the declaration
does not introduce a new binding.
How can I declare this for-loop without having to use 'x' somewhere inside of the loop
Starting go1.4 (Q4 2014), you will be able to do:
for range interval {
...
}
See go tip 1.4 doc:
as of Go 1.4 the variable-free form is now legal.
The pattern arises rarely but the code can be cleaner when it does.
Updating: The change is strictly backwards compatible to existing Go programs, but tools that analyze Go parse trees may need to be modified to accept this new form as the Key field of RangeStmt may now be nil.

Resources