From 440dc7313379bc291a63ee0ef7fd9ca243a30091 Mon Sep 17 00:00:00 2001
From: i-just <iwona@pixelandtonic.com>
Date: Thu, 16 Jan 2025 14:53:42 +0000
Subject: [PATCH 1/3] move getting datetime attributes to a helper

---
 src/base/Model.php        | 18 ++----------------
 src/helpers/Component.php | 28 ++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/src/base/Model.php b/src/base/Model.php
index ca8ae365f0c..98f0d16e75a 100644
--- a/src/base/Model.php
+++ b/src/base/Model.php
@@ -12,13 +12,10 @@
 use craft\events\DefineFieldsEvent;
 use craft\events\DefineRulesEvent;
 use craft\helpers\App;
+use craft\helpers\Component;
 use craft\helpers\DateTimeHelper;
 use craft\helpers\StringHelper;
 use craft\helpers\Typecast;
-use DateTime;
-use ReflectionClass;
-use ReflectionNamedType;
-use ReflectionProperty;
 use yii\validators\Validator;
 
 /**
@@ -242,18 +239,7 @@ public function fields(): array
     {
         $fields = parent::fields();
 
-        $datetimeAttributes = [];
-        foreach ((new ReflectionClass($this))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
-            if (!$property->isStatic()) {
-                $type = $property->getType();
-                if ($type instanceof ReflectionNamedType && $type->getName() === DateTime::class) {
-                    $datetimeAttributes[] = $property->getName();
-                }
-            }
-        }
-
-        // Include datetimeAttributes() for now
-        $datetimeAttributes = array_unique(array_merge($datetimeAttributes, $this->datetimeAttributes()));
+        $datetimeAttributes = Component::datetimeAttributes($this);
 
         // Have all DateTime attributes converted to ISO-8601 strings
         foreach ($datetimeAttributes as $attribute) {
diff --git a/src/helpers/Component.php b/src/helpers/Component.php
index 05e4c1ef122..b909fdcb3a7 100644
--- a/src/helpers/Component.php
+++ b/src/helpers/Component.php
@@ -9,7 +9,13 @@
 
 use Craft;
 use craft\base\ComponentInterface;
+use craft\base\ElementInterface;
+use craft\base\Model;
 use craft\errors\MissingComponentException;
+use DateTime;
+use ReflectionClass;
+use ReflectionNamedType;
+use ReflectionProperty;
 use yii\base\InvalidConfigException;
 
 /**
@@ -176,4 +182,26 @@ public static function iconSvg(?string $icon, string $label): string
 
         return Cp::iconSvg($icon, $label);
     }
+
+    /**
+     * Return all DateTime attributes for given model.
+     *
+     * @param Model|ElementInterface $model
+     * @return array
+     */
+    public static function datetimeAttributes(Model|ElementInterface $model): array
+    {
+        $datetimeAttributes = [];
+        foreach ((new ReflectionClass($model))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
+            if (!$property->isStatic()) {
+                $type = $property->getType();
+                if ($type instanceof ReflectionNamedType && $type->getName() === DateTime::class) {
+                    $datetimeAttributes[] = $property->getName();
+                }
+            }
+        }
+
+        // Include datetimeAttributes() for now
+        return array_unique(array_merge($datetimeAttributes, $model->datetimeAttributes()));
+    }
 }

From 17f9d440a5f5feaa9880000b44514fedd500a85f Mon Sep 17 00:00:00 2001
From: i-just <iwona@pixelandtonic.com>
Date: Thu, 16 Jan 2025 14:54:02 +0000
Subject: [PATCH 2/3] expanded export should return localised datetime
 attributes

---
 src/elements/exporters/Expanded.php | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/elements/exporters/Expanded.php b/src/elements/exporters/Expanded.php
index 3b759f65416..82ddc6966f8 100644
--- a/src/elements/exporters/Expanded.php
+++ b/src/elements/exporters/Expanded.php
@@ -13,6 +13,8 @@
 use craft\base\ElementInterface;
 use craft\elements\db\ElementQuery;
 use craft\elements\db\ElementQueryInterface;
+use craft\helpers\Component;
+use craft\helpers\DateTimeHelper;
 use craft\helpers\Db;
 
 /**
@@ -63,7 +65,29 @@ public function export(ElementQueryInterface $query): mixed
                     unset($attributes[$field->handle]);
                 }
             }
-            $elementArr = $element->toArray(array_keys($attributes));
+            // because of changes in https://github.com/craftcms/cms/commit/e662ee32d7a5c15dfaa911ae462155615ce7a320
+            // we need to split attributes to the date ones and all other;
+            // pass all other to toArray()
+            // and then return DateTimeHelper::toIso8601($date, false); for all the date ones
+            $datetimeAttributes = Component::datetimeAttributes($element);
+            $otherAttributes = array_diff(array_keys($attributes), $datetimeAttributes);
+
+            $elementArr = $element->toArray($otherAttributes);
+
+            foreach ($datetimeAttributes as $attribute) {
+                $date = $element->$attribute;
+                if ($date) {
+                    $elementArr[$attribute] = DateTimeHelper::toIso8601($date);
+                } else {
+                    $elementArr[$attribute] = $element->$attribute;
+                }
+            }
+
+            // sort the $elementArr so the keys are in the same order as the values in the $attributes table
+            uksort($elementArr, function($a, $b) use ($attributes) {
+                return $attributes[$a] <=> $attributes[$b];
+            });
+
             if ($fieldLayout !== null) {
                 foreach ($fieldLayout->getCustomFields() as $field) {
                     $value = $element->getFieldValue($field->handle);

From 67e1a207805a2ce2bb6e06be9ca3827827477844 Mon Sep 17 00:00:00 2001
From: brandonkelly <brandon@pixelandtonic.com>
Date: Mon, 20 Jan 2025 08:11:37 -0800
Subject: [PATCH 3/3] Release note

[ci skip]
---
 CHANGELOG-WIP.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md
index 549df2f53eb..24173b36ba2 100644
--- a/CHANGELOG-WIP.md
+++ b/CHANGELOG-WIP.md
@@ -16,6 +16,7 @@
 - Structure views are now available to element indexes on mobile browsers. ([#16190](https://github.com/craftcms/cms/discussions/16190))
 - Datepickers now include a dropdown menu for selecting the year. ([#16376](https://github.com/craftcms/cms/pull/16376))
 - Custom fields within element edit pages can now have action menus with “Copy value from site…”, “Edit field” and “Copy field handle” items. ([#16415](https://github.com/craftcms/cms/pull/16415), [#14056](https://github.com/craftcms/cms/pull/14056))
+- Element exports now include date attribute values set to the system time zone. ([#16447](https://github.com/craftcms/cms/pull/16447))
 - Heads-up displays now reposition themselves on window scroll.
 
 ### Accessibility