Introduction
Working with dates, times, and timestamps is essential for tasks like naming backups, rotating logs, scheduling operations, and adding deployment metadata. Ansible provides the ansible_date_time fact and powerful Jinja2 filters for all date/time operations.
This guide covers everything from basic timestamp usage to advanced date arithmetic and conditional time-based logic.
The ansible_date_time Fact
The ansible_date_time variable is automatically populated when gather_facts: true (the default). It contains:
{
"ansible_date_time": {
"date": "2026-04-20",
"day": "20",
"epoch": "1745187600",
"epoch_int": "1745187600",
"hour": "22",
"iso8601": "2026-04-20T22:00:00Z",
"iso8601_basic": "20260420T220000000000",
"iso8601_basic_short": "20260420T220000",
"iso8601_micro": "2026-04-20T22:00:00.000000Z",
"minute": "00",
"month": "04",
"second": "00",
"time": "22:00:00",
"tz": "UTC",
"tz_dst": "UTC",
"tz_offset": "+0000",
"weekday": "Monday",
"weekday_number": "1",
"weeknumber": "16",
"year": "2026"
}
}
Important: ansible_date_time is captured at the start of fact gathering. It does NOT update during playbook execution. For real-time timestamps within a playbook, use the now() function or strftime filter.
Basic Usage
Display Date and Time
---
- name: Date and time examples
hosts: all
gather_facts: true
tasks:
- name: Show full date_time object
ansible.builtin.debug:
var: ansible_date_time
- name: Show ISO 8601 timestamp
ansible.builtin.debug:
msg: "Current time: {{ ansible_date_time.iso8601 }}"
- name: Show date only
ansible.builtin.debug:
msg: "Today is {{ ansible_date_time.date }} ({{ ansible_date_time.weekday }})"
Use in File Names
---
- name: Timestamp in filenames
hosts: all
gather_facts: true
tasks:
- name: Create backup with date stamp
ansible.builtin.copy:
src: /etc/nginx/nginx.conf
dest: "/backup/nginx.conf.{{ ansible_date_time.date }}"
remote_src: true
- name: Create backup with full timestamp (no colons)
ansible.builtin.copy:
src: /etc/nginx/nginx.conf
dest: "/backup/nginx.conf.{{ ansible_date_time.iso8601_basic_short }}"
remote_src: true
# Result: nginx.conf.20260420T220000
Use in Variables
---
- name: Date-based variables
hosts: all
gather_facts: true
vars:
backup_dir: "/backup/{{ ansible_date_time.date }}"
log_file: "/var/log/deploy-{{ ansible_date_time.epoch }}.log"
release_tag: "v1.0.0-{{ ansible_date_time.iso8601_basic_short }}"
tasks:
- name: Create daily backup directory
ansible.builtin.file:
path: "{{ backup_dir }}"
state: directory
mode: '0755'
The now() Function and strftime Filter
For real-time timestamps (not captured at fact-gather time), use now():
---
- name: Real-time timestamps
hosts: all
gather_facts: false # Don't need facts for now()
tasks:
- name: Current UTC time
ansible.builtin.debug:
msg: "Now: {{ now(utc=true) }}"
- name: Custom format with strftime
ansible.builtin.debug:
msg: "{{ now(utc=true).strftime('%Y-%m-%d_%H-%M-%S') }}"
# Result: 2026-04-20_22-15-30
- name: Unix epoch
ansible.builtin.debug:
msg: "Epoch: {{ now(utc=true).strftime('%s') }}"
Common strftime Formats
| Format | Example | Description |
|---|---|---|
%Y-%m-%d | 2026-04-20 | ISO date |
%H:%M:%S | 22:15:30 | Time (24h) |
%Y%m%d%H%M%S | 20260420221530 | Compact timestamp |
%s | 1745187600 | Unix epoch |
%A | Monday | Weekday name |
%B | April | Month name |
%d/%m/%Y | 20/04/2026 | European date |
%m/%d/%Y | 04/20/2026 | US date |
%Y-%m-%dT%H:%M:%SZ | 2026-04-20T22:15:30Z | ISO 8601 |
Advanced Patterns
Date Arithmetic (to_datetime Filter)
---
- name: Date arithmetic
hosts: localhost
gather_facts: true
tasks:
- name: Calculate days since epoch
ansible.builtin.debug:
msg: "Days since epoch: {{ (ansible_date_time.epoch | int) // 86400 }}"
- name: Check if file is older than 7 days
ansible.builtin.find:
paths: /tmp
age: 7d
recurse: false
register: old_files
- name: Show old files
ansible.builtin.debug:
msg: "{{ old_files.matched }} files older than 7 days"
Conditional Logic Based on Time
---
- name: Time-based conditionals
hosts: all
gather_facts: true
tasks:
- name: Only run during business hours (9-17 UTC)
ansible.builtin.debug:
msg: "Running during business hours"
when: ansible_date_time.hour | int >= 9 and ansible_date_time.hour | int < 17
- name: Only run on weekdays
ansible.builtin.debug:
msg: "Weekday deployment"
when: ansible_date_time.weekday_number | int >= 1 and ansible_date_time.weekday_number | int <= 5
- name: Only run on first day of month
ansible.builtin.debug:
msg: "Monthly maintenance"
when: ansible_date_time.day == "01"
- name: Weekend maintenance window
ansible.builtin.debug:
msg: "Weekend maintenance"
when: ansible_date_time.weekday in ['Saturday', 'Sunday']
Deployment Metadata
---
- name: Deployment with timestamp metadata
hosts: webservers
gather_facts: true
vars:
deploy_version: "2.1.0"
tasks:
- name: Write deployment manifest
ansible.builtin.copy:
dest: /opt/app/DEPLOY_INFO
content: |
version: {{ deploy_version }}
deployed_at: {{ ansible_date_time.iso8601 }}
deployed_by: {{ lookup('env', 'USER') }}
target_host: {{ inventory_hostname }}
ansible_version: {{ ansible_version.full }}
mode: '0644'
- name: Tag deployment in log
ansible.builtin.lineinfile:
path: /var/log/deployments.log
line: "{{ ansible_date_time.iso8601 }} | v{{ deploy_version }} | {{ inventory_hostname }}"
create: true
mode: '0644'
Log Rotation with Date Stamps
---
- name: Rotate logs with timestamps
hosts: all
gather_facts: true
tasks:
- name: Check if log is large
ansible.builtin.stat:
path: /var/log/app/application.log
register: log_stat
- name: Rotate log if > 100MB
when: log_stat.stat.exists and log_stat.stat.size > 104857600
block:
- name: Move current log to dated archive
ansible.builtin.command:
cmd: "mv /var/log/app/application.log /var/log/app/application.{{ ansible_date_time.iso8601_basic_short }}.log"
changed_when: true
- name: Compress rotated log
ansible.builtin.command:
cmd: "gzip /var/log/app/application.{{ ansible_date_time.iso8601_basic_short }}.log"
changed_when: true
- name: Restart app to create new log
ansible.builtin.systemd:
name: myapp
state: restarted
Complete Playbook: Backup with Timestamp
---
- name: Database backup with timestamped archive
hosts: dbservers
gather_facts: true
become: true
vars:
backup_base: /backup/postgres
backup_name: "pgdump_{{ ansible_date_time.date }}_{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}"
retention_days: 30
tasks:
- name: Create backup directory
ansible.builtin.file:
path: "{{ backup_base }}"
state: directory
owner: postgres
group: postgres
mode: '0700'
- name: Run PostgreSQL dump
ansible.builtin.command:
cmd: "pg_dumpall -f {{ backup_base }}/{{ backup_name }}.sql"
become_user: postgres
changed_when: true
- name: Compress backup
ansible.builtin.command:
cmd: "gzip {{ backup_base }}/{{ backup_name }}.sql"
changed_when: true
- name: Remove backups older than {{ retention_days }} days
ansible.builtin.find:
paths: "{{ backup_base }}"
patterns: "pgdump_*.sql.gz"
age: "{{ retention_days }}d"
register: old_backups
- name: Delete old backups
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ old_backups.files }}"
when: old_backups.matched > 0
- name: Log backup completion
ansible.builtin.lineinfile:
path: /var/log/backup.log
line: "{{ ansible_date_time.iso8601 }} | SUCCESS | {{ backup_name }}.sql.gz | {{ inventory_hostname }}"
create: true
mode: '0644'
Troubleshooting
ansible_date_time is Undefined
Cause: gather_facts: false is set.
Fix: Either enable fact gathering or use now():
# Option 1: Enable facts
gather_facts: true
# Option 2: Use now() without facts
- debug:
msg: "{{ now(utc=true).strftime('%Y-%m-%d') }}"
Timestamp Doesn't Change Between Tasks
Cause: ansible_date_time is captured once at play start.
Fix: Use now() for real-time values:
- name: Real-time timestamp (changes each task)
ansible.builtin.debug:
msg: "{{ now(utc=true).isoformat() }}"
Timezone Issues
Cause: ansible_date_time.tz reflects the remote host's timezone, not the controller's.
Fix: Always use UTC for consistency:
- name: Force UTC
ansible.builtin.debug:
msg: "{{ now(utc=true).strftime('%Y-%m-%dT%H:%M:%SZ') }}"
Related Articles
- Ansible Jinja2 Templates — Advanced Jinja2 filters
- Ansible Variables — Variable types and precedence
- Ansible Facts — Gathering and using system facts
- Ansible cron Module — Scheduled task automation
Conclusion
Ansible provides flexible date/time handling through the ansible_date_time fact (captured at play start) and the now() function with strftime filters (real-time). Use ansible_date_time for consistent timestamps across all tasks in a play, and now() when you need the exact current time at each task. Always prefer UTC for cross-timezone consistency.