Skip to content

Getting Started

Amir Iranmanesh edited this page Aug 11, 2026 · 1 revision

Getting Started

Install

go get github.com/amiranmanesh/go-persian-calendar

Go 1.21 or newer. The package name is ptime, which does not match the last element of the import path, so name it explicitly:

import ptime "github.com/amiranmanesh/go-persian-calendar"

Creating a Time

There are five ways in, and they cover everything:

ptime.Now()                                                    // the current moment, local zone
ptime.Date(1394, ptime.Mehr, 2, 12, 59, 59, 0, ptime.Iran())   // from Persian components
ptime.New(someGoTime)                                          // from a time.Time
ptime.Unix(1454277270, 0)                                      // from a Unix timestamp
ptime.Parse(ptime.DateOnly, "1394-07-02")                      // from text

UnixMilli and UnixMicro are there too, matching the standard library.

Gregorian to Persian, and back

gt := time.Date(2016, time.January, 1, 12, 1, 1, 0, ptime.Iran())

pt := ptime.New(gt)
fmt.Println(pt.Date()) // 1394 دی 11

back := pt.Time()
fmt.Println(back.Equal(gt)) // true

The round trip is lossless for every representable moment — there is a fuzz target that asserts exactly this.

Reading a Time

pt := ptime.Now()

year, month, day := pt.Date()
hour, minute, sec := pt.Clock()

pt.Year()        // 1404
pt.Month()       // Mordad
pt.Month().String()  // مرداد
pt.Month().Dari()    // اسد
pt.Day()         // 20
pt.Weekday()     // دوشنبه
pt.Nanosecond()
pt.Location()    // *time.Location
pt.Unix()        // seconds since the Unix epoch

Month, Weekday, AmPm and DayTime are named integer types with String() methods, so they print in Persian anywhere fmt is used.

Changing a Time

Value methods return a new Time:

pt.Add(90 * time.Minute)
pt.AddDate(0, 1, 0)   // one month later, normalized
pt.Tomorrow()
pt.Yesterday()
pt.Truncate(time.Hour)
pt.In(ptime.Afghanistan())

Pointer methods mutate in place, which is handy when you are building a value field by field:

var pt ptime.Time

pt.Set(1404, ptime.Mordad, 20, 9, 30, 0, 0, ptime.Iran())
pt.SetHour(10)
pt.At(10, 15, 0, 0)

Setters clamp out of range values to the nearest valid one; Set, Date and AddDate normalize instead, so day 32 of Farvardin becomes Ordibehesht 1.

Comparing

a := ptime.Date(1394, ptime.Mehr, 2, 12, 0, 0, 0, ptime.Iran())
b := ptime.Date(1394, ptime.Mehr, 3, 12, 0, 0, 0, ptime.Iran())

a.Before(b)  // true
a.After(b)   // false
a.Equal(b)   // false — compares instants, so zones do not matter
a.Compare(b) // -1
b.Sub(a)     // 24h0m0s

Equal and Compare compare instants, not calendar fields: 6:00 +0200 and 4:00 UTC are equal.

The zero value

var pt ptime.Time

pt.IsZero()   // true
pt.String()   // 0000-00-00T00:00:00Z

The zero Time marshals to JSON null, to an empty string as text, and to SQL NULL. New returns it for Gregorian years below 1097, which is the oldest year the conversion supports.

Next

Clone this wiki locally