Packages
nous
0.13.2
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/tools/date_time_tools.ex
defmodule Nous.Tools.DateTimeTools do
@moduledoc """
Built-in tools for date and time operations.
These tools provide common date/time functionality that AI agents often need:
- Get current date/time in various formats
- Parse and format dates
- Calculate date differences
- Check business days and weekends
- Timezone conversions
## Usage
agent = Nous.new("lmstudio:qwen3-vl-4b-thinking-mlx",
tools: [
&DateTimeTools.current_date/2,
&DateTimeTools.current_time/2,
&DateTimeTools.current_datetime/2
]
)
{:ok, result} = Nous.run(agent, "What day is today?")
"""
@doc """
Get the current date in the specified format.
## Arguments
- format: "iso8601" (default), "us" (MM/DD/YYYY), "eu" (DD/MM/YYYY), "full" (Monday, January 1, 2025)
- timezone: Optional timezone (e.g., "America/New_York", "Europe/London", "UTC")
"""
def current_date(_ctx, args) do
format = Map.get(args, "format", "iso8601")
timezone = Map.get(args, "timezone", "Etc/UTC")
date = get_date_in_timezone(timezone)
formatted_date =
case format do
"iso8601" -> Date.to_iso8601(date)
"us" -> Calendar.strftime(date, "%m/%d/%Y")
"eu" -> Calendar.strftime(date, "%d/%m/%Y")
"full" -> Calendar.strftime(date, "%A, %B %-d, %Y")
"short" -> Calendar.strftime(date, "%b %-d, %Y")
_ -> Date.to_iso8601(date)
end
%{
date: formatted_date,
format: format,
timezone: timezone,
day_of_week: Calendar.strftime(date, "%A"),
is_weekend: Date.day_of_week(date) in [6, 7]
}
end
@doc """
Get the current time in the specified format.
## Arguments
- format: "24h" (default, HH:MM:SS), "12h" (hh:MM:SS AM/PM), "short" (HH:MM)
- timezone: Optional timezone
"""
def current_time(_ctx, args) do
format = Map.get(args, "format", "24h")
timezone = Map.get(args, "timezone", "Etc/UTC")
time = get_time_in_timezone(timezone)
formatted_time =
case format do
"24h" -> Time.to_iso8601(time)
"12h" -> Calendar.strftime(time, "%I:%M:%S %p")
"short" -> Calendar.strftime(time, "%H:%M")
_ -> Time.to_iso8601(time)
end
%{
time: formatted_time,
format: format,
timezone: timezone,
hour: time.hour,
minute: time.minute,
second: time.second
}
end
@doc """
Get the current date and time together.
## Arguments
- format: "iso8601" (default), "rfc3339", "unix", "human"
- timezone: Optional timezone
"""
def current_datetime(_ctx, args) do
format = Map.get(args, "format", "iso8601")
timezone = Map.get(args, "timezone", "Etc/UTC")
datetime = get_datetime_in_timezone(timezone)
formatted =
case format do
"iso8601" -> DateTime.to_iso8601(datetime)
"rfc3339" -> DateTime.to_string(datetime)
"unix" -> DateTime.to_unix(datetime) |> to_string()
"human" -> Calendar.strftime(datetime, "%A, %B %-d, %Y at %I:%M %p %Z")
_ -> DateTime.to_iso8601(datetime)
end
%{
datetime: formatted,
format: format,
timezone: timezone,
unix_timestamp: DateTime.to_unix(datetime),
date: Date.to_iso8601(DateTime.to_date(datetime)),
time: Time.to_iso8601(DateTime.to_time(datetime))
}
end
@doc """
Calculate the difference between two dates.
## Arguments
- date1: First date in ISO8601 format (YYYY-MM-DD)
- date2: Second date in ISO8601 format (YYYY-MM-DD)
- unit: "days" (default), "weeks", "months", "years"
"""
def date_difference(_ctx, args) do
# Support both date1/date2 and start_date/end_date parameter names
date1_str = Map.get(args, "date1") || Map.get(args, "start_date")
date2_str = Map.get(args, "date2") || Map.get(args, "end_date")
unit = Map.get(args, "unit", "days")
with {:ok, date1} <- Date.from_iso8601(date1_str),
{:ok, date2} <- Date.from_iso8601(date2_str) do
diff_days = Date.diff(date2, date1)
result =
case unit do
"days" -> diff_days
"weeks" -> Float.round(diff_days / 7, 2)
# Average month length
"months" -> Float.round(diff_days / 30.44, 2)
# Account for leap years
"years" -> Float.round(diff_days / 365.25, 2)
_ -> diff_days
end
%{
date1: date1_str,
date2: date2_str,
difference: result,
unit: unit,
diff_days: diff_days,
direction: if(diff_days > 0, do: "future", else: "past")
}
else
{:error, reason} ->
%{error: "Invalid date format: #{inspect(reason)}"}
end
end
@doc """
Add or subtract days from a date.
## Arguments
- date: Date in ISO8601 format (YYYY-MM-DD), defaults to today
- days: Number of days to add (positive) or subtract (negative)
"""
def add_days(_ctx, args) do
date_str = Map.get(args, "date")
days = Map.get(args, "days", 0)
base_date =
if date_str do
case Date.from_iso8601(date_str) do
{:ok, date} -> date
_ -> Date.utc_today()
end
else
Date.utc_today()
end
result_date = Date.add(base_date, days)
%{
original_date: Date.to_iso8601(base_date),
days_added: days,
result_date: Date.to_iso8601(result_date),
result_day_of_week: Calendar.strftime(result_date, "%A"),
is_weekend: Date.day_of_week(result_date) in [6, 7]
}
end
@doc """
Check if a date is a weekend.
## Arguments
- date: Date in ISO8601 format (YYYY-MM-DD), defaults to today
"""
def is_weekend(_ctx, args) do
date_str = Map.get(args, "date")
date =
if date_str do
case Date.from_iso8601(date_str) do
{:ok, date} -> date
_ -> Date.utc_today()
end
else
Date.utc_today()
end
day_of_week = Date.day_of_week(date)
is_weekend = day_of_week in [6, 7]
%{
date: Date.to_iso8601(date),
day_of_week: Calendar.strftime(date, "%A"),
day_number: day_of_week,
is_weekend: is_weekend,
is_weekday: !is_weekend
}
end
@doc """
Get the day of the week for a given date.
## Arguments
- date: Date in ISO8601 format (YYYY-MM-DD), defaults to today
"""
def day_of_week(_ctx, args) do
date_str = Map.get(args, "date")
date =
if date_str do
case Date.from_iso8601(date_str) do
{:ok, date} -> date
_ -> Date.utc_today()
end
else
Date.utc_today()
end
%{
date: Date.to_iso8601(date),
day_of_week: Calendar.strftime(date, "%A"),
day_abbreviation: Calendar.strftime(date, "%a"),
day_number: Date.day_of_week(date),
is_weekend: Date.day_of_week(date) in [6, 7]
}
end
@doc """
Parse a human-readable date string.
## Arguments
- date_string: Date in various formats (ISO8601, MM/DD/YYYY, DD/MM/YYYY, etc.)
- format: Expected format hint ("iso8601", "us", "eu")
"""
def parse_date(_ctx, args) do
# Support both "date_string" and "date" parameter names
date_string = Map.get(args, "date_string") || Map.get(args, "date")
format_hint = Map.get(args, "format", "iso8601")
result =
case format_hint do
"iso8601" ->
Date.from_iso8601(date_string)
"us" ->
# MM/DD/YYYY
parse_us_date(date_string)
"eu" ->
# DD/MM/YYYY
parse_eu_date(date_string)
_ ->
Date.from_iso8601(date_string)
end
case result do
{:ok, date} ->
%{
original: date_string,
parsed_date: Date.to_iso8601(date),
day_of_week: Calendar.strftime(date, "%A"),
success: true
}
{:error, reason} ->
%{
original: date_string,
error: inspect(reason),
success: false
}
end
end
@doc """
Get the start and end of the current week.
## Arguments
- timezone: Optional timezone
- week_start: Day to consider start of week (1=Monday default, 7=Sunday)
"""
def current_week(_ctx, args) do
timezone = Map.get(args, "timezone", "Etc/UTC")
# Monday
week_start = Map.get(args, "week_start", 1)
today = get_date_in_timezone(timezone)
day_of_week = Date.day_of_week(today)
# Calculate days to subtract to get to week start
days_to_start = rem(day_of_week - week_start + 7, 7)
week_start_date = Date.add(today, -days_to_start)
week_end_date = Date.add(week_start_date, 6)
%{
week_start: Date.to_iso8601(week_start_date),
week_end: Date.to_iso8601(week_end_date),
current_date: Date.to_iso8601(today),
days_in_week: generate_week_days(week_start_date)
}
end
@doc """
Get information about the current month.
## Arguments
- timezone: Optional timezone
"""
def current_month(_ctx, args) do
timezone = Map.get(args, "timezone", "Etc/UTC")
today = get_date_in_timezone(timezone)
first_day = Date.beginning_of_month(today)
last_day = Date.end_of_month(today)
days_in_month = Date.diff(last_day, first_day) + 1
%{
month: Calendar.strftime(today, "%B"),
month_number: today.month,
year: today.year,
first_day: Date.to_iso8601(first_day),
last_day: Date.to_iso8601(last_day),
days_in_month: days_in_month,
current_date: Date.to_iso8601(today)
}
end
# Private helper functions
defp get_date_in_timezone(timezone) do
DateTime.now!(timezone)
|> DateTime.to_date()
end
defp get_time_in_timezone(timezone) do
DateTime.now!(timezone)
|> DateTime.to_time()
end
defp get_datetime_in_timezone(timezone) do
DateTime.now!(timezone)
end
defp parse_us_date(date_string) do
# MM/DD/YYYY
case String.split(date_string, "/") do
[month, day, year] ->
Date.from_iso8601(
"#{year}-#{String.pad_leading(month, 2, "0")}-#{String.pad_leading(day, 2, "0")}"
)
_ ->
{:error, :invalid_format}
end
end
defp parse_eu_date(date_string) do
# DD/MM/YYYY
case String.split(date_string, "/") do
[day, month, year] ->
Date.from_iso8601(
"#{year}-#{String.pad_leading(month, 2, "0")}-#{String.pad_leading(day, 2, "0")}"
)
_ ->
{:error, :invalid_format}
end
end
defp generate_week_days(start_date) do
0..6
|> Enum.map(fn offset ->
date = Date.add(start_date, offset)
%{
date: Date.to_iso8601(date),
day_of_week: Calendar.strftime(date, "%A"),
is_weekend: Date.day_of_week(date) in [6, 7]
}
end)
end
end