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:

FieldExampleDescription
date2024-02-26Current date (YYYY-MM-DD)
time14:30:45Current time (HH:MM:SS)
epoch1708958445Unix timestamp
iso86012024-02-26T14:30:45ZISO 8601 format
iso8601_basic20240226T143045Compact ISO format
year2024Four-digit year
month02Two-digit month
day26Two-digit day
hour14Two-digit hour (24h)
minute30Two-digit minute
second45Two-digit second
weekdayMondayDay of the week
tzUTCTimezone 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 time
  • fmt='%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

DirectiveMeaningExample
%YFour-digit year2024
%mTwo-digit month02
%dTwo-digit day26
%HHour (24-hour)14
%MMinute30
%SSecond45
%BFull month nameFebruary
%bAbbreviated monthFeb
%AFull weekday nameMonday
%jDay of year057
%sUnix epoch1708958445
%ZTimezone nameUTC

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?

MethodUpdates at RuntimeControl NodeRemote HostBest For
ansible_date_timeNo (gathered once)✅—Simple date references
now()Yes✅—Accurate timestamps during execution
pipe lookupYes✅—Complex date formatting, date math
strftime filterYes✅—Converting epoch values

For most use cases, now() is the recommended approach — it's clean, built-in, and evaluates at task execution time.

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.