Computing time/duration in Golang templates - go

I have a structure that gets rendered via template. e.g.:
type Foo struct {
Created time.Time
...
}
I pass this value to a template, and I'd like to this rendered see:
Created at 2022-11-22 9:50 (0d1h12m34s ago)
Displaying the timestamp (and formatting it) is easy enough, but I can't find a way to calculate the interval.
Created at {{.Created}} ({{???}} ago)
In go, this would be accomplished by time.Since(foo.Created) which returns a Duration, and then I can convert duration to string in various ways.
But doing the calculation in the template itself does not seem possible:
function "time" not defined
Or is it?
Can't find any information that explicitly tells me that time (or other arbitrary functions) are never ever allowed in templates. So I don't know if I'm just calling it wrong.
(I know I could create a new FooTemplateValue from a Foo add that field, so the template can render the duration as-is. I was just trying to avoid it if I can and use the actual object as-is).

You can register a custom template function using template.FuncMap, then you can invoke that function inside the template just like you would invoke a builtin function.
template.New("").Funcs(template.FuncMap{
"dur_until_now": func(t time.Time) time.Duration {
return time.Now().Sub(t)
},
}).Parse(`{{ dur_until_now .Created }}`)
https://go.dev/play/p/wM2f1oDFtDr
Or you can declare a custom time type and have that type implement a method that produces the desired value, then you can invoke that method inside the template directly on the field.
type MyTime struct{ time.Time }
func (t MyTime) DurUntilNow() time.Duration {
return time.Now().Sub(t.Time)
}
// ...
.Parse(`{{ .Created.DurUntilNow }}`)
https://go.dev/play/p/3faW4nTzK3-

Looks like you need to initialize the Created field with time.Now()
package main
import (
"fmt"
"time"
)
type Foo struct {
Created time.Time
// ...
}
func main() {
x := Foo{Created: time.Now()}
fmt.Println(time.Since(x.Created))
}

Related

go, how to invoke a func if i declare it with type only receiver?

Just be curious, how to invoke the below go func func (t) test() if i declare it with type only receiver, is it even valid? like: https://play.golang.org/p/k8QukaCZdUN
package main
import (
"fmt"
)
type t int
func (t) test() {
fmt.Println(233)
}
func main() {
fmt.Println(666666)
}
I tried to build and run it, seems ok... but:
What exactly is this func (t) test() and how to invoke it?
Is there any meaningful use case for this kind of declaration (i mean bare type as receiver)?
More context:
I was triggered to think about this bcoz i want to find an idiomatic/elegant way to expose all values of an enum(iota) to external packages, currently i am doing something like: https://play.golang.org/p/iD0Aq_Mn2sj
package main
import (
"fmt"
)
type Gender int
const (
Male Gender = iota + 1
Female
)
func (g Gender) All() [2]string {
return [...]string{"MALE", "FEMALE"}
}
func main() {
fmt.Println("All gender types:", Gender(0).All())
}
It works, so any Gender instance can invoke its All function to get all the Gender types, but it feels a bit weird, so i start thinking of whether it is possible to register a func on the type only, not the instances, so i can do: package.Gender.All(), is it possible in go?
btw, no global var.
is it even valid?
As shown by your example, yes.
What exactly is this func (t) test() and how to invoke it?
It's a normal method. You invoke it like a normal method. See:
var val t
val.test()
Is there any meaningful use case for this kind of declaration (i mean bare type as receiver)?
The main difference with this form of method is that you can't access the receiver (as it's unnamed). So it's effectively similar to a "static" method (although it's really not. you still need an instance to call it)
If you want to get all values of an enum, there's no reason to make it a type method. All it does is create an extra step to call it:
var g Gender // create a useless variable so we can call the method
g.All() // actually call the method
You might as well make a regular function for it.

Can I use an interface as a parameter when writing a function which could end up being passed any kind of struct?

I am trying to write a function which could end up taking any kind of struct... let's say it is like this :
func setDate(s timestamp, data interface{}){
data.Date = timestamp
}
I realize that I wouldn't need a function to set this value in real life, I am trying to learn more about how interfaces work, etc.
You could approach it that way, but then inside setDate() you would need to use reflection to set the Date field. Go is a statically typed language, so if the (static) type of data is interface{} (which says nothing about it), you can't really do anything useful with it (you can't refer to its Date field, because there is no guarantee that its value has a Date field).
Instead you should define a HasDate interface which contains a single method:
type HasDate interface {
SetDate(s time.Time)
}
The ability to set the date. And your function should expect a value of this interface type:
func setDate(s time.Time, data HasDate) {
data.SetDate(s)
}
Anyone who implements this HasDate interface can be passed to your setDate() function. Note that in Go implementing interfaces is implicit: there is no declaration of intent. This means any type that has a SetDate(time.Time) method implements this HasDate interface without even knowing this interface exists.
This is an example type that implements it (more precisely its pointer *MyType):
type MyType struct {
date time.Time
}
func (mt *MyType) SetDate(s time.Time) {
mt.date = s
}
Example testing it (try it on the Go Playground):
mt := &MyType{}
setDate(time.Now(), mt)
fmt.Println(mt.date)

How to cast interface{} back into its original struct?

I need a way to dynamically cast a struct/interface back to its original object.
I can add methods / functions inside. basically I need something like this:
MyStruct => Interface{} => MyStruct
When on the final conversion I don't know anything about the original struct besides what come inside the struct, so I can't just so:
a.(MyStruct)
You need to know at least the possible types it could be. There's a couple cases, 1. You think you might know what it is. 2. You have a list of possible types it could be, 3. Your code knows nothing about the underlying types.
If you think you know it, you can use type assertion to convert back to the original struct type.
...
package main
import (
"fmt"
)
type MyStruct struct {
Thing string
}
func (s *MyStruct) Display() {
fmt.Println(s.Thing)
}
type Thingable interface {
Display()
}
func main() {
s := &MyStruct{
Thing: "Hello",
}
// print as MyThing
s.Display()
var thinger Thingable
thinger = s
// print as thingable interface
thinger.Display()
// convert thinger back to MyStruct
s2 := thinger.(*MyStruct) // this is "type assertion", you're asserting that thinger is a pointer to MyStruct. This will panic if thinger is not a *MyStruct
s2.Display()
}
You can see this in action here: https://play.golang.org/p/rL12Lrpqsyu
Note if you want to test the type without panicking if you're wrong, do s2, ok := thinger.(*MyStruct). ok will be true if it was successful and false otherwise.
if you want to test your interface variable against a bunch of types, use a switch: (scroll to bottom)
...
package main
import (
"fmt"
"reflect"
)
type MyStruct struct {
Thing string
}
type MyStruct2 struct {
Different string
}
func (s *MyStruct) Display() {
fmt.Println(s.Thing)
}
func (s *MyStruct2) Display() {
fmt.Println(s.Different)
}
type Thingable interface {
Display()
}
func main() {
s := &MyStruct{
Thing: "Hello",
}
// print as MyThing
s.Display()
var thinger Thingable
thinger = s
// print as thingable interface
thinger.Display()
// try to identify thinger
switch t := thinger.(type) {
case *MyStruct:
fmt.Println("thinger is a *MyStruct. Thing =", t.Thing)
case *MyStruct2:
fmt.Println("thinger is a *MyStruct2. Different =", t.Different)
default:
fmt.Println("thinger is an unknown type:", reflect.TypeOf(thinger))
}
}
You can try that out here https://play.golang.org/p/7NEbwB5j6Is
If you really don't know anything about the underlying types, you'll have to expose the things you need through interface functions and call those. Chances are you can do this without knowing anything about the underlying type. If all else fails, you can use the reflect package to introspect your interface object and gather information about it. this is how the json package reads json text and returns populated structs—though this is an advanced topic and expect to sink a lot of time into it if you go this route. it’s best to hide reflection code inside a package with a clean interface(ie the package api).
No: as mentioned in this thread
Go is neither covariant nor contravariant. Types are either equal or they aren't.
You have to either take the structs apart and deal with the pieces, or use reflection.
Type assertions are only "assertions", not "coercions" of any kind.
See also this thread, which reminds us that:
A pointer is one kind of type.
A struct is another kind of type.
An integer is another kind of type.
A floating point number is another kind of type.
A boolean is another kind of type.
The principle of an interface concerns the methods attached to a type T, not what type T is.
An interface type is defined by a set of methods.
Any value that implements the methods can be assigned to an interface value of that type.
That would make the conversion from interface to concrete type quite difficult to do.

What different does the * (pointer symbol) makes in the following Go method?

I'm following this tutorial: https://github.com/astaxie/build-web-application-with-golang/blob/master/en/02.5.md.
I still don't understand pointers very well so this past confuses me a bit: func (h *Human) SayHi(). I tried removing the * and the output turned out to be exactly the same. Why is the * necessary in this case? Could someone give me an example of a different output with the code below?
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human // anonymous field
school string
}
type Employee struct {
Human
company string
}
// define a method in Human
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
The difference it does make is that the method will be defined on a pointer to a Human struct and all the methods in the struct will subsequently operate on the value pointed to.
If you were to loose the *, the method would operate on a copy of the struct you call the method on, so any write you'd do to the struct in the method would be useless for the code calling the method.
Since the method body only executes a fmt.Printf and prints some values, it does not really make a big difference if the method is defined on the pointer or not.
There are some cases where in the interest of concurrent-safety, you'd better not define methods on pointers, since this may lead to simultaneous access and writing of the underlying struct values.
There are two reasons to use a pointer receiver:
First, to avoid copying the value on each method call, more efficient if the value type is a large struct).
Second, the method can modify the value that its receiver points to.
So in your example, if you add one more dump method to change phone like this:
func (h Human) ChangePhone() {
h.phone = 'whatever'
}
The phone does not change after you call this method, that's why point * comes into play.

Go Redefined type formating and methods

Here is a basic go program
package main
import (
"fmt"
"time"
)
type myTime time.Time
func main() {
my := myTime(time.Now())
fmt.Println(my)
normal := time.Now()
fmt.Println(normal)
}
And the corresponding output
{63547112172 291468455 0x545980}
2014-09-23 23:36:12.292132305 +0000 UTC
I would like to know why myTime prints diffrently than time.Time. They basically are supposed to be from the same type... Also, if I try to access any method of time.Time, let's say, Day, it's available for "normal" but not for "my".
Thanks!
Your new type does not inherit methods from time.Time. To quote the spec:
The declared type does not inherit any methods bound to the existing type
Since there is no String method, it won't print a meaningful value. You need to implement that yourself.
Your other option is to embed time.Time in your own type. That way you can include the functionality of time.Time but also add your own functionality.
Playground link: http://play.golang.org/p/PY6LIBoP6H
type myTime struct {
time.Time
}
func (t myTime) String() string {
return "<Custom format here>"
}
func main() {
my := myTime{time.Now()}
fmt.Println(my)
normal := time.Now()
fmt.Println(normal)
}
fmt.Println uses the String() method (or rather the fmt.Stringer interface) when formatting a type as a string, if it is available. When you create a new type using an underlying type (in your case time.Time):
type myTime time.Time
You will not inherit the methodset of the underlying type. Therefore, myTime has no String() method, so fmt will use the default format for a struct.

Resources