# `PgInterop.Interval`
[🔗](https://github.com/electric-sql/electric/tree/%40core/sync-service%401.6.2/packages/sync-service/lib/pg_interop/interval.ex#L1)

PostgreSQL interval representation.

`Timex.Duration` does not match PG interval representation because it doesn't store month
separately, leading to discrepancies like `date '2024-01-31' + interval '1 month'` being
`datetime '2024-02-29 00:00:00` in Postgres, but `Timex.add(~N[2024-01-31 00:00:00],
Timex.Duration.parse!("P1M"))` being `~N[2024-03-01 00:00:00]`. This implementation
sticks to PG interpretation of the events.

# `t`

```elixir
@type t() :: %PgInterop.Interval{
  days: integer(),
  microseconds: integer(),
  months: integer()
}
```

# `add`

Add two intervals together

# `add_to_date`

Add the interval to a given `Date` or `NaiveDateTime`.

## Examples

    iex> add_to_date(~D[2024-01-01], parse!("P1D"))
    ~N[2024-01-02 00:00:00]

    iex> add_to_date(~N[2024-01-01 12:00:00], parse!("P1DT10M"))
    ~N[2024-01-02 12:10:00.000000]

# `add_to_time`

Add the interval to a given `Date` or `NaiveDateTime`.

## Examples

    iex> add_to_time(~T[10:00:00], parse!("PT1H1M"))
    ~T[11:01:00.000000]

# `datetime_diff`

Build an interval as a difference between DateTimes. Interval is positive when
first datetime is greater than the second one.

## Examples

    iex> datetime_diff(~N[2024-01-02 00:10:00], ~N[2024-01-01 00:00:00])
    Interval.parse!("P1DT10M")

    iex> datetime_diff(~N[2024-01-02 00:00:00], ~N[2024-01-01 00:10:00])
    Interval.parse!("PT23H50M")

    iex> datetime_diff(~N[2024-01-02 00:00:00], ~N[2024-01-03 00:00:00])
    Interval.parse!("P-1D")

    iex> datetime_diff(DateTime.from_naive!(~N[2024-01-02 00:00:00], "Europe/Istanbul"), ~U[2024-01-02 00:00:00Z])
    Interval.parse!("PT-3H")

# `format`

Format the interval in ISO8601 format.

## Examples

    iex> parse!("5 minutes 3d 4 hours 6") |> format()
    "P3DT4H5M6S"

# `from_days`

Create an interval from specified amount of days.

## Examples

    iex> from_days(10)
    Interval.parse!("P10D")

    iex> from_days(10.5)
    Interval.parse!("P10DT12H")

# `from_hours`

Create an interval from specified amount of hours.

## Examples

    iex> from_hours(10)
    Interval.parse!("PT10H")

# `from_microseconds`

Create an interval from specified amount of microseconds.

## Examples

    iex> from_microseconds(1_000_000)
    Interval.parse!("PT1S")

# `from_milliseconds`

Create an interval from specified amount of milliseconds.

## Examples

    iex> from_milliseconds(1_000)
    Interval.parse!("PT1S")

# `from_minutes`

Create an interval from specified amount of minutes.

## Examples

    iex> from_minutes(60.5)
    Interval.parse!("PT1H30S")

# `from_months`

Create an interval from specified amount of months. Fractional
months are counted as parts of 30 days.

## Examples

    iex> from_months(14.5)
    Interval.parse!("P1Y2M15D")

# `from_seconds`

Create an interval from specified amount of seconds.

## Examples

    iex> from_seconds(60)
    Interval.parse!("PT1M")

# `from_time`

Create an interval from a time instance.

## Examples

    iex> from_time(~T[12:30:40.1])
    Interval.parse!("PT12H30M40.1S")

    iex> from_time(~T[12:30:40.000001])
    Interval.parse!("PT12H30M40.000001S")

# `from_weeks`

Create an interval from specified amount of weeks.

## Examples

    iex> from_weeks(4.2)
    Interval.parse!("P29DT9H36M")

# `justify_days`

Move complete 30 day periods from the day portion of the interval to the month portion.

# `justify_hours`

Move complete 24 hour periods from the microsecond portion of the interval to the day portion.

# `justify_interval`

Move complete 24 hour periods from the microsecond portion of the interval to the day portion
and complete 30 day periods from the day portion of the interval to the month portion.

## Examples
    iex> interval = %Interval{months: 0, days: 29, microseconds: 86400000000 + 60_000_000}
    Interval.parse!("P29DT24H1M")
    iex> justify_interval(interval)
    Interval.parse!("P1MT1M")

# `parse`

Parse a PostgreSQL interval string, in any of the supported PostgreSQL input formats.

For supported formats, see `parse!/1`

# `parse!`

Parse a PostgreSQL interval string, in any of the supported PostgreSQL input formats.

Raises an error when parsing fails, unlike `parse/1`

## Examples

SQL standard format

    iex> parse!("1-2")
    Interval.parse!("P1Y2M")
    iex> parse!("3 4:05:06")
    Interval.parse!("P3DT4H5M6S")

    iex> parse!("5 minutes 3d 4 hours 6")
    Interval.parse!("P3DT4H5M6S")

ISO8601 format

    iex> parse!("P1Y2M3DT4H5M6S")
    Interval.parse!("P1Y2M3DT4H5M6S")

ISO8601 "alternative" format

    iex> parse!("P0001-02-03T04:05:06")
    Interval.parse!("P1Y2M3DT4H5M6S")

    iex> parse!("what")
    ** (RuntimeError) Not a valid PostgreSQL interval

# `scale`

Scale an interval by a factor.

## Examples

    iex> scale(parse!("P2M4DT6H"), 1.5)
    Interval.parse!("P3M6DT9H")

# `subtract`

Subtracts the second interval from the first one

## Examples

    iex> subtract(parse!("P2D"), parse!("P1D"))
    Interval.parse!("P1D")

# `subtract_from_date`

Subtracts an interval from a given date or date-time to get a new date-time.

Accepts `DateTime`, `NaiveDateTime`, and `Date`. Returns `DateTime` in the first case
and `NaiveDateTime` in second  and third. If a plain `Date` is passed, midnight is assumed.

## Examples

    iex> subtract_from_date(~D[2024-01-10], parse!("P2DT12H"))
    ~N[2024-01-07 12:00:00.000000]

    iex> subtract_from_date(~N[2024-01-10 13:00:00], parse!("P2DT12H"))
    ~N[2024-01-08 01:00:00.000000]

    iex> subtract_from_date(~U[2024-01-10 13:00:00Z], parse!("P2DT12H"))
    ~U[2024-01-08 01:00:00.000000Z]

# `zero`

Zero-length interval, useful in reductions

---

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