# `PhoenixKit.Utils.Date`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.165/lib/phoenix_kit/utils/date.ex#L1)

Date and time formatting utilities for PhoenixKit.

This module provides robust date and time formatting functionality using Timex,
supporting various format codes used throughout the PhoenixKit system.

## Core Functions

### Date Formatting
- `format_date/2` - Format a date using PHP-style format codes
- `format_time/2` - Format a time using PHP-style format codes
- `get_date_examples/1` - Generate example formatted dates
- `get_time_examples/1` - Generate example formatted times

### Format Support

The module supports the following format codes:

**Date Formats:**
- `Y-m-d` - 2025-09-02 (ISO format)
- `m/d/Y` - 09/02/2025 (US format)
- `d/m/Y` - 02/09/2025 (European format)
- `d.m.Y` - 02.09.2025 (German format)
- `d-m-Y` - 02-09-2025 (Alternative European)
- `F j, Y` - September 2, 2025 (Long format)

**Time Formats:**
- `H:i` - 15:30 (24-hour format)
- `h:i A` - 3:30 PM (12-hour format with AM/PM)

## Usage Examples

    # Format a date
    date = Date.utc_today()
    PhoenixKit.Utils.Date.format_date(date, "F j, Y")
    # => "September 2, 2025"

    # Format a time
    time = Time.utc_now()
    PhoenixKit.Utils.Date.format_time(time, "h:i A")
    # => "3:30 PM"

    # Get examples for all date formats
    PhoenixKit.Utils.Date.get_date_examples(Date.utc_today())
    # => %{"Y-m-d" => "2025-09-02", "F j, Y" => "September 2, 2025", ...}

## Implementation

This module uses Elixir's built-in Calendar.strftime for robust date/time formatting
with extensive format support.

# `format_date`

Formats a date according to the specified format string.

Uses Calendar.strftime for robust date formatting with extensive format support.

## Examples

    iex> PhoenixKit.Utils.Date.format_date(~D[2024-01-15], "Y-m-d")
    "2024-01-15"

    iex> PhoenixKit.Utils.Date.format_date(~D[2024-01-15], "m/d/Y")
    "01/15/2024"

    iex> PhoenixKit.Utils.Date.format_date(~D[2024-01-15], "F j, Y")
    "January 15, 2024"

# `format_date_with_cached_settings`

Formats a date using pre-loaded date format settings (cache-optimized).

## Examples

    iex> settings = %{"date_format" => "F j, Y"}
    iex> PhoenixKit.Utils.Date.format_date_with_cached_settings(~D[2024-01-15], settings)
    "January 15, 2024"

# `format_date_with_user_format`

Formats a date using the user's date format preference from Settings.

Automatically loads the date_format setting and applies it to the date.

## Examples

    iex> PhoenixKit.Utils.Date.format_date_with_user_format(~D[2024-01-15])
    "January 15, 2024"  # If user has "F j, Y" format selected

# `format_date_with_user_timezone`

Formats a date using the user's timezone and date format preferences.

Takes a user struct and uses their personal timezone preference if set,
otherwise falls back to system timezone.

## Examples

    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.format_date_with_user_timezone(~D[2024-01-15], user)
    "January 15, 2024"  # If user has "F j, Y" format selected

# `format_date_with_user_timezone_cached`

Formats a date with timezone using pre-loaded settings (cache-optimized).

## Examples

    iex> settings = %{"date_format" => "F j, Y"}
    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.format_date_with_user_timezone_cached(~D[2024-01-15], user, settings)
    "January 15, 2024"

# `format_datetime`

Formats a datetime (NaiveDateTime) according to the specified date format.

Extracts the date part and formats it using format_date/2.
Returns "Never" for nil values.

## Examples

    iex> PhoenixKit.Utils.Date.format_datetime(~N[2024-01-15 15:30:00], "F j, Y")
    "January 15, 2024"

    iex> PhoenixKit.Utils.Date.format_datetime(nil, "Y-m-d")
    "Never"

# `format_datetime_full`

Formats a datetime (NaiveDateTime or DateTime) showing both date and time
according to the specified date and time formats.

Returns "Never" for nil values.

## Examples

    iex> PhoenixKit.Utils.Date.format_datetime_full(~N[2024-01-15 15:30:00], "F j, Y", "h:i A")
    "January 15, 2024 3:30 PM"

    iex> PhoenixKit.Utils.Date.format_datetime_full(nil, "Y-m-d", "H:i")
    "Never"

# `format_datetime_full_with_user_format`

Formats a datetime showing both date and time using user preferences from Settings.

Automatically loads date_format and time_format settings and applies them.
Returns "Never" for nil values.

## Examples

    iex> PhoenixKit.Utils.Date.format_datetime_full_with_user_format(~N[2024-01-15 15:30:00])
    "January 15, 2024 3:30 PM"  # If user has "F j, Y" and "h:i A" formats selected

    iex> PhoenixKit.Utils.Date.format_datetime_full_with_user_format(nil)
    "Never"

# `format_datetime_local`

Formats a UTC `DateTime` as a `datetime-local`-compatible string,
shifted to the given timezone offset.

Returns `""` for `nil` input. Output format: `"YYYY-MM-DDTHH:MM"`.

## Examples

    iex> PhoenixKit.Utils.Date.format_datetime_local(~U[2026-04-14 12:00:00Z], "0")
    "2026-04-14T12:00"

    iex> PhoenixKit.Utils.Date.format_datetime_local(~U[2026-04-14 12:00:00Z], "2")
    "2026-04-14T14:00"

    iex> PhoenixKit.Utils.Date.format_datetime_local(nil, "0")
    ""

# `format_datetime_with_cached_settings`

Formats a datetime using pre-loaded date format settings (cache-optimized).

This function accepts pre-loaded settings to avoid database queries,
providing significant performance improvements when formatting many dates.

## Examples

    iex> settings = %{"date_format" => "F j, Y"}
    iex> PhoenixKit.Utils.Date.format_datetime_with_cached_settings(~N[2024-01-15 15:30:00], settings)
    "January 15, 2024"

    iex> PhoenixKit.Utils.Date.format_datetime_with_cached_settings(nil, %{})
    "Never"

# `format_datetime_with_user_format`

Formats a datetime using the user's date format preference from Settings.

Automatically loads the date_format setting and applies it to the datetime.
Returns "Never" for nil values.

## Examples

    iex> PhoenixKit.Utils.Date.format_datetime_with_user_format(~N[2024-01-15 15:30:00])
    "January 15, 2024"  # If user has "F j, Y" format selected

    iex> PhoenixKit.Utils.Date.format_datetime_with_user_format(nil)
    "Never"

# `format_datetime_with_user_timezone`

Formats a datetime using the user's timezone and date format preferences.

Takes a user struct and uses their personal timezone preference if set,
otherwise falls back to system timezone, and finally UTC as last resort.

## Examples

    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.format_datetime_with_user_timezone(~N[2024-01-15 15:30:00], user)
    "January 15, 2024"  # If user has "F j, Y" format selected

    iex> user = %User{user_timezone: nil}  # Falls back to system timezone
    iex> PhoenixKit.Utils.Date.format_datetime_with_user_timezone(~N[2024-01-15 15:30:00], user)
    "2024-01-15"  # System default format

# `format_datetime_with_user_timezone_cached`

Formats a datetime with timezone using pre-loaded settings (cache-optimized).

This function combines timezone conversion with cached date/time formatting
for optimal performance when processing multiple users' data.

## Examples

    iex> settings = %{"date_format" => "F j, Y"}
    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.format_datetime_with_user_timezone_cached(~N[2024-01-15 15:30:00], user, settings)
    "January 15, 2024"

# `format_time`

Formats a time according to the specified format string.

Uses Calendar.strftime for robust time formatting with extensive format support.

## Examples

    iex> PhoenixKit.Utils.Date.format_time(~T[15:30:00], "H:i")
    "15:30"

    iex> PhoenixKit.Utils.Date.format_time(~T[15:30:00], "h:i A")
    "3:30 PM"

# `format_time_with_cached_settings`

Formats a time using pre-loaded time format settings (cache-optimized).

## Examples

    iex> settings = %{"time_format" => "h:i A"}
    iex> PhoenixKit.Utils.Date.format_time_with_cached_settings(~T[15:30:00], settings)
    "3:30 PM"

# `format_time_with_user_format`

Formats a time using the user's time format preference from Settings.
Automatically loads the time_format setting and applies it to the time.
## Examples
    iex> PhoenixKit.Utils.Date.format_time_with_user_format(~T[15:30:00])
    "3:30 PM"  # If user has "h:i A" format selected

# `format_time_with_user_timezone`

Formats a time using the user's timezone and time format preferences.

Takes a user struct and uses their personal timezone preference if set,
otherwise falls back to system timezone.

## Examples

    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.format_time_with_user_timezone(~T[15:30:00], user)
    "3:30 PM"  # If user has "h:i A" format selected

# `format_time_with_user_timezone_cached`

Formats a time with timezone using pre-loaded settings (cache-optimized).

## Examples

    iex> settings = %{"time_format" => "h:i A"}
    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.format_time_with_user_timezone_cached(~T[15:30:00], user, settings)
    "8:30 PM"

# `get_date_examples`

Generates example formatted dates for all supported formats.

Returns a map with format codes as keys and formatted examples as values.
Useful for generating dropdown options and previews.

## Examples

    iex> PhoenixKit.Utils.Date.get_date_examples(~D[2024-01-15])
    %{
      "Y-m-d" => "2024-01-15",
      "m/d/Y" => "01/15/2024",
      "d/m/Y" => "15/01/2024",
      "d.m.Y" => "15.01.2024",
      "d-m-Y" => "15-01-2024",
      "F j, Y" => "January 15, 2024"
    }

# `get_date_format_options`

Gets all supported date format options with dynamic examples.

Returns a list of {label, format_code} tuples suitable for dropdown menus.
Each label includes the format description and a current date example.

## Examples

    iex> PhoenixKit.Utils.Date.get_date_format_options()
    [
      {"YYYY-MM-DD (2025-09-02)", "Y-m-d"},
      {"MM/DD/YYYY (09/02/2025)", "m/d/Y"},
      # ... more formats
    ]

# `get_time_examples`

Generates example formatted times for all supported formats.

Returns a map with format codes as keys and formatted examples as values.
Useful for generating dropdown options and previews.

## Examples

    iex> PhoenixKit.Utils.Date.get_time_examples(~T[15:30:00])
    %{
      "H:i" => "15:30",
      "h:i A" => "3:30 PM"
    }

# `get_time_format_options`

Gets all supported time format options with dynamic examples.

Returns a list of {label, format_code} tuples suitable for dropdown menus.
Each label includes the format description and a current time example.

## Examples

    iex> PhoenixKit.Utils.Date.get_time_format_options()
    [
      {"24 Hour (15:30)", "H:i"},
      {"12 Hour (3:30 PM)", "h:i A"}
    ]

# `get_user_timezone`

Gets the effective timezone for a user.

Returns the user's personal timezone if set, otherwise falls back to
system timezone, and finally UTC as last resort.

## Examples

    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.get_user_timezone(user)
    "+5"

    iex> user = %User{user_timezone: nil}
    iex> PhoenixKit.Utils.Date.get_user_timezone(user)
    "0"  # System default

# `get_user_timezone_cached`

Gets the effective timezone for a user using cached system timezone.

Optimized version that accepts pre-loaded system timezone setting.

## Examples

    iex> settings = %{"time_zone" => "+1"}
    iex> user = %User{user_timezone: nil}
    iex> PhoenixKit.Utils.Date.get_user_timezone_cached(user, settings)
    "+1"

    iex> user = %User{user_timezone: "+5"}
    iex> PhoenixKit.Utils.Date.get_user_timezone_cached(user, %{})
    "+5"

# `offset_to_seconds`

Converts a timezone offset string to seconds.

Accepts strings like `"0"`, `"2"`, `"-5"`, `"5.5"` (hours from UTC).
Returns `0` for invalid input (safe default).

## Examples

    iex> PhoenixKit.Utils.Date.offset_to_seconds("0")
    0

    iex> PhoenixKit.Utils.Date.offset_to_seconds("2")
    7200

    iex> PhoenixKit.Utils.Date.offset_to_seconds("-5")
    -18000

    iex> PhoenixKit.Utils.Date.offset_to_seconds("5.5")
    19800

    iex> PhoenixKit.Utils.Date.offset_to_seconds("invalid")
    0

# `parse_datetime_local`

Parses a datetime-local input value (assumed to be in `tz_offset` timezone)
and returns the equivalent UTC `DateTime`.

Returns `{:ok, datetime}` or an error tuple. The tolerance input format is
`"YYYY-MM-DDTHH:MM"` (no seconds) or `"YYYY-MM-DDTHH:MM:SS"`.

## Examples

    iex> {:ok, dt} = PhoenixKit.Utils.Date.parse_datetime_local("2026-04-14T12:00", "0")
    iex> DateTime.to_iso8601(dt)
    "2026-04-14T12:00:00Z"

    iex> {:ok, dt} = PhoenixKit.Utils.Date.parse_datetime_local("2026-04-14T12:00", "2")
    iex> DateTime.to_iso8601(dt)
    "2026-04-14T10:00:00Z"

    iex> PhoenixKit.Utils.Date.parse_datetime_local("not-a-date", "0")
    {:error, :invalid_format}

# `shift_to_offset`

Shifts a DateTime by the system timezone offset (for display purposes only).

The result is NOT a properly-zoned DateTime — its `time_zone` still says UTC.
Use only when you need the wall-clock value for display. Never store this.

# `utc_now`

```elixir
@spec utc_now() :: DateTime.t()
```

Returns current UTC time truncated to second precision.

Use this for all `:utc_datetime` schema fields to avoid
`ArgumentError: :utc_datetime expects microseconds to be empty`.

## Examples

    iex> PhoenixKit.Utils.Date.utc_now()
    ~U[2026-02-21 14:30:45Z]

---

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