# `JalaliX`
[🔗](https://github.com/jalalix/elixir/blob/main/lib/jalalix.ex#L1)

Jalali (Persian / Shamsi) calendar for Elixir.

Pure Elixir, no runtime timezone database dependency. Internally a
`JalaliX` value is a UTC Unix millisecond instant (`ms`) plus an active
IANA-style timezone id (`timezone`). Timezone support is intentionally
simple: offsets for `UTC` and `Asia/Tehran` are hardcoded (Iran has not
observed DST since 2022, so `Asia/Tehran` is a fixed `+03:30` offset
year-round). `setTimezone/2` is **instant-preserving**: `getTime/1` stays
the same; only the wall-clock / Jalali display changes.

Elixir is immutable, so every "setter" returns a **new** `%JalaliX{}`
struct rather than mutating in place.

Method names intentionally mirror the TypeScript/Python ports
(`@jalalix/typescript`, `jalalix` on PyPI) using camelCase, so porting
code between languages is mostly 1:1.

## Month quirk

Just like the JS/Python ports:

  * `getMonth/1` is **1-based** (Farvardin = 1 .. Esfand = 12)
  * `setMonth/2,3` and `setFullYear/2,3,4` take a **0-based** month
    (Farvardin = 0 .. Esfand = 11), matching JS `Date`.

## Example

    iex> d = JalaliX.create("2025-02-25")
    iex> JalaliX.format(d, "YYYY/MM/DD")
    "1403/12/07"
    iex> JalaliX.parseJalali("1403/12/07") |> JalaliX.toISODate()
    "2025-02-25"

# `t`

```elixir
@type t() :: %JalaliX{ms: integer(), timezone: String.t()}
```

# `addDays`

```elixir
@spec addDays(t(), integer()) :: t()
```

Adds the given number of days (can be negative).

# `addMonths`

```elixir
@spec addMonths(t(), integer()) :: t()
```

Adds the given number of Jalali months (can be negative).

# `addWeeks`

```elixir
@spec addWeeks(t(), integer()) :: t()
```

Adds the given number of weeks (can be negative).

# `addYears`

```elixir
@spec addYears(t(), integer()) :: t()
```

Adds the given number of Jalali years (can be negative).

# `clone`

```elixir
@spec clone(t()) :: t()
```

Creates a copy of this `JalaliX` instance (same instant and timezone).

# `compare`

```elixir
@spec compare(t(), t()) :: boolean()
```

Returns true if `a` is before `b`.

# `create`

```elixir
@spec create(
  integer()
  | float()
  | String.t()
  | Date.t()
  | DateTime.t()
  | NaiveDateTime.t()
  | t(),
  map() | keyword()
) :: t()
```

Creates a `JalaliX` instance from a Gregorian date value.

Accepts a Unix millisecond timestamp (`integer`/`float`), an ISO8601
string, a `Date`/`DateTime`/`NaiveDateTime`, or another `JalaliX`.

`opts` may contain `timezone:` to set the active zone (map or keyword list).

# `format`

```elixir
@spec format(t(), String.t(), boolean() | map() | keyword(), boolean()) :: String.t()
```

Formats the date according to a format string.

Prefer `format(j, str, %{calendar: ..., persianDigits: ...})`. Positional
booleans remain supported: `format(j, str, persianDigits?, gregorian?)`.

## Tokens

    YYYY YYY YY Y       year
    MMM MM M            month name / number
    W w                 week of year (padded / not)
    DW dw WN wn WL wl    day-of-week (iso number / number / name / letter)
    DDDD DDD             day of year (padded / not)
    DD D                 day of month (padded / not)
    HH H                 hour, 24h (padded / not)
    hh h                 hour, 12h (padded / not)
    mm m                 minutes (padded / not)
    ss s                 seconds (padded / not)
    SSS sss              milliseconds
    A a                  meridiem
    X x                  unix seconds / milliseconds

# `fromPersian`

```elixir
@spec fromPersian(String.t()) :: String.t()
```

Converts Persian numerals (۰-۹) to Western numerals (0-9) in a string.

# `getDate`

```elixir
@spec getDate(t()) :: integer()
```

Gets the day of the month in the Jalali calendar (1-31).

# `getDay`

```elixir
@spec getDay(t()) :: integer()
```

Gets the day of the week (1=Saturday .. 7=Friday in Jalali).

# `getDayISO`

```elixir
@spec getDayISO(t()) :: integer()
```

Gets the ISO day of the week (1=Monday .. 7=Sunday).

# `getDayOfYear`

```elixir
@spec getDayOfYear(t()) :: integer()
```

Gets the Jalali day of the year (1-366).

# `getEndOfDay`

```elixir
@spec getEndOfDay(t()) :: t()
```

Returns a new instance set to the end of the current day (23:59:59.999).

# `getEndOfMonth`

```elixir
@spec getEndOfMonth(t()) :: t()
```

Returns a new instance set to the end of the current Jalali month.

# `getEndOfWeek`

```elixir
@spec getEndOfWeek(t()) :: t()
```

Returns a new instance set to the end of the current week (Friday).

# `getEndOfYear`

```elixir
@spec getEndOfYear(t()) :: t()
```

Returns a new instance set to the end of the Jalali year (23:59:59.999).

# `getFullYear`

```elixir
@spec getFullYear(t()) :: integer()
```

Gets the Jalali year.

# `getHours`

```elixir
@spec getHours(t()) :: integer()
```

Gets the hour (0-23) using wall time in the active timezone.

# `getHoursISO`

```elixir
@spec getHoursISO(t()) :: integer()
```

Gets the hour in 12-hour format (1-12).

# `getMeridiem`

```elixir
@spec getMeridiem(t()) :: String.t()
```

Gets the meridiem (AM/PM) in Persian.

# `getMilliseconds`

```elixir
@spec getMilliseconds(t()) :: integer()
```

Gets the milliseconds (0-999).

# `getMinutes`

```elixir
@spec getMinutes(t()) :: integer()
```

Gets the minutes (0-59) using wall time in the active timezone.

# `getMonth`

```elixir
@spec getMonth(t()) :: integer()
```

Gets the Jalali month (1-12, Farvardin = 1).

# `getSeconds`

```elixir
@spec getSeconds(t()) :: integer()
```

Gets the seconds (0-59) using wall time in the active timezone.

# `getStartOfDay`

```elixir
@spec getStartOfDay(t()) :: t()
```

Returns a new instance set to the start of the current day (00:00:00.000).

# `getStartOfMonth`

```elixir
@spec getStartOfMonth(t()) :: t()
```

Returns a new instance set to the start of the current Jalali month.

# `getStartOfWeek`

```elixir
@spec getStartOfWeek(t()) :: t()
```

Returns a new instance set to the start of the current week (Saturday).

# `getStartOfYear`

```elixir
@spec getStartOfYear(t()) :: t()
```

Returns a new instance set to the start of the Jalali year.

# `getTime`

```elixir
@spec getTime(t()) :: integer()
```

Gets the Unix millisecond timestamp.

# `getTimezone`

```elixir
@spec getTimezone(t()) :: String.t()
```

Gets the active IANA timezone id of this instance.

# `getWeekOfYear`

```elixir
@spec getWeekOfYear(t()) :: integer()
```

Gets the Jalali week number of the year (1-53).

# `isValidDate`

```elixir
@spec isValidDate(any()) :: boolean()
```

Returns true if `value` is a recognized date-like value.

# `parseJalali`

```elixir
@spec parseJalali(String.t(), map() | keyword()) :: t()
```

Parses a Jalali date string into a `JalaliX` instance.

Accepts `YYYY/MM/DD` or `YYYY-MM-DD`, optional time (`HH:mm` / `HH:mm:ss`),
and Persian digits.

# `setDate`

```elixir
@spec setDate(t(), integer()) :: t()
```

Sets the day of the month (Jalali) using wall time.

# `setFullYear`

```elixir
@spec setFullYear(t(), integer(), integer() | nil, integer() | nil) :: t()
```

Sets the Jalali year (and optionally month/day) using wall time.

`month` is **0-based** (Farvardin = 0 .. Esfand = 11), matching JS `Date`.

# `setHours`

```elixir
@spec setHours(t(), number(), number() | nil, number() | nil, number() | nil) :: t()
```

Sets the hour (and optionally minutes/seconds/milliseconds) using wall time.

# `setMilliseconds`

```elixir
@spec setMilliseconds(t(), number()) :: t()
```

Sets the milliseconds using wall time in the active timezone.

# `setMinutes`

```elixir
@spec setMinutes(t(), number(), number() | nil, number() | nil) :: t()
```

Sets the minutes (and optionally seconds/milliseconds) using wall time.

# `setMonth`

```elixir
@spec setMonth(t(), integer(), integer() | nil) :: t()
```

Sets the Jalali month using wall time.

`month` is **0-based** (Farvardin = 0 .. Esfand = 11), matching JS `Date`.

# `setSeconds`

```elixir
@spec setSeconds(t(), number(), number() | nil) :: t()
```

Sets the seconds (and optionally milliseconds) using wall time.

# `setTime`

```elixir
@spec setTime(t(), number()) :: t()
```

Sets the Unix millisecond timestamp.

# `setTimezone`

```elixir
@spec setTimezone(t(), String.t() | nil) :: t()
```

Creates a new `JalaliX` with the same UTC instant in a different timezone.

Instant-preserving: `getTime/1` is unchanged; wall clock / Jalali day may
change. Aliases: `local`/`system`/`default` -> host zone (or `UTC`),
`utc`/`gmt` -> `UTC`. Raises `ArgumentError` for an unrecognized zone.

# `toDateString`

```elixir
@spec toDateString(t()) :: String.t()
```

Date string with weekday, day, month, and year (no time).

# `toISODate`

```elixir
@spec toISODate(t(), String.t()) :: String.t()
```

Returns a calendar date string using wall getters in the active timezone
(no UTC shift).

`calendar` is `"gregorian"` (default, `YYYY-MM-DD` Gregorian) or
`"jalali"` (Jalali `YYYY-MM-DD`).

# `toLocaleDateString`

```elixir
@spec toLocaleDateString(t()) :: String.t()
```

Locale-formatted date string: `YYYY/MM/DD`.

# `toLocaleTimeString`

```elixir
@spec toLocaleTimeString(t()) :: String.t()
```

Time string with meridiem, e.g. `10:30:45 ق.ظ.`.

# `toPersian`

```elixir
@spec toPersian(String.t()) :: String.t()
```

Converts Western numerals (0-9) to Persian numerals (۰-۹) in a string.

# `toString`

```elixir
@spec toString(t()) :: String.t()
```

Full date string with weekday, day, month, year, and time.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
