Time Notes

2 min read

Parse timestamps, measure elapsed time, and validate duration strings in Katalog templates. Primarily used in status.fields to surface human-readable time values and in when: conditions to drive time-based rotation and expiry logic.

All timestamp notes accept RFC3339, RFC3339Nano, 2006-01-02T15:04:05Z, and YYYY-MM-DD formats. Safe zero values are returned for unparseable input.

Reference

NoteDescription
timeAgoReturn a human-readable elapsed-time string from a timestamp.
timeSinceReturn the number of seconds elapsed since a timestamp as an integer.
isExpiredReturn true when a timestamp plus a duration is in the past.
timeFormatReformat a timestamp string using Go’s time format layout.
durationSecondsParse a Go duration string and return the total number of seconds as an integer.
durationAddAdd two Go duration strings and return the result as a canonical duration string.
durationValidReturn true when the string is a valid Go duration.

Examples

# timeAgo
status:
  fields:
    - path: lastSyncAgo
      value: "{{ timeAgo .children.cronjob.status.lastScheduleTime }}"
      # → "5s ago" / "12m ago" / "3h ago" / "2d ago"

    - path: createdAgo
      value: "{{ timeAgo .metadata.creationTimestamp }}"

# timeSince
# Gate rotation on time elapsed (30 days = 2592000 seconds)
when:
  - field: "{{ timeSince (index .metadata.annotations \"myorg.io/last-rotated\") }}"
    operator: gte
    value: "2592000"

# isExpired
# Recreate the secret when the rotation annotation says it is due
onCreate:
  secrets:
    - name: "{{ .metadata.name }}-token"
      once: true
      rotateAfter: 720h

# Gate a resource on whether a timestamp annotation is past its TTL
when:
  - field: "{{ isExpired (index .metadata.annotations \"myorg.io/generated-at\") \"720h\" }}"
    equals: "true"

# timeFormat
status:
  fields:
    - path: createdDate
      value: "{{ timeFormat .metadata.creationTimestamp \"Jan 2, 2006\" }}"
      # → "Apr 13, 2026"

    - path: expiresAt
      value: "{{ timeFormat .status.expirationTime \"2006-01-02\" }}"

# durationSeconds
status:
  fields:
    - path: resyncIntervalSeconds
      value: "{{ durationSeconds .spec.resyncInterval }}"
      # "5m" → 300  |  "1h30m" → 5400

# durationAdd
# Compute the total window from base + buffer
status:
  fields:
    - path: totalWindow
      value: "{{ durationAdd .spec.baseWindow .spec.buffer }}"
      # "5m" + "30s" → "5m30s"

# durationValid
spec:
  crds:
    myApp:
      validate:
        - message: "spec.rotationPeriod must be a valid Go duration (e.g. 720h, 30m)"
          deny:
            - field: "{{ durationValid .spec.rotationPeriod }}"
              equals: "false"