دوال التاريخ والوقت (Date Functions)

مقدمة حول دوال التاريخ والوقت في SQL

دوال التاريخ والوقت في SQL هي أدوات قوية تسمح لك بالتعامل مع البيانات الزمنية بطريقة احترافية. سواء كنت تريد معرفة التاريخ الحالي، حساب الفرق بين تاريخين، أو تنسيق التواريخ بطريقة معينة، فإن SQL توفر لك مجموعة واسعة من الدوال المتخصصة.

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

لماذا دوال التاريخ مهمة؟

  • تسجيل وقت إنشاء السجلات تلقائياً
  • حساب الفترات الزمنية (مثل عمر المستخدم، مدة الاشتراك)
  • تصفية البيانات حسب نطاقات زمنية محددة
  • إنشاء تقارير زمنية دقيقة
  • جدولة المهام والتذكيرات

دالة NOW() - الحصول على التاريخ والوقت الحالي

دالة NOW() في SQL تُرجع التاريخ والوقت الحالي بصيغة 'YYYY-MM-DD HH:MM:SS'. هذه الدالة مفيدة جداً لتسجيل وقت إنشاء أو تحديث السجلات.

الصيغة الأساسية:

SELECT NOW();

مثال عملي - تسجيل وقت إنشاء طلب:

-- إنشاء جدول الطلبات
CREATE TABLE orders (
    order_id INT PRIMARY KEY AUTO_INCREMENT,
    customer_name VARCHAR(100),
    total_amount DECIMAL(10, 2),
    created_at DATETIME DEFAULT NOW()
);

-- إدراج طلب جديد (سيتم تسجيل الوقت تلقائياً)
INSERT INTO orders (customer_name, total_amount)
VALUES ('أحمد محمد', 250.50);

-- عرض الطلبات مع وقت الإنشاء
SELECT order_id, customer_name, total_amount, created_at
FROM orders;
النتيجة:
order_id customer_name total_amount created_at
1 أحمد محمد 250.50 2026-02-02 09:30:45

دالتا CURDATE() و CURTIME()

دالة CURDATE() - التاريخ الحالي فقط

تُرجع دالة CURDATE() التاريخ الحالي فقط بدون الوقت، بصيغة 'YYYY-MM-DD'.

-- الحصول على التاريخ الحالي
SELECT CURDATE() AS current_date;

-- مثال: البحث عن الطلبات التي تمت اليوم
SELECT * FROM orders
WHERE DATE(created_at) = CURDATE();

دالة CURTIME() - الوقت الحالي فقط

تُرجع دالة CURTIME() الوقت الحالي فقط بدون التاريخ، بصيغة 'HH:MM:SS'.

-- الحصول على الوقت الحالي
SELECT CURTIME() AS current_time;

-- مثال: معرفة الساعة الحالية
SELECT 
    CURTIME() AS الوقت_الحالي,
    HOUR(CURTIME()) AS الساعة,
    MINUTE(CURTIME()) AS الدقيقة,
    SECOND(CURTIME()) AS الثانية;
ملاحظة: الفرق بين NOW() و CURDATE() و CURTIME():
  • NOW() = التاريخ + الوقت
  • CURDATE() = التاريخ فقط
  • CURTIME() = الوقت فقط

دالة DATE_FORMAT() - تنسيق التواريخ

دالة DATE_FORMAT() في SQL تسمح لك بتنسيق التواريخ بأي شكل تريده. هذه الدالة مفيدة جداً لعرض التواريخ بطريقة مقروءة للمستخدمين.

الصيغة الأساسية:

DATE_FORMAT(date, format)

رموز التنسيق الشائعة:

الرمز الوصف مثال
%Y السنة (4 أرقام) 2026
%y السنة (رقمين) 26
%M اسم الشهر February
%m رقم الشهر (01-12) 02
%d اليوم (01-31) 02
%W اسم اليوم Sunday
%H الساعة (00-23) 14
%i الدقائق (00-59) 30
%s الثواني (00-59) 45

أمثلة عملية:

-- تنسيقات مختلفة للتاريخ
SELECT 
    NOW() AS التاريخ_الأصلي,
    DATE_FORMAT(NOW(), '%d/%m/%Y') AS تنسيق_1,
    DATE_FORMAT(NOW(), '%Y-%m-%d') AS تنسيق_2,
    DATE_FORMAT(NOW(), '%W, %M %d, %Y') AS تنسيق_3,
    DATE_FORMAT(NOW(), '%d-%m-%Y %H:%i:%s') AS تنسيق_4;

-- مثال عملي: عرض الطلبات بتنسيق مقروء
SELECT 
    order_id,
    customer_name,
    DATE_FORMAT(created_at, '%d/%m/%Y الساعة %H:%i') AS تاريخ_الطلب
FROM orders;
النتيجة:
order_id customer_name تاريخ_الطلب
1 أحمد محمد 02/02/2026 الساعة 09:30

دالة DATEDIFF() - حساب الفرق بين تاريخين

دالة DATEDIFF() في SQL تحسب الفرق بالأيام بين تاريخين. هذه الدالة مفيدة جداً لحساب المدد الزمنية.

الصيغة الأساسية:

DATEDIFF(date1, date2)

النتيجة = date1 - date2 بالأيام

أمثلة عملية:

-- حساب عدد الأيام بين تاريخين
SELECT DATEDIFF('2026-02-10', '2026-02-02') AS عدد_الأيام;
-- النتيجة: 8

-- حساب عدد الأيام منذ تاريخ معين
SELECT DATEDIFF(CURDATE(), '2026-01-01') AS أيام_منذ_بداية_السنة;

-- مثال عملي: حساب عمر الطلب بالأيام
SELECT 
    order_id,
    customer_name,
    created_at,
    DATEDIFF(NOW(), created_at) AS عمر_الطلب_بالأيام
FROM orders;

-- مثال: البحث عن الطلبات القديمة (أكثر من 30 يوم)
SELECT * FROM orders
WHERE DATEDIFF(NOW(), created_at) > 30;

استخدامات عملية لـ DATEDIFF:

  • حساب عمر المستخدم من تاريخ الميلاد
  • معرفة عدد الأيام المتبقية لانتهاء الاشتراك
  • حساب مدة تأخير التسليم
  • إنشاء تقارير عن الفترات الزمنية

دالتا DATE_ADD() و DATE_SUB() - إضافة وطرح الفترات الزمنية

دالة DATE_ADD() - إضافة فترة زمنية

دالة DATE_ADD() في SQL تضيف فترة زمنية محددة إلى تاريخ معين.

DATE_ADD(date, INTERVAL value unit)

الوحدات الزمنية المتاحة:

الوحدة الوصف مثال
DAY أيام INTERVAL 7 DAY
WEEK أسابيع INTERVAL 2 WEEK
MONTH أشهر INTERVAL 3 MONTH
YEAR سنوات INTERVAL 1 YEAR
HOUR ساعات INTERVAL 5 HOUR
MINUTE دقائق INTERVAL 30 MINUTE
SECOND ثواني INTERVAL 45 SECOND

أمثلة على DATE_ADD:

-- إضافة 7 أيام للتاريخ الحالي
SELECT DATE_ADD(CURDATE(), INTERVAL 7 DAY) AS بعد_أسبوع;

-- إضافة 3 أشهر
SELECT DATE_ADD(CURDATE(), INTERVAL 3 MONTH) AS بعد_3_أشهر;

-- إضافة سنة واحدة
SELECT DATE_ADD(CURDATE(), INTERVAL 1 YEAR) AS بعد_سنة;

-- مثال عملي: حساب تاريخ انتهاء الاشتراك
CREATE TABLE subscriptions (
    subscription_id INT PRIMARY KEY AUTO_INCREMENT,
    user_name VARCHAR(100),
    start_date DATE,
    duration_months INT,
    end_date DATE
);

-- إدراج اشتراك جديد مع حساب تاريخ الانتهاء تلقائياً
INSERT INTO subscriptions (user_name, start_date, duration_months, end_date)
VALUES (
    'فاطمة أحمد',
    CURDATE(),
    6,
    DATE_ADD(CURDATE(), INTERVAL 6 MONTH)
);

-- عرض الاشتراكات
SELECT 
    user_name,
    start_date AS تاريخ_البداية,
    end_date AS تاريخ_الانتهاء,
    DATEDIFF(end_date, CURDATE()) AS أيام_متبقية
FROM subscriptions;

دالة DATE_SUB() - طرح فترة زمنية

دالة DATE_SUB() تعمل بنفس طريقة DATE_ADD() لكنها تطرح الفترة الزمنية بدلاً من إضافتها.

-- طرح 30 يوم من التاريخ الحالي
SELECT DATE_SUB(CURDATE(), INTERVAL 30 DAY) AS قبل_شهر;

-- البحث عن الطلبات في آخر 7 أيام
SELECT * FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY);

-- البحث عن الطلبات في آخر 3 أشهر
SELECT * FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 3 MONTH);

دالة EXTRACT() - استخراج أجزاء من التاريخ

دالة EXTRACT() في SQL تسمح لك باستخراج جزء معين من التاريخ (مثل السنة، الشهر، اليوم، إلخ).

الصيغة الأساسية:

EXTRACT(unit FROM date)

أمثلة عملية:

-- استخراج أجزاء مختلفة من التاريخ
SELECT 
    NOW() AS التاريخ_الكامل,
    EXTRACT(YEAR FROM NOW()) AS السنة,
    EXTRACT(MONTH FROM NOW()) AS الشهر,
    EXTRACT(DAY FROM NOW()) AS اليوم,
    EXTRACT(HOUR FROM NOW()) AS الساعة,
    EXTRACT(MINUTE FROM NOW()) AS الدقيقة;

-- مثال: تجميع الطلبات حسب الشهر
SELECT 
    EXTRACT(YEAR FROM created_at) AS السنة,
    EXTRACT(MONTH FROM created_at) AS الشهر,
    COUNT(*) AS عدد_الطلبات,
    SUM(total_amount) AS إجمالي_المبيعات
FROM orders
GROUP BY EXTRACT(YEAR FROM created_at), EXTRACT(MONTH FROM created_at)
ORDER BY السنة DESC, الشهر DESC;
بديل لـ EXTRACT: يمكنك أيضاً استخدام دوال مثل YEAR(), MONTH(), DAY(), HOUR() للحصول على نفس النتيجة:
SELECT 
    YEAR(NOW()) AS السنة,
    MONTH(NOW()) AS الشهر,
    DAY(NOW()) AS اليوم;

دوال DAYNAME() و MONTHNAME()

دالة DAYNAME() - اسم اليوم

تُرجع دالة DAYNAME() اسم اليوم من الأسبوع (مثل Monday, Tuesday, إلخ).

-- الحصول على اسم اليوم الحالي
SELECT DAYNAME(CURDATE()) AS اسم_اليوم;

-- مثال: عرض الطلبات مع اسم اليوم
SELECT 
    order_id,
    customer_name,
    DAYNAME(created_at) AS يوم_الطلب,
    created_at
FROM orders;

دالة MONTHNAME() - اسم الشهر

تُرجع دالة MONTHNAME() اسم الشهر (مثل January, February, إلخ).

-- الحصول على اسم الشهر الحالي
SELECT MONTHNAME(CURDATE()) AS اسم_الشهر;

-- مثال: تجميع الطلبات حسب اسم الشهر
SELECT 
    MONTHNAME(created_at) AS الشهر,
    COUNT(*) AS عدد_الطلبات
FROM orders
GROUP BY MONTHNAME(created_at), MONTH(created_at)
ORDER BY MONTH(created_at);

دالة TIMESTAMPDIFF() - حساب الفرق بوحدات مختلفة

بينما DATEDIFF() تحسب الفرق بالأيام فقط، فإن TIMESTAMPDIFF() تسمح لك بحساب الفرق بوحدات مختلفة (سنوات، أشهر، أيام، ساعات، إلخ).

الصيغة الأساسية:

TIMESTAMPDIFF(unit, datetime1, datetime2)

أمثلة عملية:

-- حساب الفرق بالسنوات
SELECT TIMESTAMPDIFF(YEAR, '2000-01-01', CURDATE()) AS العمر_بالسنوات;

-- حساب الفرق بالأشهر
SELECT TIMESTAMPDIFF(MONTH, '2025-01-01', CURDATE()) AS عدد_الأشهر;

-- حساب الفرق بالساعات
SELECT TIMESTAMPDIFF(HOUR, '2026-02-01 10:00:00', NOW()) AS عدد_الساعات;

-- مثال عملي: حساب عمر المستخدمين
CREATE TABLE users (
    user_id INT PRIMARY KEY AUTO_INCREMENT,
    user_name VARCHAR(100),
    birth_date DATE,
    registration_date DATETIME
);

INSERT INTO users (user_name, birth_date, registration_date) VALUES
('محمد علي', '1995-05-15', '2024-01-10 14:30:00'),
('سارة أحمد', '2000-08-22', '2024-06-20 09:15:00'),
('خالد محمود', '1988-12-03', '2023-11-05 16:45:00');

-- عرض المستخدمين مع أعمارهم ومدة تسجيلهم
SELECT 
    user_name,
    birth_date,
    TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS العمر_بالسنوات,
    registration_date,
    TIMESTAMPDIFF(MONTH, registration_date, NOW()) AS أشهر_منذ_التسجيل,
    TIMESTAMPDIFF(DAY, registration_date, NOW()) AS أيام_منذ_التسجيل
FROM users;

دالة LAST_DAY() - آخر يوم في الشهر

دالة LAST_DAY() تُرجع آخر يوم في الشهر لتاريخ معين.

-- الحصول على آخر يوم في الشهر الحالي
SELECT LAST_DAY(CURDATE()) AS آخر_يوم_في_الشهر;

-- الحصول على آخر يوم في شهر فبراير 2026
SELECT LAST_DAY('2026-02-01') AS آخر_يوم_في_فبراير;

-- مثال: البحث عن الطلبات في آخر يوم من كل شهر
SELECT * FROM orders
WHERE DAY(created_at) = DAY(LAST_DAY(created_at));

-- حساب عدد الأيام المتبقية في الشهر الحالي
SELECT DATEDIFF(LAST_DAY(CURDATE()), CURDATE()) AS أيام_متبقية_في_الشهر;

أمثلة عملية متقدمة

مثال 1: نظام تتبع المهام مع المواعيد النهائية

CREATE TABLE tasks (
    task_id INT PRIMARY KEY AUTO_INCREMENT,
    task_name VARCHAR(200),
    created_at DATETIME DEFAULT NOW(),
    deadline DATE,
    status ENUM('pending', 'in_progress', 'completed') DEFAULT 'pending'
);

INSERT INTO tasks (task_name, deadline, status) VALUES
('تصميم واجهة المستخدم', '2026-02-10', 'in_progress'),
('كتابة الوثائق', '2026-02-05', 'pending'),
('اختبار التطبيق', '2026-02-15', 'pending'),
('نشر الإصدار', '2026-01-30', 'completed');

-- عرض المهام مع حالة الموعد النهائي
SELECT 
    task_name,
    deadline,
    status,
    DATEDIFF(deadline, CURDATE()) AS أيام_متبقية,
    CASE 
        WHEN deadline < CURDATE() AND status != 'completed' THEN 'متأخر'
        WHEN DATEDIFF(deadline, CURDATE()) <= 3 AND status != 'completed' THEN 'عاجل'
        WHEN status = 'completed' THEN 'مكتمل'
        ELSE 'عادي'
    END AS الأولوية
FROM tasks
ORDER BY deadline;

مثال 2: تقرير المبيعات الشهري

-- تقرير مبيعات شامل
SELECT 
    DATE_FORMAT(created_at, '%Y-%m') AS الشهر,
    COUNT(*) AS عدد_الطلبات,
    SUM(total_amount) AS إجمالي_المبيعات,
    AVG(total_amount) AS متوسط_قيمة_الطلب,
    MIN(total_amount) AS أقل_طلب,
    MAX(total_amount) AS أعلى_طلب
FROM orders
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY الشهر DESC;

مثال 3: نظام الحضور والانصراف

CREATE TABLE attendance (
    attendance_id INT PRIMARY KEY AUTO_INCREMENT,
    employee_name VARCHAR(100),
    check_in DATETIME,
    check_out DATETIME
);

INSERT INTO attendance (employee_name, check_in, check_out) VALUES
('أحمد محمد', '2026-02-02 08:30:00', '2026-02-02 17:15:00'),
('فاطمة علي', '2026-02-02 08:45:00', '2026-02-02 17:30:00'),
('خالد حسن', '2026-02-02 09:00:00', '2026-02-02 18:00:00');

-- حساب ساعات العمل
SELECT 
    employee_name,
    DATE_FORMAT(check_in, '%H:%i') AS وقت_الدخول,
    DATE_FORMAT(check_out, '%H:%i') AS وقت_الخروج,
    TIMESTAMPDIFF(HOUR, check_in, check_out) AS ساعات_العمل,
    TIMESTAMPDIFF(MINUTE, check_in, check_out) % 60 AS دقائق_إضافية,
    CASE 
        WHEN TIME(check_in) > '09:00:00' THEN 'متأخر'
        ELSE 'في الوقت'
    END AS حالة_الحضور
FROM attendance
WHERE DATE(check_in) = CURDATE();

مثال 4: حساب الأعمار والفئات العمرية

-- تحليل الفئات العمرية للمستخدمين
SELECT 
    user_name,
    birth_date,
    TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS العمر,
    CASE 
        WHEN TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) < 18 THEN 'قاصر'
        WHEN TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) BETWEEN 18 AND 30 THEN 'شباب'
        WHEN TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) BETWEEN 31 AND 50 THEN 'متوسط العمر'
        ELSE 'كبار السن'
    END AS الفئة_العمرية
FROM users
ORDER BY العمر;

-- إحصائيات الفئات العمرية
SELECT 
    CASE 
        WHEN TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) < 18 THEN 'قاصر'
        WHEN TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) BETWEEN 18 AND 30 THEN 'شباب'
        WHEN TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) BETWEEN 31 AND 50 THEN 'متوسط العمر'
        ELSE 'كبار السن'
    END AS الفئة_العمرية,
    COUNT(*) AS العدد
FROM users
GROUP BY الفئة_العمرية;

أخطاء شائعة وكيفية تجنبها

1. الخلط بين DATEDIFF و TIMESTAMPDIFF

خطأ شائع:

-- هذا سيعطي نتيجة بالأيام فقط
SELECT DATEDIFF('2026-02-02', '2025-01-01') AS الفرق;

الحل الصحيح:

-- للحصول على الفرق بالأشهر أو السنوات
SELECT TIMESTAMPDIFF(MONTH, '2025-01-01', '2026-02-02') AS الفرق_بالأشهر;

2. نسيان تحويل النص إلى تاريخ

خطأ شائع:

-- قد لا يعمل بشكل صحيح
SELECT * FROM orders WHERE created_at = '2026-02-02';

الحل الصحيح:

-- استخدم DATE() لمقارنة التواريخ فقط
SELECT * FROM orders WHERE DATE(created_at) = '2026-02-02';

3. عدم مراعاة المنطقة الزمنية

دوال مثل NOW() تستخدم المنطقة الزمنية للخادم. تأكد من ضبط المنطقة الزمنية بشكل صحيح:

-- ضبط المنطقة الزمنية
SET time_zone = '+03:00';  -- توقيت السعودية مثلاً

أفضل الممارسات

1. استخدم القيم الافتراضية للتواريخ

CREATE TABLE records (
    id INT PRIMARY KEY AUTO_INCREMENT,
    data VARCHAR(100),
    created_at DATETIME DEFAULT NOW(),
    updated_at DATETIME DEFAULT NOW() ON UPDATE NOW()
);

2. استخدم الفهارس على أعمدة التاريخ

-- إنشاء فهرس لتحسين الأداء
CREATE INDEX idx_created_at ON orders(created_at);

3. استخدم أنواع البيانات المناسبة

  • DATE: للتواريخ فقط (YYYY-MM-DD)
  • TIME: للأوقات فقط (HH:MM:SS)
  • DATETIME: للتاريخ والوقت معاً
  • TIMESTAMP: للتاريخ والوقت مع تحديث تلقائي

4. تحقق من صحة التواريخ

-- التحقق من أن التاريخ صحيح
SELECT 
    CASE 
        WHEN STR_TO_DATE('2026-02-30', '%Y-%m-%d') IS NULL 
        THEN 'تاريخ غير صحيح'
        ELSE 'تاريخ صحيح'
    END AS النتيجة;

ملخص الدرس

في هذا الدرس، تعلمنا كيفية استخدام دوال التاريخ والوقت في SQL:

  • NOW(), CURDATE(), CURTIME() - للحصول على التاريخ والوقت الحالي
  • DATE_FORMAT() - لتنسيق التواريخ بأشكال مختلفة
  • DATEDIFF() - لحساب الفرق بين تاريخين بالأيام
  • DATE_ADD() و DATE_SUB() - لإضافة أو طرح فترات زمنية
  • EXTRACT() - لاستخراج أجزاء من التاريخ
  • TIMESTAMPDIFF() - لحساب الفرق بوحدات مختلفة
  • DAYNAME(), MONTHNAME() - للحصول على أسماء الأيام والأشهر
  • LAST_DAY() - للحصول على آخر يوم في الشهر

هذه الدوال ضرورية لأي تطبيق يتعامل مع البيانات الزمنية، وستساعدك في إنشاء تقارير دقيقة وتحليلات زمنية متقدمة.

في الدرس القادم: سنتعلم عن الدوال الرقمية (Numeric Functions) في SQL، وكيفية إجراء العمليات الحسابية المتقدمة على البيانات الرقمية.

الخطوة التالية: الدوال الشرطية (Conditional Functions)

أكمل رحلتك التعليمية وانتقل إلى الدرس التالي لتعلم الدوال الشرطية (Conditional Functions) وتطوير مهاراتك في قواعد البيانات.

الانتقال إلى الدرس التالي
المحرر الذكي

اكتب الكود وشاهد النتيجة فوراً

جرب الآن مجاناً
قناة ديف عربي

تابع أحدث الدروس والتحديثات مباشرة على واتساب

انضم الآن