Repository: oasis-open/cti-python-stix2

Issue:
AST make_constant() function doesn't make timestamp constants
If make_constant() can interpret the passed value as a timestamp, it will return a STIXdatetime object instead of a timestamp constant.  That results in an incorrect AST and messed up pattern format.  E.g.

```python
import stix2.patterns

comp_expr = stix2.patterns.EqualityComparisonExpression("a:b", "1993-07-12T22:27:23Z")
print(comp_expr)
```

results in

```
a:b = 1993-07-12 22:27:23+00:00
```



Comments:

Patch:
diff --git a/stix2/patterns.py b/stix2/patterns.py
index bbee7ac..f1472cd 100644
--- a/stix2/patterns.py
+++ b/stix2/patterns.py
@@ -227,7 +227,7 @@ def make_constant(value):
         return value
 
     try:
-        return parse_into_datetime(value)
+        return TimestampConstant(value)
     except (ValueError, TypeError):
         pass
 


Test patch:
diff --git a/stix2/test/v20/test_pattern_expressions.py b/stix2/test/v20/test_pattern_expressions.py
index fa9000e..526fe97 100644
--- a/stix2/test/v20/test_pattern_expressions.py
+++ b/stix2/test/v20/test_pattern_expressions.py
@@ -1,9 +1,11 @@
 import datetime
 
 import pytest
+import pytz
 
 import stix2
 from stix2.pattern_visitor import create_pattern_object
+import stix2.utils
 
 
 def test_create_comparison_expression():
@@ -482,6 +484,44 @@ def test_invalid_startstop_qualifier():
         )
 
 
+@pytest.mark.parametrize(
+    "input_, expected_class, expected_value", [
+        (1, stix2.patterns.IntegerConstant, 1),
+        (1.5, stix2.patterns.FloatConstant, 1.5),
+        ("abc", stix2.patterns.StringConstant, "abc"),
+        (True, stix2.patterns.BooleanConstant, True),
+        (
+            "2001-02-10T21:36:15Z", stix2.patterns.TimestampConstant,
+            stix2.utils.STIXdatetime(2001, 2, 10, 21, 36, 15, tzinfo=pytz.utc),
+        ),
+        (
+            datetime.datetime(2001, 2, 10, 21, 36, 15, tzinfo=pytz.utc),
+            stix2.patterns.TimestampConstant,
+            stix2.utils.STIXdatetime(2001, 2, 10, 21, 36, 15, tzinfo=pytz.utc),
+        ),
+    ],
+)
+def test_make_constant_simple(input_, expected_class, expected_value):
+    const = stix2.patterns.make_constant(input_)
+
+    assert isinstance(const, expected_class)
+    assert const.value == expected_value
+
+
+def test_make_constant_list():
+    list_const = stix2.patterns.make_constant([1, 2, 3])
+
+    assert isinstance(list_const, stix2.patterns.ListConstant)
+    assert all(
+        isinstance(elt, stix2.patterns.IntegerConstant)
+        for elt in list_const.value
+    )
+    assert all(
+        int_const.value == test_elt
+        for int_const, test_elt in zip(list_const.value, [1, 2, 3])
+    )
+
+
 def test_make_constant_already_a_constant():
     str_const = stix2.StringConstant('Foo')
     result = stix2.patterns.make_constant(str_const)
diff --git a/stix2/test/v21/test_pattern_expressions.py b/stix2/test/v21/test_pattern_expressions.py
index ac6a439..58cef3e 100644
--- a/stix2/test/v21/test_pattern_expressions.py
+++ b/stix2/test/v21/test_pattern_expressions.py
@@ -1,10 +1,12 @@
 import datetime
 
 import pytest
+import pytz
 from stix2patterns.exceptions import ParseException
 
 import stix2
 from stix2.pattern_visitor import create_pattern_object
+import stix2.utils
 
 
 def test_create_comparison_expression():
@@ -603,6 +605,44 @@ def test_invalid_startstop_qualifier():
         )
 
 
+@pytest.mark.parametrize(
+    "input_, expected_class, expected_value", [
+        (1, stix2.patterns.IntegerConstant, 1),
+        (1.5, stix2.patterns.FloatConstant, 1.5),
+        ("abc", stix2.patterns.StringConstant, "abc"),
+        (True, stix2.patterns.BooleanConstant, True),
+        (
+            "2001-02-10T21:36:15Z", stix2.patterns.TimestampConstant,
+            stix2.utils.STIXdatetime(2001, 2, 10, 21, 36, 15, tzinfo=pytz.utc),
+        ),
+        (
+            datetime.datetime(2001, 2, 10, 21, 36, 15, tzinfo=pytz.utc),
+            stix2.patterns.TimestampConstant,
+            stix2.utils.STIXdatetime(2001, 2, 10, 21, 36, 15, tzinfo=pytz.utc),
+        ),
+    ],
+)
+def test_make_constant_simple(input_, expected_class, expected_value):
+    const = stix2.patterns.make_constant(input_)
+
+    assert isinstance(const, expected_class)
+    assert const.value == expected_value
+
+
+def test_make_constant_list():
+    list_const = stix2.patterns.make_constant([1, 2, 3])
+
+    assert isinstance(list_const, stix2.patterns.ListConstant)
+    assert all(
+        isinstance(elt, stix2.patterns.IntegerConstant)
+        for elt in list_const.value
+    )
+    assert all(
+        int_const.value == test_elt
+        for int_const, test_elt in zip(list_const.value, [1, 2, 3])
+    )
+
+
 def test_make_constant_already_a_constant():
     str_const = stix2.StringConstant('Foo')
     result = stix2.patterns.make_constant(str_const)