GanttEx.Dates (GanttEx v0.1.0)

Copy Markdown View Source

Date arithmetic used across GanttEx.

GanttEx works with NaiveDateTime values internally, the same way the original DHTMLX Gantt works with local Date objects: a Gantt chart shows wall-clock time, so time zones are left to the host application.

Durations are always expressed as an integer amount of a unit:

  • :minute, :hour, :day and :week have a fixed length
  • :month, :quarter and :year follow the calendar (a month can have 28 to 31 days)

Examples

iex> GanttEx.Dates.add(~N[2025-01-30 00:00:00], 1, :month)
~N[2025-02-28 00:00:00]

iex> GanttEx.Dates.diff(~N[2025-01-01 00:00:00], ~N[2025-01-04 00:00:00], :day)
3

iex> GanttEx.Dates.floor(~N[2025-05-14 13:45:00], :week)
~N[2025-05-12 00:00:00]

Summary

Types

A duration unit.

Functions

Adds amount units to datetime. Negative amounts move backwards.

Casts a date-like value into a NaiveDateTime truncated to seconds.

Like cast/1 but raises ArgumentError on invalid input.

Casts a unit given as atom or string.

Rounds datetime up to the start of the next unit, unless it is already aligned.

Returns the duration between from and to in unit, rounded to the nearest integer. The result is negative when to is before from.

Like diff/3 but returns the exact (fractional) amount.

Rounds datetime down to the start of unit.

Converts gregorian seconds back into a NaiveDateTime.

Returns the ISO 8601 week number of datetime.

Returns the latest of two datetimes (ignoring nil).

Returns the earliest of two datetimes (ignoring nil).

Rounds datetime to the nearest boundary of unit.

Converts a NaiveDateTime into gregorian seconds (useful for fast math).

Returns true if unit is a supported duration unit.

Returns the length in seconds of a fixed-length unit.

Returns all supported units, from the smallest to the largest.

Types

unit()

@type unit() :: :minute | :hour | :day | :week | :month | :quarter | :year

A duration unit.

Functions

add(datetime, amount, unit)

@spec add(NaiveDateTime.t(), number(), unit()) :: NaiveDateTime.t()

Adds amount units to datetime. Negative amounts move backwards.

Calendar units clamp the day when the target month is shorter.

Examples

iex> GanttEx.Dates.add(~N[2025-01-01 00:00:00], 36, :hour)
~N[2025-01-02 12:00:00]

iex> GanttEx.Dates.add(~N[2025-01-31 00:00:00], 1, :quarter)
~N[2025-04-30 00:00:00]

iex> GanttEx.Dates.add(~N[2025-01-10 00:00:00], -1, :week)
~N[2025-01-03 00:00:00]

cast(dt)

@spec cast(term()) :: {:ok, NaiveDateTime.t() | nil} | :error

Casts a date-like value into a NaiveDateTime truncated to seconds.

Accepts NaiveDateTime, DateTime (converted to its wall-clock time), Date (midnight), ISO 8601 strings ("2025-01-31", "2025-01-31 10:30", "2025-01-31T10:30:00") and the DHTMLX default format ("31-01-2025", "31-01-2025 10:30"). nil is returned as {:ok, nil}.

Examples

iex> GanttEx.Dates.cast(~D[2025-03-01])
{:ok, ~N[2025-03-01 00:00:00]}

iex> GanttEx.Dates.cast("2025-03-01 08:30")
{:ok, ~N[2025-03-01 08:30:00]}

iex> GanttEx.Dates.cast("01-03-2025")
{:ok, ~N[2025-03-01 00:00:00]}

iex> GanttEx.Dates.cast(nil)
{:ok, nil}

iex> GanttEx.Dates.cast("yesterday")
:error

cast!(value)

@spec cast!(term()) :: NaiveDateTime.t() | nil

Like cast/1 but raises ArgumentError on invalid input.

iex> GanttEx.Dates.cast!("2025-01-01")
~N[2025-01-01 00:00:00]

cast_unit(unit)

@spec cast_unit(term()) :: {:ok, unit()} | :error

Casts a unit given as atom or string.

iex> GanttEx.Dates.cast_unit("hour")
{:ok, :hour}

iex> GanttEx.Dates.cast_unit(:day)
{:ok, :day}

iex> GanttEx.Dates.cast_unit("decade")
:error

ceil(datetime, unit, opts \\ [])

@spec ceil(NaiveDateTime.t(), unit(), keyword()) :: NaiveDateTime.t()

Rounds datetime up to the start of the next unit, unless it is already aligned.

Examples

iex> GanttEx.Dates.ceil(~N[2025-05-14 13:45:10], :day)
~N[2025-05-15 00:00:00]

iex> GanttEx.Dates.ceil(~N[2025-05-01 00:00:00], :month)
~N[2025-05-01 00:00:00]

diff(from, to, unit)

@spec diff(NaiveDateTime.t(), NaiveDateTime.t(), unit()) :: integer()

Returns the duration between from and to in unit, rounded to the nearest integer. The result is negative when to is before from.

Examples

iex> GanttEx.Dates.diff(~N[2025-01-01 00:00:00], ~N[2025-01-01 18:00:00], :day)
1

iex> GanttEx.Dates.diff(~N[2025-01-15 00:00:00], ~N[2025-03-15 00:00:00], :month)
2

iex> GanttEx.Dates.diff(~N[2025-01-01 00:00:00], ~N[2024-12-31 00:00:00], :hour)
-24

diff_float(from, to, unit)

@spec diff_float(NaiveDateTime.t(), NaiveDateTime.t(), unit()) :: float()

Like diff/3 but returns the exact (fractional) amount.

iex> GanttEx.Dates.diff_float(~N[2025-01-01 00:00:00], ~N[2025-01-01 12:00:00], :day)
0.5

floor(datetime, unit, opts \\ [])

@spec floor(NaiveDateTime.t(), unit(), keyword()) :: NaiveDateTime.t()

Rounds datetime down to the start of unit.

Weeks start on Monday by default; pass week_start: 7 to start on Sunday (ISO day numbers, 1 = Monday ... 7 = Sunday).

Examples

iex> GanttEx.Dates.floor(~N[2025-05-14 13:45:10], :hour)
~N[2025-05-14 13:00:00]

iex> GanttEx.Dates.floor(~N[2025-05-14 13:45:10], :quarter)
~N[2025-04-01 00:00:00]

iex> GanttEx.Dates.floor(~N[2025-05-14 13:45:10], :week, week_start: 7)
~N[2025-05-11 00:00:00]

from_seconds(seconds)

@spec from_seconds(number()) :: NaiveDateTime.t()

Converts gregorian seconds back into a NaiveDateTime.

iex> GanttEx.Dates.from_seconds(60)
~N[0000-01-01 00:01:00]

iso_week(map)

@spec iso_week(NaiveDateTime.t() | Date.t()) :: 1..53

Returns the ISO 8601 week number of datetime.

iex> GanttEx.Dates.iso_week(~N[2025-01-01 00:00:00])
1

iex> GanttEx.Dates.iso_week(~N[2024-12-30 00:00:00])
1

max(a, b)

@spec max(NaiveDateTime.t() | nil, NaiveDateTime.t() | nil) :: NaiveDateTime.t() | nil

Returns the latest of two datetimes (ignoring nil).

iex> GanttEx.Dates.max(~N[2025-01-02 00:00:00], ~N[2025-01-01 00:00:00])
~N[2025-01-02 00:00:00]

min(a, b)

@spec min(NaiveDateTime.t() | nil, NaiveDateTime.t() | nil) :: NaiveDateTime.t() | nil

Returns the earliest of two datetimes (ignoring nil).

iex> GanttEx.Dates.min(~N[2025-01-02 00:00:00], ~N[2025-01-01 00:00:00])
~N[2025-01-01 00:00:00]

iex> GanttEx.Dates.min(nil, ~N[2025-01-01 00:00:00])
~N[2025-01-01 00:00:00]

round(datetime, unit, opts \\ [])

@spec round(NaiveDateTime.t(), unit(), keyword()) :: NaiveDateTime.t()

Rounds datetime to the nearest boundary of unit.

iex> GanttEx.Dates.round(~N[2025-05-14 13:00:00], :day)
~N[2025-05-15 00:00:00]

iex> GanttEx.Dates.round(~N[2025-05-14 11:59:00], :day)
~N[2025-05-14 00:00:00]

to_seconds(dt)

@spec to_seconds(NaiveDateTime.t()) :: integer()

Converts a NaiveDateTime into gregorian seconds (useful for fast math).

iex> GanttEx.Dates.to_seconds(~N[0000-01-01 00:01:00])
60

unit?(unit)

@spec unit?(term()) :: boolean()

Returns true if unit is a supported duration unit.

iex> GanttEx.Dates.unit?(:day)
true

iex> GanttEx.Dates.unit?(:fortnight)
false

unit_seconds(unit)

@spec unit_seconds(unit()) :: pos_integer() | nil

Returns the length in seconds of a fixed-length unit.

iex> GanttEx.Dates.unit_seconds(:hour)
3600

iex> GanttEx.Dates.unit_seconds(:month)
nil

units()

@spec units() :: [unit()]

Returns all supported units, from the smallest to the largest.

iex> GanttEx.Dates.units()
[:minute, :hour, :day, :week, :month, :quarter, :year]