import re
from datetime import UTC, timezone, tzinfo
import dateparser
import demistomock as demisto # noqa: F401
import pytz
from CommonServerPython import * # noqa: F401
def timezone_abbreviations(utc_offset: Optional[timedelta]) -> set[str]:
tznames = set()
if utc_offset is not None:
now = datetime.now(pytz.utc)
for tz in map(pytz.timezone, pytz.all_timezones_set):
if (d := now.astimezone(tz)).utcoffset() == utc_offset and (tzname := d.tzname()) and tzname[0:1] not in ("+", "-"):
tznames.add(tzname)
return tznames
def detect_time_zone(value: Any) -> tzinfo:
if isinstance(value, int):
return timezone(timedelta(minutes=value))
if isinstance(value, str):
value = value.strip()
if value == "Z":
return timezone(timedelta(hours=0))
elif m := re.fullmatch("([+-])([0-9]{1,2})(:?([0-9]{2}))?", value):
# +H, +HMM, +HHMM, +H:MM, +HH:MM
offset = ((int(m[2]) * 60) + int(m[4] or 0)) * (-1 if m[1] == "-" else 1)
return timezone(timedelta(minutes=offset))
elif m := re.match(r"(^|\s)(UTC|GMT)([+-])([0-9]{1,2})(:?([0-9]{2}))?($|\s)", value):
# Includes: UTC+H, UTC+HMM, UTC+HHMM, UTC+H:MM, UTC+HH:MM
offset = ((int(m[4]) * 60) + int(m[6] or 0)) * (-1 if m[3] == "-" else 1)
return timezone(timedelta(minutes=offset))
else:
# Try to parse as zone info
try:
return pytz.timezone(value)
except Exception:
pass
# Try to parse as time string
try:
d = dateparser.parse(value)
if d is not None and d.tzinfo is not None:
return d.tzinfo
except Exception:
pass
raise DemistoException(f"Unable to extract time zone - {value}")
def parse_date_time_value(value: Any) -> datetime:
"""Parse a date time value
:param value: The date or time to parse
:return: aware datetime object
"""
if value in [None, ""]:
return datetime.now(UTC)
if isinstance(value, int):
# Parse as time stamp
try:
# Considered the value as seconds > milliseconds > microseconds when it's too large (> uint max).
# (Currently later than 2106-02-07 06:28:15)
while value > 4294967295:
value /= 1000
return datetime.fromtimestamp(value).astimezone(UTC)
except Exception as err:
raise DemistoException(f"Error with input date / time - {err}")
if isinstance(value, str):
value = value.strip()
try:
# Parse as time stamp
return parse_date_time_value(int(value))
except (TypeError, ValueError):
pass
try:
date_time = dateparser.parse(value)
assert date_time is not None, f"could not parse {value}"
if date_time.tzinfo is not None:
return date_time
date_time = dateparser.parse(value, settings={"TIMEZONE": "UTC", "RETURN_AS_TIMEZONE_AWARE": True})
assert date_time is not None, f"could not parse {value}"
return date_time
except Exception as err:
raise DemistoException(f"Error with input date / time - {err}")
def main():
try:
args = demisto.args()
date_time = parse_date_time_value(args.get("value"))
if time_zone := args.get("time_zone"):
date_time = date_time.astimezone(detect_time_zone(time_zone))
time_components = {
"year": date_time.year,
"year_4_digit": date_time.strftime("%Y"),
"month": date_time.month,
"month_3_letter": date_time.strftime("%b"),
"month_full_name": date_time.strftime("%B"),
"month_2_digit": date_time.strftime("%m"),
"day": date_time.day,
"day_2_digit": date_time.strftime("%d"),
"day_of_week": int(date_time.strftime("%w")),
"day_of_week_3_letter": date_time.strftime("%a"),
"day_of_week_full_name": date_time.strftime("%A"),
"day_of_year": int(date_time.strftime("%j")),
"day_of_year_3_digit": date_time.strftime("%j"),
"hour": date_time.hour,
"hour_12_clock": (date_time.hour % 12) or 12,
"hour_2_digit_24_clock": date_time.strftime("%H"),
"hour_2_digit_12_clock": date_time.strftime("%I"),
"hour_of_day": date_time.hour + (date_time.minute / 60) + (date_time.second / 60 / 60),
"minute": date_time.minute,
"minute_2_digit": date_time.strftime("%M"),
"minute_of_day": (date_time.hour * 24) + date_time.minute + (date_time.second / 60),
"second": date_time.second,
"second_2_digit": date_time.strftime("%S"),
"second_of_day": (date_time.hour * 24 * 60) + (date_time.minute * 60) + date_time.second,
"millisecond": int(date_time.microsecond / 1000),
"period_12_clock": date_time.strftime("%p"),
"time_zone_hhmm": date_time.strftime("%z"),
"time_zone_offset": (date_time.utcoffset() or timedelta(hours=0)).total_seconds() / 60,
"time_zone_abbreviations": sorted(timezone_abbreviations(date_time.utcoffset())),
"unix_epoch_time": int(date_time.timestamp()),
"iso_8601": date_time.isoformat(),
"y-m-d": f"{date_time.year}-{date_time.month}-{date_time.day}",
"yyyy-mm-dd": f"{date_time.strftime('%Y-%m-%d')}",
"h:m:s": f"{date_time.hour}:{date_time.minute}:{date_time.second}",
"H:m:s": f"{(date_time.hour % 12) or 12}:{date_time.minute}:{date_time.second}",
"hh:mm:ss": f"{date_time.strftime('%I:%M:%S')}",
"HH:mm:ss": f"{date_time.strftime('%H:%M:%S')}",
}
if key := args.get("key"):
if (component := time_components.get(key)) is None:
raise DemistoException(f"No key is found in the time components - {key}")
return_results(component)
else:
return_results(time_components)
except Exception as err:
# Don't return an error by return_error() as this is transformer.
raise DemistoException(str(err))
if __name__ in ("__main__", "builtin", "builtins"):
main()
README
Takes a date or time input and get time components in a specific time zone.
Returns a dictionary with the following components.
year
year_4_digit
month
month_3_letter
month_full_name
month_2_digit
day
day_2_digit
day_of_week (Sun:0, Sat:6)
day_of_week_3_letter
day_of_week_full_name
day_of_year
day_of_year_3_digit
hour
hour_12_clock
hour_2_digit_24_clock
hour_2_digit_12_clock
hour_of_day
minute
minute_2_digit
minute_of_day
second
second_2_digit
second_of_day
millisecond
period_12_clock
time_zone_hhmm
time_zone_offset
time_zone_abbreviations
unix_epoch_time
iso_8601
y-m-d
yyyy-mm-dd
h:m:s
H:m:s
hh:mm:ss
HH:mm:ss
Script Data
Name
Description
Script Type
python3
Tags
transformer, date
Cortex XSOAR Version
6.5.0
Inputs
Argument Name
Description
value
Input date or time in a format that is supported by the dateparser.parse() function as outlined here- https://dateparser.readthedocs.io/en/latest/#popular-formats. For example: ‘2020-01-01’ or ‘1999/02/03 12:01:59’. (Default is the current time). Assume given time is in UTC if time zone is not detected.
time_zone
The time zone (e.g. -0400, +09:00) or time string to extract a time zone
key
The name of a key to choose which time component to return
Get a specific time component (day_of_week_full_name)
Parameters
Argument Name
Value
value
2022-01-23 01:23:45 +00:00
time_zone
key
day_of_week_full_name
Output
Sunday
Get a time component in a specific time zone given by zone info
Parameters
Argument Name
Value
value
2022-01-23 01:23:45 +00:00
time_zone
Asia/Tokyo
key
iso_8601
Output
2022-01-23T10:23:45+09:00
Get a time component in a time zone which is extracted from the time string given to time_zone
Parameters
Argument Name
Value
value
2022-01-23 01:23:45 +00:00
time_zone
2022-01-01 00:00:00 +09:00
key
iso_8601
Output
2022-01-23T10:23:45+09:00
Get a current time in a specific time zone
Parameters
Argument Name
Value
value
now
time_zone
+09:00
key
iso_8601
Output
2022-09-30T12:34:56+09:00
Tips
Build a custom time format string
You can create a custom time format in combination with the DT transformer on the chain of transformers. For example, now you want to create a RFC 1123 date string such as Thu, 10 Nov 2022 08:01:44 +0200, and have the following results from the TimeComponents.