Introduction
Working with dates and timestamps is essential in Ansible automation. Whether you're creating timestamped backup directories, naming log files, setting file expiration dates, or adding deployment timestamps, Ansible provides several methods to capture and format dates dynamically during playbook execution.
This guide covers all the ways to work with dates and times in Ansible — from the built-in ansible_date_time facts to the now() function, strftime filter, and pipe lookup.
Method 1: ansible_date_time Facts
The simplest way to access the current date and time is through Ansible's gathered facts. When gather_facts: true (the default), Ansible automatically collects date/time information:
- name: Display date facts
hosts: all
tasks:
- name: Show all date/time facts
ansible.builtin.debug:
var: ansible_date_time
This provides a dictionary with these fields:
| Field | Example | Description |
|---|---|---|
date | 2024-02-26 | Current date (YYYY-MM-DD) |
time | 14:30:45 | Current time (HH:MM:SS) |
epoch | 1708958445 | Unix timestamp |
iso8601 | 2024-02-26T14:30:45Z | ISO 8601 format |
iso8601_basic | 20240226T143045 | Compact ISO format |
year | 2024 | Four-digit year |
month | 02 | Two-digit month |
day | 26 | Two-digit day |
hour | 14 | Two-digit hour (24h) |
minute | 30 | Two-digit minute |
second | 45 | Two-digit second |
weekday | Monday | Day of the week |
tz | UTC | Timezone abbreviation |
Important: ansible_date_time is captured once at the start of fact gathering. It does not update during playbook execution. If your playbook runs for 30 minutes, the timestamp will still reflect when facts were gathered.
- name: Create timestamped backup directory
ansible.builtin.file:
path: "/backups/{{ ansible_date_time.date }}"
state: directory
mode: '0755'
Method 2: The now() Function
For a timestamp that reflects the actual moment of task execution, use the now() function (available since Ansible 2.8):
- name: Get current timestamp at execution time
ansible.builtin.set_fact:
deploy_time: "{{ now(utc=true) }}"
deploy_date: "{{ now(fmt='%Y-%m-%d') }}"
deploy_timestamp: "{{ now(fmt='%Y%m%d_%H%M%S') }}"
The now() function accepts these parameters:
utc=true— Return UTC time instead of local timefmt='%Y-%m-%d'— Format string using Python strftime directives
- name: Practical now() examples
hosts: all
tasks:
- name: Set deployment facts
ansible.builtin.set_fact:
backup_dir: "/backups/{{ now(fmt='%Y-%m-%d') }}"
log_file: "deploy-{{ now(fmt='%Y%m%d-%H%M%S') }}.log"
expiry_notice: "Deployed on {{ now(fmt='%B %d, %Y at %H:%M') }}"
Method 3: The pipe Lookup
For maximum flexibility, use the pipe lookup to execute the system date command:
- name: Set a fact for the date
ansible.builtin.set_fact:
unix_date: "{{ lookup('pipe', 'date +%Y-%m-%d-%H%M') }}"
This executes on the control node (not the remote host) and captures the output. Useful when you need:
- Non-standard date formats
- Timezone conversions using system tools
- Date arithmetic via
date -d
- name: Advanced date operations with pipe
ansible.builtin.set_fact:
current_date: "{{ lookup('pipe', 'date +%Y-%m-%d') }}"
yesterday: "{{ lookup('pipe', 'date -d yesterday +%Y-%m-%d') }}"
next_week: "{{ lookup('pipe', 'date -d \"+7 days\" +%Y-%m-%d') }}"
epoch_seconds: "{{ lookup('pipe', 'date +%s') }}"
Method 4: The strftime Filter
The strftime filter converts a Unix timestamp to a formatted date string:
- name: Format timestamps with strftime
ansible.builtin.debug:
msg: "{{ '%Y-%m-%d %H:%M:%S' | strftime }}"
# Output: 2024-02-26 14:30:45
- name: Convert a known epoch to formatted date
ansible.builtin.debug:
msg: "{{ '%B %d, %Y' | strftime(1708958445) }}"
# Output: February 26, 2024
Common Date Format Directives
| Directive | Meaning | Example |
|---|---|---|
%Y | Four-digit year | 2024 |
%m | Two-digit month | 02 |
%d | Two-digit day | 26 |
%H | Hour (24-hour) | 14 |
%M | Minute | 30 |
%S | Second | 45 |
%B | Full month name | February |
%b | Abbreviated month | Feb |
%A | Full weekday name | Monday |
%j | Day of year | 057 |
%s | Unix epoch | 1708958445 |
%Z | Timezone name | UTC |
Practical Use Cases
Timestamped Backup Directories
- name: Create daily backup
hosts: all
tasks:
- name: Set backup path
ansible.builtin.set_fact:
backup_path: "/backups/{{ now(fmt='%Y-%m-%d_%H%M%S') }}"
- name: Create backup directory
ansible.builtin.file:
path: "{{ backup_path }}"
state: directory
mode: '0755'
- name: Backup configuration files
ansible.builtin.archive:
path: /etc/myapp/
dest: "{{ backup_path }}/config-backup.tar.gz"
Log File Naming
- name: Run deployment with timestamped log
ansible.builtin.shell: |
/opt/deploy.sh >> /var/log/deploys/deploy-{{ now(fmt='%Y%m%d-%H%M%S') }}.log 2>&1
Cleanup Old Files
- name: Delete backups older than 30 days
ansible.builtin.find:
paths: /backups
age: 30d
recurse: false
file_type: directory
register: old_backups
- name: Remove old backup directories
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ old_backups.files }}"
Deployment Metadata
- name: Write deployment metadata
ansible.builtin.copy:
content: |
deployed_at: {{ now(utc=true, fmt='%Y-%m-%dT%H:%M:%SZ') }}
deployed_by: {{ ansible_user_id }}
version: {{ app_version }}
hosts: {{ ansible_play_hosts | join(', ') }}
dest: /opt/app/DEPLOYMENT.yml
Conditional Execution Based on Time
- name: Run maintenance only during off-hours
ansible.builtin.include_tasks: maintenance.yml
when: now(fmt='%H') | int >= 22 or now(fmt='%H') | int <= 6
Date Arithmetic in Ansible
For date calculations, combine multiple approaches:
- name: Calculate dates
ansible.builtin.set_fact:
# Current epoch + 7 days in seconds
next_week_epoch: "{{ now(fmt='%s') | int + (7 * 86400) }}"
- name: Format the future date
ansible.builtin.set_fact:
next_week_date: "{{ '%Y-%m-%d' | strftime(next_week_epoch | int) }}"
- name: Display
ansible.builtin.debug:
msg: "Certificate expires: {{ next_week_date }}"
Timezone Handling
- name: Work with timezones
ansible.builtin.set_fact:
utc_time: "{{ now(utc=true, fmt='%Y-%m-%d %H:%M:%S %Z') }}"
local_time: "{{ now(fmt='%Y-%m-%d %H:%M:%S %Z') }}"
# For specific timezone conversions, use pipe lookup
- name: Get Tokyo time
ansible.builtin.set_fact:
tokyo_time: "{{ lookup('pipe', 'TZ=Asia/Tokyo date +%Y-%m-%d\\ %H:%M:%S') }}"
Which Method Should You Use?
| Method | Updates at Runtime | Control Node | Remote Host | Best For |
|---|---|---|---|---|
ansible_date_time | No (gathered once) | ✅ | — | Simple date references |
now() | Yes | ✅ | — | Accurate timestamps during execution |
pipe lookup | Yes | ✅ | — | Complex date formatting, date math |
strftime filter | Yes | ✅ | — | Converting epoch values |
For most use cases, now() is the recommended approach — it's clean, built-in, and evaluates at task execution time.
Related Articles
- Mastering Dynamic Variable Creation with set_fact
- Ansible Magic Variables Reference
- Using Date, Time and Timestamp in Ansible
- Mastering Time in Ansible: The now() Function
- Ansible Best Practices Guide
- Schedule a Cron Job with Ansible
- Ansible Jinja2 Length Filter
Conclusion
Ansible provides multiple tools for working with dates and timestamps, each suited to different needs. The built-in ansible_date_time facts work for simple references, the now() function gives you accurate runtime timestamps, the pipe lookup enables system-level date commands, and the strftime filter handles epoch-to-string conversions.
For most automation tasks — backup naming, deployment logging, and metadata generation — the now() function combined with set_fact provides the cleanest and most reliable approach.