دوال التاريخ والوقت (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() = الوقت فقط
دالة 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;
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) وتطوير مهاراتك في قواعد البيانات.
الانتقال إلى الدرس التالي