الأساسيات في برنامج واحد
تعيد time.Now() الوقت المحلي الحالي، وتوقف time.Sleep الـ goroutine الحالية، وتقيس time.Since الوقت المنقضي، وتبني time.Date لحظة محدّدة. تتناول بقية هذه الصفحة كل قطعة بدورها. تستخدم الأمثلة تواريخ ثابتة وتوقيت UTC لتكون مخرجاتها واحدة أينما شغّلتها.
Sleep وDuration
الـ time.Duration عدد من النوع int64 للنانوثواني. تعرّف الحزمة ثوابت لبناء قيم مقروءة:
| الثابت | القيمة |
|---|---|
time.Nanosecond | 1 |
time.Microsecond | 1000 ns |
time.Millisecond | 1000 µs |
time.Second | 1000 ms |
time.Minute | 60 s |
time.Hour | 60 min |
لا يوجد time.Day، لأن اليوم ليس دائمًا 24 ساعة عندما تتغيّر المنطقة الزمنية للتوقيت الصيفي. استخدم AddDate للأيام التقويمية (انظر أدناه).
خطآن شائعان يأتيان من كون Duration عددًا صحيحًا:
time.Sleep(5)تنام 5 نانوثوانٍ. اضرب دائمًا بوحدة:time.Sleep(5 * time.Second).time.Sleep(n * time.Second)لا تُترجم عندما يكونnمتغيّرًا من النوعint. حوّله:time.Duration(n) * time.Second. والثابت مثل5يعمل دون تحويل لأن الثوابت عديمة النوع تتكيّف مع النوع Duration.
تقبل ParseDuration الوحدات ns وus (أو µs) وms وs وm وh، مدمجة مثل "2h45m" أو "1.5s". ولا وحدة فيها للأيام.
التنسيق: تخطيط المرجع 2006-01-02
لا تستخدم Go الصيغة %Y-%m-%d ولا yyyy-MM-dd. التخطيط هو الوقت المرجعي
Mon Jan 2 15:04:05 MST 2006
مكتوبًا بالشكل الذي تريد أن تبدو به مخرجاتك. اختيرت القيم بحيث تكون كل منها فريدة: الشهر 1، واليوم 2، والساعة 3 (أو 15)، والدقيقة 4، والثانية 5، والسنة 6 (2006)، وإزاحة المنطقة -7 (-0700). اقرأها هكذا: 01/02 03:04:05PM '06 -0700.
رموز التخطيط التي ستحتاجها أكثر:
| الرمز | المعنى | مثال |
|---|---|---|
2006 / 06 | السنة، 4 أو 2 أرقام | 2026 / 26 |
01 / 1 / Jan / January | الشهر | 03 / 3 / Mar / March |
02 / 2 / _2 | يوم الشهر (بصفر في البداية، عادي، بمسافة في البداية) | 05 / 5 / " 5" |
Mon / Monday | يوم الأسبوع | Thu / Thursday |
15 | الساعة، بنظام 24 ساعة | 09 |
03 / 3 | الساعة، بنظام 12 ساعة | 09 / 9 |
04 / 4 | الدقيقة | 07 / 7 |
05 / 5 | الثانية | 03 / 3 |
PM / pm | علامة AM أو PM | AM |
.000 / .999 | كسور الثانية (ثابتة / مع حذف الأصفار الزائدة) | .250 / .25 |
MST | اختصار المنطقة | UTC |
-0700 / -07:00 | إزاحة المنطقة الرقمية | +0000 / +00:00 |
Z07:00 | مثل -07:00، لكنه يطبع Z لتوقيت UTC | Z |
التخطيطات المعرّفة مسبقًا:
| الثابت | التخطيط |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00 (استخدمه للواجهات البرمجية وJSON) |
time.RFC3339Nano | 2006-01-02T15:04:05.999999999Z07:00 |
time.DateTime (Go 1.20) | 2006-01-02 15:04:05 |
time.DateOnly (Go 1.20) | 2006-01-02 |
time.TimeOnly (Go 1.20) | 15:04:05 |
time.Kitchen | 3:04PM |
time.RFC1123 | Mon, 02 Jan 2006 15:04:05 MST (تواريخ HTTP تستخدم http.TimeFormat بدلًا منه) |
الخطأ الكلاسيكي كتابة تخطيط بأرقام خاطئة، مثل "2023-01-01". لا ترفضه Go. بل تنسخ الأحرف التي لا تعرفها وتستبدل التي تعرفها: كل 2 هو اليوم، و3 ساعة نظام 12 ساعة، و01 كلاهما الشهر، فيُنسَّق 5 مارس الساعة 9:07 بالشكل 5059-03-03. والتخطيط مثل "YYYY-MM-DD" لا يحتوي أي رموز أصلًا فيطبع نفسه دون تغيير. إذا بدت التواريخ المنسّقة غريبة، فتحقّق من أن التخطيط يستخدم قيم المرجع بالضبط.
تحليل النصوص إلى أوقات
تستخدم time.Parse(layout, value) التخطيطات نفسها، وتعيد خطأ يجب أن تفحصه:
دون منطقة في المدخل تعيد Parse توقيت UTC. النص نفسه لوقت الساعة قد يعني لحظات مختلفة حسب المنطقة، ولهذا توجد ParseInLocation. تسمّي رسالة الخطأ الجزء الذي فشلت مطابقته، وهذا يساعد عند تتبّع أخطاء تخطيط.
المناطق الزمنية
الـ time.Time لحظة زائد موقع يُستخدم للعرض. تغيير الموقع بـ In يغيّر طريقة الطباعة، لا اللحظة.
time.UTCمتاح دائمًا. خزّن الأوقات وأرسلها بتوقيت UTC (أو RFC 3339 مع إزاحة) وحوّل فقط للعرض.time.Localهي منطقة الجهاز. على الخوادم وفي الحاويات تكون غالبًا UTC، وعلى الحاسوب المحمول ليست كذلك، فتتصرّف الشيفرة التي تعتمد عليها بشكل مختلف في كل مكان.- تقرأ
time.LoadLocation("Europe/Berlin")قاعدة بيانات IANA من نظام التشغيل. صور الحاويات المصغّرة تفتقدها غالبًا، فيعيد الاستدعاء عندها خطأ. الاستيراد الفارغ_ "time/tzdata"يضمّن قاعدة البيانات في ملفك التنفيذي (نحو 450 KB) فتعمل دائمًا. عالج الخطأ دائمًا. - تنشئ
time.FixedZone(name, offsetSeconds)منطقة بإزاحة ثابتة. لا قواعد توقيت صيفي فيها، فاستخدمها للإزاحات التي تلقّيتها، لا للمناطق المسمّاة.
الحساب والمقارنة
أمور تلاحظها في المخرجات:
- تعطي
AddDate(0, 1, 0)على 31 يناير 3 مارس، لا 28 فبراير. تضيف Go شهرًا لتحصل على "31 فبراير" ثم تطبّع الفائض. إذا احتجت "اليوم نفسه في الشهر التالي مع التقييد بآخر الشهر"، فاكتب ذلك المنطق بنفسك. - تعيد
SubقيمةDuration، وحدّها الأقصى نحو 292 سنة. للفروق بالأيام التقويمية، قارن التواريخ عند منتصف الليل UTC واقسم على 24 ساعة. - للحصول على بداية اليوم، أعد بناء الوقت من
Date(). تقرّبt.Truncate(24 * time.Hour)بالنسبة إلى الوقت الصفري في UTC، فتعطي إجابة خاطئة لأي منطقة غير UTC.
قارن بـ Equal وBefore وAfter، ولا تقارن بـ == أبدًا. يتضمّن time.Now() قراءة ساعة رتيبة، تُستخدم لتبقى time.Since صحيحة إذا عُدّلت ساعة النظام. تقارن == تلك القراءة والموقع أيضًا، فقد لا تتساوى قيمتا Time للحظة نفسها. وللسبب نفسه لا تستخدم time.Time مفتاحًا لخريطة دون تطبيعه أولًا (تزيل t.UTC().Round(0) القراءة الرتيبة).
الـ time.Time الصفري هو 1 يناير من السنة 1، الساعة 00:00 UTC. افحصه بـ t.IsZero().
قياس الوقت المنقضي
start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))
time.Since(start) تساوي time.Now().Sub(start)، وtime.Until(deadline) تساوي deadline.Sub(time.Now()). كلتاهما تستخدمان الساعة الرتيبة عند توفّرها، فهما آمنتان من تغييرات ساعة النظام. ولقياس أداء الشيفرة استخدم دعم قياس الأداء في الحزمة testing بدل التوقيت اليدوي.
المؤقّتات والـ tickers
تعيد time.After(d) قناة تستقبل مرة واحدة بعد d. وtime.Timer هو الشيء نفسه مع تابع Stop. وtime.Ticker يسلّم قيمة كل فترة حتى توقفه.
تصل النبضات الثلاث عند 20 و40 و60 ms قبل مؤقّت الـ 110 ms بوقت طويل، فيتوقّف البرنامج بعد النبضة الثالثة. تشغّل time.AfterFunc(d, f) الدالة f في goroutine خاصة بها بعد d، وهذا مفيد لعمل مؤجّل لمرة واحدة.
منذ Go 1.23 تُجمع المؤقّتات والـ tickers التي لم يعد يشير إليها شيء حتى لو لم تستدعِ Stop قط، ولم تعد قناة المؤقّت الموقوف أو المعاد ضبطه تسلّم قيمة قديمة. لكن استدعاء Stop مع defer يبقى الطريقة الواضحة للقول إن حياة الـ ticker انتهت. وعندما ينطبق حد زمني على عملية كاملة لا على انتظار واحد، تُقرأ context.WithTimeout عادة أفضل من مؤقّت.
الوقت في JSON
يُسلسَل time.Time من نصوص RFC 3339 وإليها في JSON تلقائيًا، بدقة النانوثانية:
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}
نص JSON بصيغة مختلفة يفشل فكّ ترميزه. للطوابع الزمنية Unix أو الصيغ المخصّصة، خزّن int64 أو نصًا وحوّل، أو عرّف نوعًا له UnmarshalJSON خاص به.
أخطاء شائعة
time.Sleep(1)أوtime.Sleep(n)برقم مجرّد. هذه نانوثوانٍ.- تخطيطات بأرقام خاطئة.
"2023-01-01"و"YYYY-MM-DD"ليست تخطيطات. استخدم"2006-01-02". - الخلط بين الدقائق والشهور. الدقائق
04، والشهور01. تطبع"15:01"الساعة ثم الشهر، لا الدقيقة. - المقارنة بـ
==. استخدمEqual. - الاعتماد على
time.Local. تختلف بين الأجهزة. كن صريحًا بـtime.UTCأو بموقع محمّل. - تجاهل الخطأ الصادر عن
LoadLocationأوParse. كلتاهما تفشلان مع المدخلات الحقيقية. تعيدParseالفاشلة الوقت الصفري، الذي يُطبع كسنة 1، وتعيدLoadLocationالفاشلة موقعًاnilيجعلt.In(loc)تسبّب panic.
الأسئلة الشائعة
كيف أجعل البرنامج ينام (sleep) في Go؟
استدعِ time.Sleep مع time.Duration: time.Sleep(2 * time.Second) أو time.Sleep(500 * time.Millisecond). الرقم المجرّد مثل time.Sleep(2) يُترجم لكنه ينام 2 نانوثانية، لأن Duration تعدّ النانوثواني. ولا توقف Sleep إلا الـ goroutine الحالية.
لماذا تستخدم Go الصيغة 2006-01-02 15:04:05 لتنسيق التواريخ؟
تنسّق Go التواريخ بالمثال. التخطيط هو الوقت المرجعي Mon Jan 2 15:04:05 MST 2006 مكتوبًا بالشكل الذي تريد أن تبدو به مخرجاتك. أجزاؤه تتصاعد بالترتيب الأمريكي: الشهر 1، اليوم 2، الساعة 3 (15 بنظام 24 ساعة)، الدقيقة 4، الثانية 5، السنة 6 (2006)، إزاحة المنطقة 7 (-0700). لذا تعني "2006-01-02" سنة ثم شهرًا ثم يومًا، وتعني "02/01/2006" يومًا ثم شهرًا ثم سنة.
كيف أحلّل نص تاريخ في Go؟
استخدم time.Parse(layout, value) مع تخطيط مكتوب بالوقت المرجعي: t, err := time.Parse("2006-01-02", "2026-09-23"). افحص err دائمًا. دون معلومات منطقة في النص تكون النتيجة بتوقيت UTC؛ استخدم time.ParseInLocation لتفسيره في منطقة أخرى.
كيف أحصل على طابع زمني Unix في Go؟
تعيد time.Now().Unix() الثواني منذ 1 يناير 1970 UTC كقيمة int64. وتعطي UnixMilli() وUnixMicro() وUnixNano() وحدات أدق. وللاتجاه المعاكس استخدم time.Unix(sec, 0) أو time.UnixMilli(ms).
كيف أقارن وقتين في Go؟
استخدم t1.Before(t2) وt1.After(t2) وt1.Equal(t2). لا تستخدم ==: فهي تقارن أيضًا الموقع وقراءة الساعة الرتيبة، فقد لا تتساوى قيمتان للحظة نفسها. وتعطي t2.Sub(t1) الفرق كـ Duration.