Google Cloud Client Libraries for Go providing documentation, authentication patterns, and utility packages for civil time types and HTTP/gRPC recording/replay functionality
The civil package provides time-zone-independent representations of time following the proleptic Gregorian calendar. These types are useful when working with dates and times that should not be interpreted in any particular timezone, such as when storing calendar dates in databases or working with Google Cloud APIs like BigQuery.
import "cloud.google.com/go/civil"Civil time types represent dates, times, and datetimes without location information. They follow the rules of the proleptic Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second minutes. Because they lack timezone information, they do not represent unique moments in time - use time.Time for that purpose.
The package provides three main types:
Date: A calendar date (year, month, day)Time: A time of day with nanosecond precisionDateTime: A combination of date and timeAll types implement database/sql/driver.Valuer and database/sql.Scanner for easy database integration, as well as encoding.TextMarshaler and encoding.TextUnmarshaler for serialization.
type Date struct {
Year int // Year (e.g., 2014)
Month time.Month // Month of the year (January = 1, ...)
Day int // Day of the month, starting at 1
}A Date represents a calendar date without any timezone information. It does not describe a unique 24-hour timespan.
func DateOf(t time.Time) DateReturns the Date in which a time.Time occurs in that time's location.
Example:
now := time.Now()
today := civil.DateOf(now)
fmt.Printf("%d-%02d-%02d\n", today.Year, today.Month, today.Day)func ParseDate(s string) (Date, error)Parses a string in RFC3339 full-date format (YYYY-MM-DD) and returns the date value it represents.
Example:
date, err := civil.ParseDate("2024-01-15")
if err != nil {
log.Fatal(err)
}
fmt.Println(date) // 2024-01-15func (d Date) AddDays(n int) DateReturns the date that is n days in the future. n can be negative to go into the past.
Example:
today := civil.ParseDate("2024-01-15")
tomorrow := today.AddDays(1) // 2024-01-16
yesterday := today.AddDays(-1) // 2024-01-14
nextWeek := today.AddDays(7) // 2024-01-22func (d Date) AddMonths(n int) DateReturns the date that is n months in the future. n can be negative to go into the past.
Example:
date := civil.ParseDate("2024-01-15")
nextMonth := date.AddMonths(1) // 2024-02-15
lastMonth := date.AddMonths(-1) // 2023-12-15func (d Date) AddYears(n int) DateReturns the date that is n years in the future. n can be negative to go into the past.
Example:
date := civil.ParseDate("2024-01-15")
nextYear := date.AddYears(1) // 2025-01-15
lastYear := date.AddYears(-1) // 2023-01-15func (d Date) DaysSince(s Date) (days int)Returns the signed number of days between the date and s, not including the end day. This is the inverse operation to AddDays.
Example:
start := civil.ParseDate("2024-01-01")
end := civil.ParseDate("2024-01-15")
days := end.DaysSince(start) // 14func (d Date) Before(d2 Date) boolReports whether d occurs before d2.
func (d Date) After(d2 Date) boolReports whether d occurs after d2.
func (d Date) Compare(d2 Date) intCompares d and d2. Returns -1 if d is before d2, +1 if d is after d2, and 0 if they are equal.
Example:
date1 := civil.ParseDate("2024-01-15")
date2 := civil.ParseDate("2024-01-20")
if date1.Before(date2) {
fmt.Println("date1 is before date2")
}
cmp := date1.Compare(date2) // -1func (d Date) In(loc *time.Location) time.TimeReturns the time.Time corresponding to time 00:00:00 of the date in the specified location. Panics if loc is nil.
In is always consistent with time.Date, even when time.Date returns a time on a different day due to timezone transitions.
Example:
date := civil.ParseDate("2024-01-15")
loc, _ := time.LoadLocation("America/New_York")
t := date.In(loc) // 2024-01-15 00:00:00 -0500 ESTfunc (d Date) IsValid() boolReports whether the date is valid.
func (d Date) IsZero() boolReports whether date fields are set to their default value (zero values).
func (d Date) Weekday() time.WeekdayReturns the day of the week for the date.
Example:
date := civil.ParseDate("2024-01-15")
weekday := date.Weekday() // time.Mondayfunc (d Date) String() stringReturns the date in RFC3339 full-date format (YYYY-MM-DD).
func (d Date) MarshalText() ([]byte, error)Implements the encoding.TextMarshaler interface. The output is the result of d.String().
func (d *Date) UnmarshalText(data []byte) errorImplements the encoding.TextUnmarshaler interface. The date is expected to be a string in a format accepted by ParseDate.
func (d Date) Value() (driver.Value, error)Implements the database/sql/driver.Valuer interface.
func (d *Date) Scan(v any) errorImplements the database/sql.Scanner interface. Accepts time.Time, string, []byte, and Date types.
Example:
var date civil.Date
err := db.QueryRow("SELECT birth_date FROM users WHERE id = ?", userID).Scan(&date)type DateTime struct {
Date Date
Time Time
}A DateTime represents a date and time without location information. It does not describe a unique moment in time.
func DateTimeOf(t time.Time) DateTimeReturns the DateTime in which a time.Time occurs in that time's location.
Example:
now := time.Now()
dt := civil.DateTimeOf(now)
fmt.Printf("%v %v\n", dt.Date, dt.Time)func ParseDateTime(s string) (DateTime, error)Parses a string and returns the DateTime it represents. Accepts a variant of the RFC3339 date-time format that omits the time offset but includes an optional fractional time, as described in ParseTime. The accepted format is:
YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]where the 'T' may be a lower-case 't'.
Example:
dt, err := civil.ParseDateTime("2024-01-15T14:30:00")
if err != nil {
log.Fatal(err)
}func (dt DateTime) Before(dt2 DateTime) boolReports whether dt occurs before dt2.
func (dt DateTime) After(dt2 DateTime) boolReports whether dt occurs after dt2.
func (dt DateTime) Compare(dt2 DateTime) intCompares dt and dt2. Returns -1 if dt is before dt2, +1 if dt is after dt2, and 0 if they are equal.
func (dt DateTime) In(loc *time.Location) time.TimeReturns the time.Time corresponding to the DateTime in the given location. Panics if loc is nil.
If the time is missing or ambiguous at the location (e.g., during DST transitions), In returns the same result as time.Date.
Example:
dt := civil.DateTime{
Date: civil.Date{Year: 2024, Month: time.January, Day: 15},
Time: civil.Time{Hour: 14, Minute: 30},
}
loc, _ := time.LoadLocation("America/New_York")
t := dt.In(loc)func (dt DateTime) IsValid() boolReports whether the datetime is valid.
func (dt DateTime) IsZero() boolReports whether datetime fields are set to their default value.
func (dt DateTime) String() stringReturns the datetime in the format described in ParseDateTime.
func (dt DateTime) MarshalText() ([]byte, error)Implements the encoding.TextMarshaler interface. The output is the result of dt.String().
func (dt *DateTime) UnmarshalText(data []byte) errorImplements the encoding.TextUnmarshaler interface. The datetime is expected to be a string in a format accepted by ParseDateTime.
func (dt DateTime) Value() (driver.Value, error)Implements the database/sql/driver.Valuer interface.
func (dt *DateTime) Scan(v any) errorImplements the database/sql.Scanner interface.
type Time struct {
Hour int // The hour of the day in 24-hour format; range [0-23]
Minute int // The minute of the hour; range [0-59]
Second int // The second of the minute; range [0-59]
Nanosecond int // The nanosecond of the second; range [0-999999999]
}A Time represents a time of day with nanosecond precision, without location information. It does not describe a unique moment in time.
This type exists to represent the TIME type in storage-based APIs like BigQuery. Most operations on Time are unlikely to be meaningful outside this context. Consider using DateTime when you need both date and time information.
func TimeOf(t time.Time) TimeReturns the Time representing the time of day in which a time.Time occurs in that time's location. It ignores the date component.
Example:
now := time.Now()
timeOfDay := civil.TimeOf(now)
fmt.Printf("%02d:%02d:%02d\n", timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second)func ParseTime(s string) (Time, error)Parses a string and returns the time value it represents. Accepts an extended form of the RFC3339 partial-time format. After the HH:MM:SS part, an optional fractional part may appear, consisting of a decimal point followed by one to nine decimal digits (RFC3339 admits only one digit).
Example:
t, err := civil.ParseTime("14:30:45.123456789")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%02d:%02d:%02d.%09d\n", t.Hour, t.Minute, t.Second, t.Nanosecond)func (t Time) Before(t2 Time) boolReports whether t occurs before t2.
func (t Time) After(t2 Time) boolReports whether t occurs after t2.
func (t Time) Compare(t2 Time) intCompares t and t2. Returns -1 if t is before t2, +1 if t is after t2, and 0 if they are equal.
func (t Time) IsValid() boolReports whether the time is valid.
func (t Time) IsZero() boolReports whether time fields are set to their default value.
func (t Time) String() stringReturns the time in the format described in ParseTime. If Nanosecond is zero, no fractional part will be generated. Otherwise, the result will end with a fractional part consisting of a decimal point and nine digits.
func (t Time) MarshalText() ([]byte, error)Implements the encoding.TextMarshaler interface. The output is the result of t.String().
func (t *Time) UnmarshalText(data []byte) errorImplements the encoding.TextUnmarshaler interface. The time is expected to be a string in a format accepted by ParseTime.
func (t Time) Value() (driver.Value, error)Implements the database/sql/driver.Valuer interface.
func (t *Time) Scan(v any) errorImplements the database/sql.Scanner interface.
package main
import (
"database/sql"
"fmt"
"log"
"time"
"cloud.google.com/go/civil"
_ "github.com/mattn/go-sqlite3"
)
func main() {
// Working with dates
today := civil.DateOf(time.Now())
fmt.Printf("Today: %s\n", today)
tomorrow := today.AddDays(1)
fmt.Printf("Tomorrow: %s\n", tomorrow)
// Date arithmetic
nextWeek := today.AddDays(7)
daysSince := nextWeek.DaysSince(today)
fmt.Printf("Days until next week: %d\n", daysSince)
// Date comparison
if today.Before(tomorrow) {
fmt.Println("Today is before tomorrow")
}
// Working with times
now := time.Now()
timeOfDay := civil.TimeOf(now)
fmt.Printf("Time: %02d:%02d:%02d\n",
timeOfDay.Hour, timeOfDay.Minute, timeOfDay.Second)
// Working with datetimes
dt := civil.DateTime{
Date: today,
Time: timeOfDay,
}
fmt.Printf("DateTime: %s\n", dt)
// Converting to time.Time
loc, _ := time.LoadLocation("America/New_York")
specificTime := dt.In(loc)
fmt.Printf("Specific time: %v\n", specificTime)
// Parsing
parsedDate, _ := civil.ParseDate("2024-12-25")
fmt.Printf("Christmas: %s (weekday: %s)\n",
parsedDate, parsedDate.Weekday())
// Database integration
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`CREATE TABLE events (
id INTEGER PRIMARY KEY,
event_date TEXT,
event_time TEXT
)`)
if err != nil {
log.Fatal(err)
}
// Insert using civil types
_, err = db.Exec("INSERT INTO events (event_date, event_time) VALUES (?, ?)",
today, timeOfDay)
if err != nil {
log.Fatal(err)
}
// Query using civil types
var retrievedDate civil.Date
var retrievedTime civil.Time
err = db.QueryRow("SELECT event_date, event_time FROM events WHERE id = 1").
Scan(&retrievedDate, &retrievedTime)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Retrieved: %s at %s\n", retrievedDate, retrievedTime)
}Install with Tessl CLI
npx tessl i tessl/golang-cloud-google-com--go