
Getting started with Go's time functions can be a bit overwhelming, especially if you're new to programming.
The Go standard library provides a range of time functions that make it easy to work with dates and times.
One of the most useful time functions is the `time.Now()` function, which returns the current local time. This function is often used to get the current date and time for logging or other purposes.
To use the `time.Now()` function, you simply need to import the `time` package and call the function. For example: `import "time"; t := time.Now()`.
Working with time zones can be a bit tricky, but Go's time functions make it easy to handle different time zones.
Time
The Time type in Go is a powerful tool for working with dates and times. It represents an instant in time with nanosecond precision.
A Time value can be used by multiple goroutines simultaneously, but certain methods like Time.GobDecode and Time.UnmarshalBinary are not concurrency-safe. This means you need to be careful when using these methods in multi-threaded programs.
You can compare Time values using the Before, After, and Equal methods. These methods allow you to determine if one Time is before, after, or equal to another. The Sub method subtracts two Time values, producing a Duration, while the Add method adds a Time and a Duration, producing a new Time.
The zero value of a Time is January 1, year 1, 00:00:00.000000000 UTC, which is unlikely to come up in practice. To detect an uninitialized Time, you can use the IsZero method.
Each Time value has an associated Location, which can be set using the Local and UTC methods. These methods return a new Time with the specified Location, but they do not change the actual instant represented by the Time.
If you need to use Time values as map or database keys, you need to guarantee that the identical Location has been set for all values. This can be achieved by using the UTC or Local method and stripping the monotonic clock reading by rounding to 0.
On a similar theme: Golang Use Cases
Instantiation and Exercise
Go instantiates a version of Identity that takes a string parameter and returns a string result, which is just a plain Go function we could have written ourselves.
This means we don't need to supply a separate version of Identity for each concrete type we want to use, but instead, we can write it once for some arbitrary type T, and Go will automatically generate a version of Identity for each type that's actually used in our program.
In the example code, a generic function for maps containing either integer or float values is added. This allows us to write one function instead of two, which is a key benefit of using generics in Go.
To exercise this new generic function, we initialize two maps, one for integer values and one for float values, and then use them as arguments when calling the function. The result is printed to the console using fmt.Printf.
Here's a summary of the steps involved in adding a generic function:
Instantiating Identity
You can instantiate a function in Go that takes a string parameter and returns a string result, just like the Identity function. This is a plain function that doesn't require a separate version for each concrete type.
Go will automatically generate a version of the Identity function for each type that's actually used in your program. This means you don't need to write a separate Identity function for each type of value.
The Identity function is useful because it simply returns whatever value you pass it, without modifying it. This is where generic programming comes in handy.
Using a type parameter, you can specify that both the parameter and the function's result must be of the same concrete type. This is done using a type parameter like T, where T can be any type.
This approach is more satisfactory than using any, as it ensures that the parameter and result are of the same type. For example, if you pass an int to the Identity function, you expect to get an int back.
Running the Test

Running the test is a crucial step in this exercise. You can run the test using your editor or the go test command.
The test will fail because the required function doesn't exist yet. You'll need to define a generic function in the print package named PrintAnythingTo.
This function should take one parameter of type io.Writer and another value of some unspecified type. The type parameter T can be any type.
To make the test pass, you'll need to write the supplied value to the supplied writer. You might like to use fmt.Fprintln, like the PrintTo example in the previous tutorial.
The necessary go.mod and print.go files are already set up for you. All you need to do is edit print.go and add the PrintAnythingTo function.
You can use the [T any] syntax to define the PrintAnythingTo function. This says that PrintAnything is about some arbitrary type T and takes a parameter of that type.
Sleep

Sleep is a crucial function in programming, especially when working with goroutines. It allows you to pause the current goroutine for at least the duration specified.
A negative or zero duration causes Sleep to return immediately, so use it wisely.
New Ticker
The New Ticker function is a great tool to have in your Go toolkit. It returns a new Ticker containing a channel that sends the current time on the channel after each tick.
The period of the ticks is specified by the duration argument, which must be greater than zero; otherwise, NewTicker will panic. This is a crucial detail to keep in mind when working with New Ticker.
Before Go 1.23, the garbage collector didn't recover tickers that had not yet expired or been stopped, so it was common to immediately defer t.Stop after calling NewTicker to make the ticker recoverable. This was a necessary step to prevent memory leaks.
As of Go 1.23, the garbage collector can recover unreferenced tickers, even if they haven't been stopped. This change makes the Stop method less necessary, but it's still a good idea to call Stop to stop the ticker for other reasons.
Recommended read: Golang Recover
Source Files
In object-oriented programming, source files are where the magic happens. They contain the code that defines the classes, methods, and variables used to create objects.
A source file typically has a .java extension and contains the class definition, including the class name, methods, and variables. For example, in the example code, the source file MyObject.java defines a class called MyObject.
The source file is where you write the code that will be compiled into a .class file. The compiler reads the source file and translates it into bytecode that can be executed by the Java Virtual Machine (JVM).
The source file is also where you can use import statements to bring in other classes and libraries that your class needs to function. This is done using the import keyword, followed by the name of the class or library you want to import.
Write The Code
To write the code, start by creating a file called main.go in the generics directory. This is where you'll write your Go code.
You might enjoy: Golang Go
You'll need to paste the package declaration at the top of the file, which is "package main". This indicates that the program is standalone and not a library.
Next, paste the function declarations for SumInts and SumFloats. These functions take a map as an argument and return the sum of its values.
The main function initializes two maps, one for integers and one for floats, and uses them as arguments when calling the SumInts and SumFloats functions.
To support this code, you'll need to import the "fmt" package, which you can add just beneath the package declaration.
Here's a summary of the code:
Composite and Functions
Composite types in Go allow for more flexibility in function declarations. We can declare parameters of type T itself or composite types involving T, such as a slice of T.
A composite type can also be a channel of some element type E, which enables communication between goroutines. This is a powerful tool for building concurrent programs.
We can even write variadic functions that take a variable number of channels of E. This allows for a flexible and dynamic way to pass data between functions.
Return

In Go, the return value of functions like NewTicker and Tick can be a channel that sends the current time or a duration.
NewTicker returns a channel that sends the current time after each tick, with a period specified by the duration argument.
The duration d must be greater than zero; if not, NewTicker will panic.
The period of the ticks is adjusted by the ticker to make up for slow receivers.
As of Go 1.23, the garbage collector can recover unreferenced tickers, even if they haven't been stopped.
Tick is a convenience wrapper for NewTicker providing access to the ticking channel only.
Unlike NewTicker, Tick will return nil if d <= 0.
Before Go 1.23, the underlying Ticker would never be recovered by the garbage collector, and code should use NewTicker instead and call Ticker.Stop when the ticker is no longer needed.
As of Go 1.23, the Stop method is no longer necessary to help the garbage collector.
Exercise: Hello, Generics

Let's start by checking out a copy of the GitHub repo for the Know Go book: https://github.com/bitfield/know-go. This will give us access to the exercises and code we'll be working with.
The first exercise is to write a generic function in Go. To do this, we need to open the exercises/print folder in our code editor and take a look at the print_test.go file. This file contains a test that we'll use as a starting point.
The test is designed to help us define a generic function that takes a parameter of type T. This type T is a placeholder for any type we want to use with our function. By using generics, we can write functions that work with multiple types without having to duplicate code.
To get started, we need to make sure we understand how to define a generic function that takes a parameter of type T. We can do this by looking at the print_test.go file and seeing how the test is structured.
Explore further: Simple Http Server Golang Github
Composite

Composite types are not limited to just the type T itself, but can also involve composite types, such as a slice of T.
You can use a type parameter in other kinds of composite types, like a channel of some element type E.
A generic function can take a variable number of channels of E, making it a variadic function.
Defining your own constraint types is fundamental to building a generic collection type, which is one of the most prominent uses of generics.
To combine a built-in constraint like comparable with your own generic type, you need to read the syntax necessary, which can be found in the change proposal.
The language specification has a very useful table of constraint satisfaction examples, which can be a lifesaver when working with generics.
Trying to write a generic collection type in Go can be frustrating, especially when you can't find any working examples anywhere, including the official Go documentation.
Functions

Functions are a fundamental concept in Go programming, and understanding how to write them is crucial for building robust and efficient code.
You can define a generic function in Go that works with multiple types by using type parameters. Type parameters are declared in the function signature and can be constrained to specific types.
A type constraint is used to specify the permissible type arguments for a type parameter. In the case of the `SumIntsOrFloats` function, the constraint allows either integer or float types.
By using type parameters and constraints, you can write a single function that can work with different types, making your code more reusable and maintainable.
Here's an example of how to write a generic function that sums the values of a map with either integer or float values:
```go
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {

s += v
}
return s
}
```
This function takes two type parameters, `K` and `V`, where `K` is a comparable type and `V` is either an `int64` or a `float64`. The function then sums the values of the map using the `+=` operator, which is allowed for both integers and floats.
You can call this function with type arguments to specify the types of the map values. For example:
```go
fmt.Printf("Generic Sums: %v and %v
",
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))
```
In this example, the `SumIntsOrFloats` function is called with type arguments `string, int64` and `string, float64`, which tells the compiler to use `string` as the type for the map keys and `int64` or `float64` as the type for the map values.
If this caught your attention, see: S Golang
Secrets of Generic Constraints
Declaring type constraints is fundamental to building a generic collection type. This is something that none of the top 20 search results for "Golang generics" mention, at all.
Type constraints specify the permissible type arguments that calling code can use for a type parameter. For example, a type constraint can allow either integer or float types.
You can declare a type constraint by using the syntax `int64 | float64`, which represents the union of integers and floats. This can be used as a type constraint for a type parameter, enabling it to work with arguments of different types.
A type parameter must support all the operations the generic code is performing on it. For example, if your function's code were to try to perform string operations on a type parameter whose constraint included numeric types, the code wouldn't compile.
Here are some examples of type constraints:
- `int64 | float64`: allows either integer or float types
- `comparable; E`: allows types that are comparable and implement interface E
Note that type constraints can be used to declare what types a generic function supports, and what types can be used as arguments for a type parameter.
Parsing and Formatting
Parsing and formatting are crucial tasks when working with time in Go. You can use the ParseDuration function to parse a duration string, which is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms" or "-1.5h".
Valid time units include "ns", "us", "ms", "s", "m", and "h". This is useful for tasks like converting between different time units or validating user input.
To format a time value, you can use the Format method, which returns a textual representation of the time value formatted according to the layout defined by the argument. This is demonstrated in the executable example for Time.Format, which shows the working of the layout string in detail.
ParseDuration
The ParseDuration function is a powerful tool for parsing duration strings. 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". This means you can use ParseDuration to parse a wide range of duration strings, from nanoseconds to hours.
To add a duration to a time, you can use the Add method, which returns the time t+d. This is useful for calculating the end time of an event. For example, if you have a start time and a duration, you can use Add to calculate the end time.
Format
The Format method returns a textual representation of the time value formatted according to the layout defined by the argument. This method is a good way to format time values for display.
You can use the Format method to format time values in a variety of ways, such as using the RFC 3339 format, which is a standard format for representing dates and times. The RFC 3339 format includes sub-second precision, making it suitable for many applications.
The Format method is similar to the AppendFormat method, but it does not append the formatted string to a buffer. Instead, it returns the formatted string. This makes it easier to use the formatted string in other parts of your program.
The Format method is also similar to the AppendText method, but it returns the formatted string instead of appending it to a buffer. The AppendText method is used to implement the encoding.TextAppender interface, which is used to format time values in a specific way.
In general, the Format method is a good choice when you need to format time values for display or other purposes. It is easy to use and provides a lot of flexibility in terms of the format you can use.
The Layout string is used to define the format of the time value. This string can include a variety of format specifiers, such as %d for the day of the month and %H for the hour. You can use these format specifiers to customize the format of the time value.
For example, you can use the following Layout string to format the time value in the format "Monday, January 1, 2006 3:04:05 PM": "Monday, January 2, 2006 3:04:05 PM". The Format method will return this string when given a time value and this Layout string.
The Format method is a powerful tool for formatting time values. It provides a lot of flexibility and is easy to use. With a little practice, you can use the Format method to format time values in a variety of ways.
UnixNano

The UnixNano function returns a Time as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC. This function is defined in the Go standard library.
The result of calling UnixNano on a Time is a Unix time in nanoseconds. However, the result is undefined if the Unix time in nanoseconds cannot be represented by an int64, which is a date before the year 1678 or after 2262.
The result of calling UnixNano does not depend on the location associated with the Time.
Time Zone Bounds
Time Zone Bounds can be tricky to wrap your head around, but don't worry, it's simpler than it sounds.
The ZoneBounds function returns the bounds of the time zone in effect at a given time t.
If a time zone begins at the start of time, the start time will be returned as a zero Time, which is a convenient way to indicate the beginning of time.
The end time will be returned as a zero Time if the time zone goes on forever.
The Location of the returned times will be the same as the original time t.
Remove Unused Args

Removing unused arguments from your code can make it more efficient and easier to read. This is especially true for generic functions, where you might be tempted to include type arguments even if they're not necessary.
The Go compiler can often infer the types you want to use, so you can omit type arguments in calling code. However, this isn't always possible, especially if the function has no arguments.
In some cases, you might need to include type arguments in the function call, but in general, it's a good practice to remove them when they're not needed. This will make your code cleaner and more maintainable.
String and Location
The String and Location section of t golang is where things get really interesting. The String method for the Location type returns a descriptive name for the time zone information, corresponding to the name argument to LoadLocation or FixedZone.
You can use this method to get a human-readable representation of a location's time zone. For example, if you have a location with the name "America/New_York", calling Location.String() would return a string like "America/New_York (EDT)".
The Location method for the Time type returns the time zone information associated with a given time. This is useful if you want to know the time zone of a specific time.
Suggestion: Golang Method
Parse In Location
Parse In Location is a function that differs from Parse in two key ways.
In the absence of time zone information, Parse In Location interprets a time as in the given location. This means that if you don't specify a time zone, it will automatically assume the time is in the location you provide.
Parse In Location uses the given location to interpret the time, whereas Parse tries to match it against the Local location.
This function is useful when you need to work with times in specific locations.
String (*Location)
The String function for a Location object is quite useful. It returns a descriptive name for the time zone information, corresponding to the name argument to LoadLocation or FixedZone.
This descriptive name is exactly what it sounds like - a string that describes the time zone information. If you've loaded a location using LoadLocation or FixedZone, calling String on that location will give you a string that describes the time zone.
You can use this function to get a human-readable representation of the time zone, which can be helpful for debugging or logging purposes. For example, if you've loaded a location for New York, calling String on that location might return "America/New_York".
Stop
The Stop function is a crucial part of working with t in Go. It turns off a ticker, preventing any more ticks from being sent.
After calling Stop, no more ticks will be sent, but the channel remains open to prevent a concurrent goroutine from seeing an erroneous "tick".
The Stop function for timers is a bit more complex. It prevents the timer from firing, but it doesn't wait for any function that may have been started by the timer to complete.
If you're using a func-based timer, calling Stop returns true if it stops the timer and false if the timer has already expired or been stopped. If Stop returns false, the function started by the timer has been started in its own goroutine, and you'll need to coordinate with it explicitly to know whether it's completed.
For chan-based timers, as of Go 1.23, any receive from the timer's channel after Stop has returned is guaranteed to block rather than receive a stale time value from before the Stop. If you're using an older version of Go, you'll need to insert an extra receive from the channel to drain any potential stale value.
Readers also liked: Golang Timer
Frequently Asked Questions
What is T in Golang?
In Go, T is a type parameter used to create generic functions, allowing code to work with multiple data types. It enables the development of reusable and flexible code.
What is the Golang used for?
Go, also known as Golang, is used for cloud and server-side applications, DevOps, and command line tools, among other high-performance tasks. It's a versatile language that's ideal for building scalable and efficient software solutions.
Does Uber still use Golang?
Yes, Uber still uses Golang to support its large-scale microservices architecture. With over 2,000 microservices and 46 million lines of Go code, Golang remains a crucial part of Uber's technology stack.
Featured Images: pexels.com


