Repository navigation
FAQ
Yes, as long as you do not call a pointer method on a shared value. Time is an
immutable value; every method with a value receiver returns a new one. The
pointer methods (Set, SetHour, At, Scan, UnmarshalJSON, …) mutate, so
they need the usual care.
Iran() and Afghanistan() are safe to call concurrently — the lookup is
guarded by sync.OnceValue.
The arithmetic 33 year cycle rule: 8 leap years in every 33, determined by
(25*year + 11) mod 33 < 8.
This is the rule almost every software implementation uses. The official Iranian calendar is astronomical, defined by the March equinox at the 52.5°E meridian, and the two rules agree for the range this package targets. If you need equinox-exact behavior for years far outside it, this is not the package for you.
Below it the conversion formulas leave their valid range and start producing
nonsense. Rather than return wrong dates, New returns the zero Time:
old := time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC)
ptime.New(old).IsZero() // trueDates between 1097 and the 1582 Gregorian reform are handled through the Julian calendar, which is what those dates actually meant at the time.
Because time.Parse defaults to UTC, and surprising a Go developer is worse
than the extra keystrokes. Use ParseInLocation when you know the zone:
pt, err := ptime.ParseInLocation(ptime.DateOnly, "1404-05-20", ptime.Iran())So the column stays an ordinary SQL timestamp. ORDER BY, BETWEEN, indexes,
date_trunc and every other database date function keep working, and other
consumers of the database can read it without knowing about the Persian
calendar. Conversion happens at the edge of your program, which is where it
belongs. See Encoding.
Historical reasons, and both turned out to be useful. Format uses pattern
letters (yyyy/MM/dd), which is what most non-Go date libraries use and what
most designers and product people write in a ticket. TimeFormat uses the Go
reference time (2006/01/02), so layouts you already have for time.Time work
unchanged. Both languages have a matching parser.
ns writes the nanosecond field as a plain decimal number, without padding —
5 nanoseconds is 5, not 000000005. It is a numeric field, not a fraction.
The .000/.999 patterns are proper fractions and are what you want in
anything that will be parsed again:
pt.Format("ns") // 5
pt.Format(".000000000") // .000000005Yes. Time implements encoding.TextMarshaler and encoding.TextUnmarshaler,
which is what encoding/json uses for map keys and what most YAML, TOML and
form-binding libraries look for.
Formatting is a single pass into a byte buffer, so Format costs one
allocation and AppendFormat costs none. Conversion is pure integer
arithmetic — no floating point, no allocation. Location lookups are cached.
Run make bench to see the numbers on your machine.
Only if you use Iran(), Afghanistan(), or a named zone. Both helpers fall
back to a fixed offset zone when time.LoadLocation fails, so a scratch
container without tzdata still gets correct offsets — it just loses daylight
saving history, which Iran no longer observes anyway.
To be certain, import the embedded database:
import _ "time/tzdata"No. This package converts and formats dates. Persian digits, holidays and UI components are separate concerns, and keeping them out is why the package has no dependencies. Converting digits is four lines:
var persianDigits = strings.NewReplacer(
"0", "۰", "1", "۱", "2", "۲", "3", "۳", "4", "۴",
"5", "۵", "6", "۶", "7", "۷", "8", "۸", "9", "۹",
)
persianDigits.Replace(pt.Format(ptime.DateOnly)) // ۱۴۰۴-۰۵-۲۰Open an issue with the Persian and Gregorian dates involved, the layout string if formatting or parsing is affected, and what you expected instead. A failing test case is the fastest path to a fix.
MIT licensed · Source · pkg.go.dev · Site