AWX extra_var for datetimes exhibiting different behavior between Ansible 2.18 and 2.19

I’ve been making my way through converting my playbooks from 2.18 to 2.19 and I’ve noticed odd (to me) behavior when supplying extra_vars via a job template in AWX between the two.

Here’s the playbook:

---
- name: Show fact
  hosts: localhost
  gather_facts: false
  become: false
  connection: local
  tasks:
    - name: Show fact
      ansible.builtin.debug:
        msg: "date_fact is {{ date_fact }}"

I supply an extra_var via AWX for date_fact of "date_fact": "2025-09-11T12:29:45.000000+00:00"

The results of the playbook run for the 2.18 execution environment are:

PLAY [Show fact] ***************************************************************

TASK [Show fact] ***************************************************************

ok: [localhost] => {
    "msg": "date_fact is 2025-09-11T12:29:45.000000+00:00"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

For the 2.19 execution environment it is:

PLAY [Show fact] ***************************************************************

TASK [Show fact] ***************************************************************

ok: [localhost] => {
    "msg": "date_fact is 2025-09-11 12:29:45+00:00"
}

PLAY RECAP *********************************************************************

localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

I can’t seem to replicate this behavior running ansible-playbook using 2.19 locally, it seems to only happen in the AWX template with the 2.19 EE.

So, in short, 2025-09-11T12:29:45.000000+00:00 shows as 2025-09-11T12:29:45.000000+00:00 for Ansible 2.18 EE via an AWX template extra_var. However, 2025-09-11T12:29:45.000000+00:00 turns to 2025-09-11 12:29:45+00:00 for Ansible 2.19 EE via an AWX template extra_var.

My use-case is that I have other jobs that trigger this job and they send a datetime stamp as an extra_var for it to send to an external API endpoint so the source jobs don’t need to wait/care about this reporting process.

I was able to get it to work with a strftime conversion in my original job, but I’m curious at the root cause of this as it may affect other playbooks in different ways. I didn’t see anything in Ansible-core 2.19 Porting Guide — Ansible Community Documentation pardon me if I missed it.

I don’t have an exact answer for you, as I also cannot replicate this behavior exactly. But I can probably give you enough information about the changes made in 2.19 that would help this make sense.

  1. AWX writes out the extra vars file in YAML format for consumption by ansible-playbook

  2. YAML has a timestamp type, and your ISO8601 formatted string matches the regex used by YAML to parse it into this timestamp format. What this means for python, is that pyyaml converts it to the native datetime format using the datetime module:

    >>> data = '''date_fact: 2025-09-11T12:29:45.000000+00:00'''
    >>> yaml.safe_load(data)
    {'date_fact': datetime.datetime(2025, 9, 11, 12, 29, 45, tzinfo=datetime.timezone.utc)}
    
  3. The process of switching over the the jinja2 native environment fully, means that we carry the exact types through the jinja templating process much better than before

  4. The default behavior of stringifying a datetime.datetime object is to format it like ISO8601 but explicitly using a space as the separator character instead of T

Something in the past must have caused it be serialized back to an explicit string in iso8601 format during the process in the past, or otherwise it was somehow never consumed as a YAML
timestamp for some reason that I cannot guess at currently.

1 Like

Interesting, thank you!

I didn’t notice the "T vs. " delimiter change. I focused on “.000000” getting stripped off the seconds.

Probably whatever change caused the one also caused the other. [sigh]