Golang如何仅显示日期而不显示时间
Golang如何仅显示日期而不显示时间 我正在寻找用SQL值填充Go模板的方法。目前我有三种方案,各有优缺点。然而,日期显示方式有些奇怪。
在PGAdmin中,日期正确显示为 2020-08-26 使用标准sql库,日期显示为 2020-08-26 00:00:00 +0000 +0000 使用sqlx,日期显示为 2020-08-26T00:00:00Z 使用json,日期正确显示为 2020-08-26
或者,Go结构体中是否没有日期格式?
1. 标准SQL库 标准sql库是惯用的方法,但你必须重复定义结构体两次。
2. Sqlx Sqlx是标准库的扩展,提供了"StructScan"功能,使其更加通用。我认为这仍然是一种惯用的方式。
3. JSON实验 这是最高级别的DRY(不要重复自己),但我认为这是一个死胡同。因为json_agg()函数无法与连接查询等一起工作。不过,在某些情况下我仍然认为它是一个选项。
我的问题
- 如何去除方案1和2中的时间部分(“T00:00:00Z”)?有没有办法在结构体中使用类似time.Date(YYYY-MM-DD)的方式?
- 是否有更多方法来实现这个目标?更简单或更好的方法?
更多关于Golang如何仅显示日期而不显示时间的实战教程也可以访问 https://www.itying.com/category-94-b0.html
skillian:
我不确定这对你是否有帮助
我不知道如何将 SQL查询 格式化为不带时间戳的ISO标准。
更多关于Golang如何仅显示日期而不显示时间的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Sibert:
在我看来,Go 语言缺少一个标准的日期库。
Google Cloud Go 日期包:Package civil。
我不确定这对你是否有帮助,但这里我将演示如何拥有一个 time.Time 结构体字段,但在模板中我希望使用特定的格式:https://play.golang.org/p/LqCTJ41xLr_1
func main() {
fmt.Println("hello world")
}
petrus:
代码可以编译,但我无法测试它。
谢谢!它按预期工作了。我自己永远也搞不明白这个。字符串 + 时间 🙂
// 此处假设原始HTML的<div class="post">...</div>中可能包含代码,但提供的HTML片段中没有显式的代码块。
// 根据指令,如果后续内容中有Go代码,应放置在此代码块内。
为什么你要求SQL查询不包含时间戳?我在这里修改了我的示例,在GetModel函数中包含了小时、分钟和秒,以展示如何将记录加载到使用sqlx或database/sql的切片中,但由于我正在格式化输出,显示时并不包含那个时间。如果你试图用SQL来实现,你可以在SQL中转换数据类型。如果你使用的是MS SQL Server,你可以使用cast(hr_date as date)或convert(date, hr_date)等。
我从你在 play.golang.org 上的示例中看到了如何从 SQL 中获取数据,但你是如何准备数据以供查看的呢?你是让默认的 time.Time.String 函数来格式化它吗?我想你需要的可能是 time.Time.Format,它允许你指定想要的精确格式(例如 hr := get().([]Hr)[0]; hr.Date.Format("2006-01-02");)。
hr := get().([]Hr)[0]; hr.Date.Format("2006-01-02");
petrus:
Google Cloud Go 日期包:Package civil。
但我该如何使用它呢?文档中没有结构体的示例。
type Hr struct {
Date civil.Date `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
错误:*扫描第 0 列索引时出错,列名 “hr_date”:不支持扫描,将 driver.Value 类型 time.Time 存储到类型 civil.Date
skillian:
time.Time.Format允许你指定想要的精确格式(例如hr := get().([]Hr)[0]; hr.Date.Format("2006-01-02");)。
我尝试了以下方法(使用sqlx):
list := []Hr{}
for rows.Next() {
row := Hr{}
hr := get().([]Hr)[0]
hr.Date.Format("2006-01-02")
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
}
list = append(list, row)
}
但是出现了一个错误:hr.Date.Format undefined (type string has no field or method Format)
skillian:
你是如何使用这些
Hr结构的?
说实话,我也不知道自己在做什么。我多年来学到的所有知识在 Go 中都不适用。对我来说,结构体是对表中列类型的描述,至少应具备基本的格式化能力,比如短日期或日期+时间格式。有一个库
GitHub - rickb777/date: 一个用于处理日期的 Go 包
看起来能实现我想要的功能,但我还没能让它正常工作。而且我不知道它是否能作为结构体的列类型使用。
在我看来,Go 缺少一个标准日期库,以便在结构体中使用。
skillian:
那么当你想要显示它时,问题就出现了
我认为 Go 不应该改变来自数据库的值。数据库已经将数据转换为正确的格式。为什么 Go 还要改变这些值呢?
你是如何使用这些 Hr 结构的?它们仅用于显示目的吗(例如,它们只是你网站上该表格的模型)?如果是这样,那么你可以为 Date 字段使用 string 类型。如果你有任何使用该 Date 字段的代码(例如,按特定日期筛选),那么你可能应该使用 time.Time 类型。
如果你使用 time.Time 类型,那么你就不需要解析时间;(*sqlx.Rows).StructScan 会处理将值读取为“时间类型”(例如 date、datetime、datetime2,无论 RDBMS 返回什么)。这样一来,你的问题就变成了:当你想要显示它时,如果你不想要从 time.Time.String 得到的默认长格式输出,你就必须调用 time.Time.Format 并提供一个格式字符串。
skillian:
但是你是如何准备它以供查看的呢?
type Hr struct {
Date time.Time
Sign string
Code string
Qty float64
Sum float64
}
我应该如何准备?我认为通过在结构体中设置类型来准备它是合乎逻辑的。还是我想错了?
“结构体”的目的是什么?难道不是用来设置列的类型吗?Go 中没有“日期”列吗?
skillian:
我想你正在寻找
time.Time.Format,它允许你指定你想要的精确格式(例如hr := get().([]Hr)[0]; hr.Date.Format("2006-01-02");)。
那么我如何以及何时在查询中实现 time.Time.Format?是在查询中,还是在循环 for rows.Next() 时?我如何用 sqlx 实现这个?还是我应该把它放在结构体里?
尝试使用一个辅助数据结构
HrDB。
type HrDB struct {
Date time.Time `db:"hr_date"`
Hr
}
type Hr struct {
Date string
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
有没有办法让这个结构更“通用”一些?例如,在 Hr 结构体下创建一个自定义的“ISO”类型?我知道“泛型”在 Go 里是个敏感词,但我只是好奇。伪代码如下:
type ISO func () {
Date.Format("2006-01-02")
}
type Hr struct {
Date *ISO `db:"hr_date"` // 通用的自定义类型
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
是的,这个方法有效。谢谢!
但是如果结构体字段是
time.Time类型,我该如何格式化呢?下面的代码不起作用。
type Hr struct {
Date time.Time `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
func get() interface{} {
rows, err := db.Queryx("SELECT *::text FROM hr")
if err != nil {
log("getsql error" + err.Error())
}
defer rows.Close()
list := []Hr{}
for rows.Next() {
row := Hr{}
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
}
time.Parse("2006-01-02", row.Date)
list = append(list, row)
}
return (list)
}
错误信息:cannot use row.Date (type time.Time) as type string in argument to time.Parse
谷歌云 Go 日期包:Package civil。
但是我该如何使用它呢?
type HrDB struct {
Date time.Time `db:"hr_date"`
Hr
}
type Hr struct {
Date civil.Date
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
list := []Hr{}
for rows.Next() {
row := HrDB{}
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
}
row.Hr.Date = civil.DateOf(row.Date)
list = append(list, row.Hr)
}
return (list)
要从 Hr.Date 字段获取 ISO 格式的日期 (2020-08-31),请使用 String() 方法。例如:
isoDate = row.Hr.Date.String()
Sibert:
我尝试了以下代码(使用sqlx):
list := []Hr{} for rows.Next() { row := Hr{} hr := get().([]Hr)[0] hr.Date.Format("2006-01-02") err := rows.StructScan(&row) if err != nil { log(err.Error()) } list = append(list, row) }但出现错误:hr.Date.Format undefined (type string has no field or method Format)
在你的sqlx示例中,Hr.Date 是一个 string 类型。
Sibert:
Go Playground - The Go Programming Language http://94.237.92.101:5052/
type Hr struct {
Date string `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
list := []Hr{}
for rows.Next() {
row := Hr{}
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
continue
}
if len(row.Date) >= 10 {
row.Date = row.Date[:10]
}
list = append(list, row)
}
return (list)
如何格式化
time.Time类型的结构体?这个方法不起作用。
type Hr struct {
Date time.Time `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
func get() interface{} {
rows, err := db.Queryx("SELECT *::text FROM hr")
if err != nil {
log("getsql error" + err.Error())
}
defer rows.Close()
list := []Hr{}
for rows.Next() {
row := Hr{}
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
}
time.Parse("2006-01-02", row.Date)
list = append(list, row)
}
return (list)
}
错误:无法在 time.Parse 的参数中将 row.Date(类型为 time.Time)用作字符串类型
尝试使用一个辅助数据结构 HrDB。
type HrDB struct {
Date time.Time `db:"hr_date"`
Hr
}
type Hr struct {
Date string
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
list := []Hr{}
for rows.Next() {
row := HrDB{}
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
}
row.Hr.Date = row.Date.Format("2006-01-02")
list = append(list, row.Hr)
}
return (list)
代码可以编译,但我无法测试它。
为什么我想要不带时区的“2006-01-02”? 正如有人指出的那样——出生日期不带毫秒看起来更美观。所以这主要是一个显示问题。由于PostgreSQL的输出是“2006-01-02”,我希望保持这种格式,而不给出生日期添加毫秒。根据维基百科,唯一使用MM/DD/YYYY系统的国家是美国、菲律宾、帕劳、加拿大和密克罗尼西亚。因此,ISO 8601是一个应该被支持的全球标准。
我的新手结论: 无论对错。Go语言无法一步到位地支持ISO 8601短日期格式。这个结论基于以下来源得出:
https://golang.org/search?q=iso+8601 https://godoc.org/?q=iso+8601
time包应该有一些ISO 8601日期和日期/时间格式
我认为time包应该有一些ISO 8601日期和日期/时间格式,例如: ISO8601Date = “2006-01-02” ISO8601DateTime = “2006-01-02T15:04:05Z”
https://golang.org/search?q=“2006-01-02”
ISO 8601可以通过几种变通方法得到支持。但我的经验是,每一行额外的代码都会消耗性能并增加执行时间。或者使其更难阅读和维护。
那么RFC3339呢? RFC3339应该与ISO 8601相同(我认为),但在结构体中仍然无法使用它
type Hr struct {
Date time.RFC3339 `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
错误:time.RFC3339 不是一个类型
变通方法1:双重结构体 这是一个克服缺乏ISO支持的绝妙方法。
type HrDB struct {
Date time.Time `db:"hr_date"`
Hr
}
type Hr struct {
Date string
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
然而,这并不能消除对所有涉及日期的解析。你必须专门处理每一个涉及的日期列。
time.Parse("2006-01-02", row.Date)
time.Parse("2006-01-02", row.Edit) 等等
变通方法2:使用字符串 这可能是处理数百个结构体的最简单方法。但仍然不简单。
type Hr struct {
Date string `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
list := []Hr{}
for rows.Next() {
row := Hr{}
err := rows.StructScan(&row)
if err != nil {
log(err.Error())
continue
}
row.Date = row.Date[:10]
list = append(list, row)
}
return (list)
变通方法3:在PostgreSQL中转换 在PostgreSQL中转换更简单,但有局限性:
"SELECT hr_date::text, hr_sign, hr_code, hr_qty, hr_sum FROM hr"
转换所有列将不起作用
"SELECT *::text FROM hr"
我在寻找什么? 我正在寻找一种方法,可以让我使用结构体作为具有有限格式化能力的“列类型”。即一步到位的解决方案。我见过许多非标准库声称应该以这种方式工作,但我没有一个能让它们工作起来。
type Hr struct {
Date date.ISO8601 `db:"hr_date"`
Sign string `db:"hr_sign"`
Code string `db:"hr_code"`
Qty float64 `db:"hr_qty"`
Sum float64 `db:"hr_sum"`
}
我感谢所有收到的有用回答,并且我发现来自petrus的“字符串解决方案”是最接近且最简单的。尽管这不是一个新手所设想的方式
在Go中处理日期格式时,确实需要手动处理时间部分的格式化。以下是针对你问题的解决方案:
1. 自定义类型处理日期
import (
"database/sql"
"encoding/json"
"time"
)
// 自定义日期类型
type Date struct {
time.Time
}
// 实现sql.Scanner接口
func (d *Date) Scan(value interface{}) error {
if value == nil {
d.Time = time.Time{}
return nil
}
switch v := value.(type) {
case time.Time:
d.Time = v
case []byte:
t, err := time.Parse("2006-01-02", string(v))
if err != nil {
return err
}
d.Time = t
case string:
t, err := time.Parse("2006-01-02", v)
if err != nil {
return err
}
d.Time = t
default:
return fmt.Errorf("unsupported type: %T", v)
}
return nil
}
// 实现driver.Valuer接口
func (d Date) Value() (driver.Value, error) {
if d.IsZero() {
return nil, nil
}
return d.Format("2006-01-02"), nil
}
// 自定义JSON序列化
func (d Date) MarshalJSON() ([]byte, error) {
if d.IsZero() {
return []byte("null"), nil
}
return []byte(`"` + d.Format("2006-01-02") + `"`), nil
}
// 自定义JSON反序列化
func (d *Date) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
d.Time = time.Time{}
return nil
}
str := string(data)
if len(str) > 2 && str[0] == '"' && str[len(str)-1] == '"' {
str = str[1 : len(str)-1]
}
t, err := time.Parse("2006-01-02", str)
if err != nil {
return err
}
d.Time = t
return nil
}
// 在结构体中使用
type User struct {
ID int `db:"id"`
Name string `db:"name"`
BirthDate Date `db:"birth_date"`
}
2. 使用模板函数格式化日期
import (
"html/template"
"time"
)
// 创建模板函数
func formatDate(t time.Time) string {
return t.Format("2006-01-02")
}
// 在模板中使用
tmpl := template.New("").Funcs(template.FuncMap{
"formatDate": formatDate,
})
// 模板内容
const tmplStr = `
{{range .Users}}
<div>
<h3>{{.Name}}</h3>
<p>出生日期: {{formatDate .BirthDate}}</p>
</div>
{{end}}
`
3. 使用sqlx的StructScan配合自定义类型
import (
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
type User struct {
ID int `db:"id"`
Name string `db:"name"`
BirthDate Date `db:"birth_date"`
}
func getUsers(db *sqlx.DB) ([]User, error) {
var users []User
err := db.Select(&users, "SELECT id, name, birth_date FROM users")
if err != nil {
return nil, err
}
return users, nil
}
4. 使用标准库的格式化方法
// 在需要显示的地方直接格式化
func displayUser(user User) {
fmt.Printf("姓名: %s\n", user.Name)
fmt.Printf("日期: %s\n", user.BirthDate.Format("2006-01-02"))
}
// 或者在模板中直接使用Format方法
// {{.BirthDate.Format "2006-01-02"}}
5. 使用第三方库(如gorm)
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
// 自定义GORM数据类型
type DateType struct {
time.Time
}
func (d *DateType) Scan(value interface{}) error {
// 实现扫描逻辑
}
func (d DateType) Value() (driver.Value, error) {
// 实现值转换逻辑
}
// 在GORM模型中使用
type User struct {
gorm.Model
Name string
BirthDate DateType
}
示例:完整的数据库操作
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
"github.com/jmoiron/sqlx"
)
// 使用自定义Date类型
type User struct {
ID int `db:"id"`
Name string `db:"name"`
BirthDate Date `db:"birth_date"`
}
func main() {
// 连接数据库
db, err := sqlx.Connect("postgres", "user=postgres dbname=test sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 查询数据
var users []User
err = db.Select(&users, "SELECT id, name, birth_date FROM users")
if err != nil {
log.Fatal(err)
}
// 显示结果(只显示日期)
for _, user := range users {
fmt.Printf("ID: %d, Name: %s, BirthDate: %s\n",
user.ID,
user.Name,
user.BirthDate.Format("2006-01-02"))
}
}
这些方法都可以让你在Go中只显示日期而不显示时间。自定义类型方法是最彻底的解决方案,它确保在整个应用程序中日期都以统一的格式处理。


