Repository navigation
Encoding
ptime.Time implements the standard encoding interfaces, so it drops into
structs and database columns without a wrapper type.
| Interface | Methods |
|---|---|
json.Marshaler, json.Unmarshaler
|
MarshalJSON, UnmarshalJSON
|
encoding.TextMarshaler, encoding.TextUnmarshaler
|
MarshalText, UnmarshalText
|
driver.Valuer |
Value |
sql.Scanner |
Scan |
The wire format is RFC3339Nano: 1404-01-01T00:00:00+03:30, with the
fractional second trimmed of trailing zeros and omitted when zero.
type Event struct {
Name string `json:"name"`
At ptime.Time `json:"at"`
}
b, _ := json.Marshal(Event{
Name: "نوروز",
At: ptime.Date(1404, ptime.Farvardin, 1, 0, 0, 0, 0, ptime.Iran()),
})
// {"name":"نوروز","at":"1404-01-01T00:00:00+03:30"}
var back Event
json.Unmarshal(b, &back)The zero Time is null, in both directions:
json.Marshal(ptime.Time{}) // null
json.Unmarshal([]byte("null"), &pt) // pt.IsZero() == trueBecause TextUnmarshaler is implemented, a ptime.Time also works as a map
key and in any encoder that speaks text — YAML, TOML, encoding/xml, URL
query binding libraries.
If you need something else on the wire, wrap the type — this is the same pattern
you would use for time.Time:
type PersianDate ptime.Time
func (d PersianDate) MarshalJSON() ([]byte, error) {
return json.Marshal(ptime.Time(d).Format(ptime.DateOnly))
}
func (d *PersianDate) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
pt, err := ptime.ParseInLocation(ptime.DateOnly, s, ptime.Iran())
if err != nil {
return err
}
*d = PersianDate(pt)
return nil
}Value hands the driver a Gregorian time.Time, so the column is an
ordinary SQL timestamp. That keeps ORDER BY, range queries, indexes and every
other database date function working, and it keeps the data readable to
non-Go consumers.
at := ptime.Date(1404, ptime.Farvardin, 1, 0, 0, 0, 0, ptime.Iran())
db.Exec("INSERT INTO events (name, created_at) VALUES ($1, $2)", "نوروز", at)
var got ptime.Time
db.QueryRow("SELECT created_at FROM events WHERE id = $1", id).Scan(&got)Scan accepts:
| Source | Handling |
|---|---|
nil |
the zero Time
|
time.Time |
converted through New
|
string, []byte
|
parsed as a Gregorian timestamp |
Text is read as Gregorian, not Persian, because that is what Value writes and
the two are indistinguishable on the wire — 1394-07-02 is a valid date in
both calendars. The layouts tried, in order:
2006-01-02T15:04:05.999999999Z07:00
2006-01-02 15:04:05.999999999 -0700 MST
2006-01-02 15:04:05.999999999-07:00
2006-01-02 15:04:05.999999999
2006-01-02 15:04:05
2006-01-02
Anything else fails with a *ParseError. A nil driver.Value is written for
the zero Time, so it round trips through a nullable column.
If you deliberately want the Persian form in the column — for a report table someone reads directly, say — store the formatted string and parse it back yourself:
db.Exec("INSERT INTO reports (day) VALUES ($1)", at.Format(ptime.DateOnly))
var day string
db.QueryRow("SELECT day FROM reports WHERE id = $1", id).Scan(&day)
at, err := ptime.ParseInLocation(ptime.DateOnly, day, ptime.Iran())Keep in mind you lose the database's date arithmetic when you do this.
MIT licensed · Source · pkg.go.dev · Site