> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> دليل لاستخدام قيم Date/Time في JDBC

# دليل قيم Date/Time

تتطلب قيم Date وTime وTimestamp عناية خاصة، لأن هناك عدة مشكلات شائعة مرتبطة بها.
وأكثر هذه المشكلات شيوعًا هو كيفية التعامل مع المناطق الزمنية. وهناك مشكلة أخرى تتعلق بالتمثيل النصي وكيفية استخدامه.
إضافة إلى ذلك، لكل قاعدة بيانات ولكل برنامج تشغيل خصائصه وقيوده الخاصة.

يهدف هذا المستند إلى أن يكون دليلًا يساعد على اتخاذ القرار، من خلال وصف المهام، وتقديم تفاصيل التنفيذ، وشرح المشكلات.

<div id="timezones">
  ## المناطق الزمنية
</div>

نعلم جميعًا أن التعامل مع المناطق الزمنية صعب (التوقيت الصيفي والتغييرات المستمرة في الإزاحة الزمنية). لكن هذا القسم يتناول مشكلة أخرى مرتبطة بالمناطق الزمنية: كيف ترتبط بالتمثيل النصي للطابع الزمني.

<div id="clickhouse-datetime-string-conversion">
  ### كيف يحوّل ClickHouse سلاسل `DateTime`
</div>

يستخدم ClickHouse القواعد التالية لتحويل قيم السلاسل النصية من `DateTime`:

* إذا كان العمود معرّفًا بمنطقة زمنية (`DateTime64(9, ‘Asia/Tokyo’)`)، فستُعامَل قيمة السلسلة على أنها طابع زمني في تلك المنطقة الزمنية. وستصبح `2026-01-01 13:00:00` هي `2026-01-01 04:00:00` بتوقيت `UTC`.
* إذا لم يكن للعمود تعريف لمنطقة زمنية، فستُستخدم فقط المنطقة الزمنية الخاصة بالخادم. مهم: لا يؤثر الإعداد `session_timezone` على ذلك. لذا، إذا كانت المنطقة الزمنية للخادم هي `UTC` وكانت المنطقة الزمنية للجلسة هي `America/Los_Angeles`، فستُكتب `2026-01-01 13:00:00` بتوقيت `UTC`.
* عند قراءة قيمة من عمود لا يحتوي على تعريف لمنطقة زمنية، تُستخدم `session_timezone`، أو المنطقة الزمنية الخاصة بالخادم إذا لم تكن مضبوطة. ولهذا السبب، قد تتأثر قراءة الطوابع الزمنية كسلاسل نصية بـ `session_timezone`. لا مشكلة في ذلك، ولكن ينبغي أخذه في الاعتبار.

<div id="writing-timestamps-across-timezones">
  ### كتابة الطوابع الزمنية عبر المناطق الزمنية
</div>

لنفترض الآن أن لدينا تطبيقًا يعمل في المنطقة `us-west` مع منطقة زمنية محلية هي `UTC-8`، ونحتاج إلى كتابة طابع زمني محلي `2026-01-01 02:00:00`، والذي يقابل في `UTC` القيمة `2026-01-01 10:00:00`:

* تتطلب كتابته كسلسلة نصية تحويله إلى المنطقة الزمنية للخادم أو المنطقة الزمنية للعمود.
* تتطلب كتابته باستخدام بنية زمنية أصلية في اللغة أن يكون برنامج التشغيل على علم بالمنطقة الزمنية المستهدفة، ولكن:
  * لا يكون ذلك ممكنًا دائمًا
  * واجهة برمجة تطبيقات برنامج التشغيل ليست مصممة جيدًا لهذا الغرض
  * والطريقة الوحيدة هي توضيح التحويلات التي ستُجرى حتى يتمكن التطبيق من التعويض (أو كتابة `طابع زمني Unix` كرقم)

<div id="java-and-jdbc-timestamp-apis">
  ### واجهات Java وJDBC للطابع الزمني
</div>

توفّر Java وJDBC طرقًا مختلفة لتعيين طابع زمني:

1. استخدم الفئة `Timestamp`، وهي في الواقع طابع زمني Unix.
   1. عند استخدامها مع الكائن `Calendar`، يتيح ذلك إعادة تفسير `Timestamp` وفقًا للمنطقة الزمنية الخاصة بالتقويم.
   2. تحتوي `Timestamp` على تقويم داخلي ليس من السهل ملاحظته.
2. استخدم الفئة `LocalDateTime`، إذ يسهل تحويلها إلى أي منطقة زمنية، ولكن لا توجد طريقة تتيح تمرير منطقة زمنية مستهدفة.
3. استخدم الفئة `ZonedDateTime`، التي تساعد في تحويل المنطقة الزمنية عند الكتابة إلى `DateTime` من دون منطقة زمنية (لأننا نعرف أنه يجب استخدام المنطقة الزمنية للخادم).
   1. لكن كتابة `ZonedDateTime` في عمود ذي منطقة زمنية محددة تتطلب من المستخدم تعويض التحويل الذي يجريه برنامج التشغيل.
4. استخدم `Long` لكتابة طابع زمني Unix بالميلي ثانية.
5. استخدم `String` لإجراء جميع التحويلات على جانب التطبيق (وهذا ليس عمليًا جدًا عبر البيئات المختلفة).

<Warning>
  يُفضَّل استخدام `java.time.ZoneId#of(java.lang.String)` عند البحث عن منطقة زمنية بحسب المعرّف.
  ستطرح هذه الطريقة استثناءً إذا لم يتم العثور على المنطقة الزمنية (بينما سيتراجع `java.util.TimeZone#getTimeZone(java.lang.String)` بصمت إلى `GMT`).

  الطريقة الصحيحة للحصول على المنطقة الزمنية `Tokyo` هي:

  `TimeZone.getTimeZone(ZoneId.of("Asia/Tokyo"))`
</Warning>

<div id="date">
  ## Date
</div>

التواريخ بطبيعتها غير مرتبطة بمنطقة زمنية. يوجد النوعان `Date` و`Date32` لتخزين التواريخ. ويستخدم كلا النوعين عدد الأيام منذ Epoch ‏(`1970-01-01`). يستخدم `Date` أعدادًا موجبة من الأيام فقط، لذا ينتهي نطاقه عند `2149-06-06`. أما `Date32` فيتعامل مع أعداد سالبة من الأيام لتغطية التواريخ السابقة لـ `1970-01-01`، لكن نطاقه أصغر (من `1900-01-01` إلى `2100-01-01`، حيث تمثل القيمة 0 التاريخ `1970-01-01`). ويتعامل ClickHouse مع `2026-01-01` على أنه `2026-01-01` في أي منطقة زمنية، ولا توجد معلمة منطقة زمنية في تعريفات الأعمدة.

<div id="using-localdate">
  ### استخدام `java.time.LocalDate`
</div>

في Java، تُعد الفئة `java.time.LocalDate` الأنسب لتمثيل قيم التاريخ. يستخدم العميل هذه الفئة لتخزين قيمة أعمدة `Date` و`Date32` (عند القراءة: `LocalDate.ofEpochDay((long)readUnsignedShortLE())`).

نوصي باستخدام `java.time.LocalDate` لأنها لا تتأثر بتحويلات المناطق الزمنية، وهي جزء من واجهة برمجة تطبيقات الوقت والتاريخ الحديثة.

<div id="using-java-sql-date">
  ### استخدام `java.sql.Date`
</div>

ظهر `LocalDate` في Java 8. وقبل ذلك، كان `java.sql.Date` يُستخدم لكتابة التواريخ وقراءتها. وداخليًا، تمثل هذه الفئة طبقة تغليف حول لحظة زمنية (أي قيمة وقت تمثل نقطة مطلقة في الزمن). لذلك، تُرجع `toString()` تاريخًا مختلفًا بحسب المنطقة الزمنية التي تعمل فيها JVM. وهذا يفرض على `برنامج تشغيل` إنشاء القيم بعناية، كما يتطلب من المستخدم أن يكون على دراية بذلك.

<div id="calendar-based-reinterpretation">
  ### إعادة التفسير المستندة إلى التقويم
</div>

تتضمن `java.sql.ResultSet` طريقة لجلب قيم التاريخ تقبل `Calendar`، وهناك طريقة مماثلة في `java.sql.PreparedStatement`. صُمِّم ذلك لإتاحة إعادة تفسير برنامج تشغيل JDBC لقيمة تاريخ في `منطقة زمنية` المحددة. على سبيل المثال، تحتوي قاعدة البيانات على القيمة `2026-01-01`، لكن التطبيق يريد عرض هذا التاريخ على أنه منتصف الليل في `Tokyo`. وهذا يعني أن الكائن `java.sql.Date` المُعاد سيُسنَد إليه توقيت زمني محدد، وعند تحويله إلى `منطقة زمنية` المحلية قد يصبح تاريخًا مختلفًا بسبب فرق التوقيت. ويمكن تحقيق الأمر نفسه باستخدام `LocalDate` عبر `java.time.LocalDate#atStartOfDay(java.time.ZoneId)`.

يعيد ClickHouse برنامج تشغيل JDBC دائمًا كائن `java.sql.Date` يشير إلى التاريخ **المحلي** عند منتصف الليل. وبعبارة أخرى، إذا كان التاريخ هو `2026-01-01`، فنحن نقصد `2026-01-01 12:00 AM` في `منطقة زمنية` الخاصة بـ JVM (وهو السلوك نفسه في PostgreSQL وMariaDB JDBC drivers).

<div id="time">
  ## Time
</div>

تكون قيم Time، مثل قيم Date، غير مرتبطة بمنطقة زمنية في معظم الحالات. ولا يُجري ClickHouse أي تحويلات على القيم الحرفية من نوع Time إلى أي منطقة زمنية — فالقيمة `’6:30’` تبقى نفسها أينما قُرئت.

<div id="clickhouse-time-types">
  ### أنواع Time في ClickHouse
</div>

تم تقديم `Time` و`Time64` في `25.6`. قبل ذلك، كان يُستخدم بدلًا منهما نوعا الطابع الزمني `DateTime` و`DateTime64` (وسنناقشهما لاحقًا في هذا الدليل). يُخزَّن `Time` كعدد صحيح من 32 بت يمثّل عدد الثواني، ويقع ضمن النطاق `[-999:59:59, 999:59:59]`. ويُرمَّز `Time64` على أنه `Decimal64` غير موقَّع، ويخزّن وحدات زمنية مختلفة حسب الدقة. والخيارات الشائعة هي 3 (مللي ثانية) و6 (ميكروثانية) و9 (نانوثانية). ويتراوح نطاق قيمة الدقة بين `[0, 9]`.

<div id="java-type-mapping">
  ### ربط الأنواع في Java
</div>

يقرأ العميل `Time` و`Time64` ويخزّنهما بصيغة `LocalDateTime`. ويتم ذلك لدعم النطاق الزمني السالب (إذ إن `LocalTime` لا يدعمه). في هذه الحالة، يكون جزء التاريخ هو تاريخ Epoch `1970-01-01`، لذا ستكون القيم السالبة سابقة لهذا التاريخ.

يُنفَّذ الدعم الأساسي لأنواع الوقت باستخدام `LocalTime` (عندما تكون القيمة ضمن يوم واحد) و`Duration` للاستفادة من النطاق الكامل للقيم. ويمكن استخدام `LocalDateTime` للقراءة فقط.

<div id="using-java-sql-time">
  ### استخدام `java.sql.Time`
</div>

يقتصر استخدام `java.sql.Time` على نطاق `LocalTime`. داخليًا، يُحوَّل `java.sql.Time` إلى قيمة نصية حرفية. ويمكن تغيير هذه القيمة باستخدام المعلمة Calendar مع `PreparedStatement#setTime()`.

<div id="totime-function">
  ### الدالة `toTime`
</div>

<Note>
  * تتطلب `toTime` دائمًا النوع `Date` أو `DateTime` أو نوعًا مشابهًا آخر. ولا تقبل السلاسل النصية. المشكلة ذات الصلة: [https://github.com/ClickHouse/ClickHouse/issues/89896](https://github.com/ClickHouse/ClickHouse/issues/89896)
  * لها اسم مستعار هو [`toTimeWithFixedDate`](/ar/reference/functions/regular-functions/date-time-functions#toTimeWithFixedDate).
  * توجد مشكلة متعلقة بالمنطقة الزمنية: [https://github.com/ClickHouse/ClickHouse/pull/90310](https://github.com/ClickHouse/ClickHouse/pull/90310)
</Note>

<div id="timestamp">
  ## الطابع الزمني
</div>

الطابع الزمني هو لحظة محددة في الزمن. على سبيل المثال، يمثّل طابع زمني Unix أي لحظة زمنية على هيئة عدد من الثواني بالنسبة إلى `1970-01-01 00:00:00` `UTC` (ويمثّل العدد السالب من الثواني طابعًا زمنيًا يسبق زمن يونكس، بينما يمثّل العدد الموجب طابعًا زمنيًا يقع بعده). يسهل حساب هذا التمثيل والتعامل معه إذا كان المستخدم ضمن المنطقة الزمنية `UTC` أو يستخدمها بدلًا من منطقته الزمنية المحلية.

<div id="clickhouse-timestamp-types">
  ### أنواع الطوابع الزمنية في ClickHouse
</div>

يوجد في ClickHouse نوعان من الطوابع الزمنية: `DateTime` (عدد صحيح 32-بت، وتكون الدقة دائمًا بالثواني) و`DateTime64` (عدد صحيح 64-بت، وتعتمد الدقة على التعريف). تُخزَّن القيم دائمًا كطوابع زمنية بتوقيت UTC. وهذا يعني أنه عند تمثيلها كأرقام، لا يُجرى أي تحويل للمنطقة الزمنية.

<div id="string-representation-and-timezone-behavior">
  ### التمثيل النصي وسلوك المنطقة الزمنية
</div>

ينطوي التمثيل النصي على بعض التعقيدات:

* إذا لم تُحدَّد منطقة زمنية في تعريف العمود، وتم تمرير سلسلة نصية عند الكتابة، فستُحوَّل من المنطقة الزمنية الخاصة بالخادم إلى رقم طابع زمني بتوقيت UTC. وعند قراءة قيمة من هذا العمود، ستُحوَّل من طابع زمني UTC إلى قيمة طابع زمني حرفية باستخدام المنطقة الزمنية الخاصة بالخادم أو الجلسة (ويُطبَّق نهج مماثل على قيم الطابع الزمني الحرفية في التعبيرات عندما لا تكون المنطقة الزمنية محددة صراحةً).
* إذا كانت المنطقة الزمنية محددة في تعريف العمود، فستُستخدم هذه المنطقة الزمنية وحدها في جميع التحويلات النصية. وهذا يتعارض مع المنطق المتبع عند عدم تحديد منطقة زمنية، لذا يتطلب الأمر فهمًا جيدًا لكيفية كتابة البيانات لكل عمود في الاستعلام.
* إذا تم تمرير تاريخ كسلسلة نصية بتنسيق يتضمن منطقة زمنية، فستكون هناك حاجة إلى دالة تحويل. وعادةً ما يُستخدم [`parseDateTimeBestEffort`](/ar/reference/functions/regular-functions/type-conversion-functions#parseDateTimeBestEffort).

<div id="how-jdbc-driver-handles-timestamps">
  ### كيفية تعامل برنامج تشغيل JDBC مع الطوابع الزمنية
</div>

في برنامج تشغيل JDBC، نحوّل الطوابع الزمنية إلى تمثيل رقمي:

```java theme={null}
"fromUnixTimestamp64Nano(" + epochSeconds * 1_000_000_000L + nanos + ")"
```

يحل هذا التمثيل معظم مشكلات التحويل المتعلقة بقيم الطابع الزمني لأنه يرسل البيانات إلى الخادم بتنسيق موحّد. ومع ذلك، يتطلب هذا النهج تعديلًا بسيطًا في عبارات SQL، لكنه يوفّر أبسط الطرق وأكثرها مباشرة لكتابة الطوابع الزمنية في أي عمود.

تُقرأ `DateTime` و`DateTime64` وتُخزَّنان لدى العميل بصيغة `java.time.ZonedDateTime`، مما يساعد على تحويل هذه القيم إلى أي منطقة زمنية أخرى (مع الاحتفاظ بمعلومات المنطقة الزمنية).

<div id="common-pitfall-todatetime64">
  ### من الأخطاء الشائعة عند استخدام `toDateTime64`
</div>

يبدو مثال الشيفرة التالي صحيحًا، لكنه يفشل عند التحقق:

```java theme={null}
String sql = "SELECT toDateTime64(?, 3)";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
    LocalDateTime localTs = LocalDateTime.parse("2021-01-01T01:34:56");
    stmt.setObject(1, localTs);
    try (ResultSet rs = stmt.executeQuery()) {
        rs.next();
        assertEquals(rs.getObject(1, LocalDateTime.class), localTs);
    }
}
```

يحدث هذا لأن `toDateTime64` يستخدم المنطقة الزمنية للخادم ولا يأخذ المنطقة الزمنية للمصدر في الحسبان.

<div id="conversion-tables">
  ## جداول التحويل
</div>

إذا لم يكن زوج التحويل مذكورًا في الجداول أدناه، فهذا يعني أن هذا التحويل غير مدعوم. على سبيل المثال، لا يمكن قراءة أعمدة `Date` على أنها `java.sql.Timestamp` لعدم وجود جزء زمني فيها.
لا يحوّل برنامج التشغيل القيم الصحيحة إلى أي قيمة من قيم التاريخ/الوقت. سيؤدي استدعاء `pstmt.setLong("timestamp", 1772132359L)` إلى كتابة `1772132359` كرقم في الخادم، وسيُعامَل عندها على أنه
طابع زمني Unix بتوقيت UTC وبوحدة الثواني.

<div id="writing-values-setobject">
  ### كتابة القيم باستخدام `PreparedStatement#setObject`
</div>

يوضح الجدول التالي كيفية تحويل القيم عند ضبطها باستخدام `PreparedStatement#setObject(column, value)`:

| فئة `value`               | التحويل                                                                              |
| ------------------------- | ------------------------------------------------------------------------------------ |
| `java.time.LocalDate`     | تُنسَّق على هيئة `YYYY-MM-DD`.                                                       |
| `java.sql.Date`           | تُحوَّل باستخدام التقويم الافتراضي وتُنسَّق كـ `LocalDate` (`YYYY-MM-DD`).           |
| `java.time.LocalTime`     | تُنسَّق على هيئة `HH:mm:ss`.                                                         |
| `java.time.Duration`      | تُنسَّق على هيئة `HHH:mm:ss`. يمكن أن تكون القيمة سالبة.                             |
| `java.sql.Time`           | تُحوَّل باستخدام التقويم الافتراضي وتُنسَّق كـ `LocalTime` (`HH:mm`).                |
| `java.time.LocalDateTime` | تُحوَّل إلى Unix timestamp بالنانوثانية وتُغلَّف باستخدام `fromUnixTimestamp64Nano`. |
| `java.time.ZonedDateTime` | تُحوَّل إلى Unix timestamp بالنانوثانية وتُغلَّف باستخدام `fromUnixTimestamp64Nano`. |
| `java.sql.Timestamp`      | تُحوَّل إلى Unix timestamp بالنانوثانية وتُغلَّف باستخدام `fromUnixTimestamp64Nano`. |

<Note>
  يجب اعتبار نوع العمود غير معروف. ويعود إلى التطبيق تحديد ما يجب تمريره إلى العبارة المُحضَّرة.
</Note>

<div id="reading-values-getobject">
  ### قراءة القيم باستخدام `ResultSet#getObject`
</div>

يوضح الجدول التالي كيفية تحويل القيم عند قراءتها باستخدام `ResultSet#getObject(column, class)`:

| نوع بيانات ClickHouse لـ `column` | قيمة `class`              | التحويل                                                                                                                                                                                                                                                                       |
| --------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Date` or `Date32`                | `java.time.LocalDate`     | تُحوَّل قيمة DB (عدد الأيام) إلى `LocalDate`.                                                                                                                                                                                                                                 |
| `Date` or `Date32`                | `java.sql.Date`           | تُحوَّل قيمة DB (عدد الأيام) إلى `LocalDate` ثم إلى `java.sql.Date` باستخدام منتصف الليل في المنطقة الزمنية المحلية كجزء الوقت. وإذا استُخدم تقويم، فستُستخدم منطقته الزمنية بدلًا من المنطقة الزمنية المحلية. مثال: قيمة DB `1970-01-10` → تكون `LocalDate` هي `1970-01-10`. |
| `Time` or `Time64`                | `java.time.LocalTime`     | تُحوَّل قيمة DB إلى `LocalDateTime` ثم إلى `LocalTime`. وينجح ذلك فقط مع الوقت الواقع ضمن يوم واحد.                                                                                                                                                                           |
| `Time` or `Time64`                | `java.time.LocalDateTime` | تُحوَّل قيمة DB إلى `LocalDateTime`.                                                                                                                                                                                                                                          |
| `Time` or `Time64`                | `java.sql.Time`           | تُحوَّل قيمة DB إلى `LocalDateTime` ثم إلى `java.sql.Time` باستخدام التقويم الافتراضي. وينجح ذلك فقط مع الوقت الواقع ضمن يوم واحد.                                                                                                                                            |
| `Time` or `Time64`                | `java.time.Duration`      | تُحوَّل قيمة DB إلى `LocalDateTime` ثم إلى `Duration`.                                                                                                                                                                                                                        |
| `DateTime` or `DateTime64`        | `java.time.LocalDateTime` | تُحوَّل قيمة DB إلى `ZonedDateTime`، ثم إلى `LocalDateTime`.                                                                                                                                                                                                                  |
| `DateTime` or `DateTime64`        | `java.time.ZonedDateTime` | تُحوَّل قيمة DB إلى `ZonedDateTime`.                                                                                                                                                                                                                                          |
| `DateTime` or `DateTime64`        | `java.sql.Timestamp`      | تُحوَّل قيمة DB إلى `ZonedDateTime`، ثم إلى `java.sql.Timestamp` باستخدام المنطقة الزمنية الافتراضية.                                                                                                                                                                         |

<div id="using-calendar-based-methods">
  ### استخدام الطرائق المعتمدة على التقويم
</div>

استخدم `ResultSet#getTime(column, calendar)` و`ResultSet#getDate(column, calendar)` إذا كانت القيم قد خُزّنت باستخدام `PreparedStatement#setTime(param, value, calendar)` و`PreparedStatement#setDate(param, value, calendar)`، على التوالي.
