چگونه از تاریخ‌ها و زمان‌ها در زبان Go استفاده کنیم؟

این نرم‌افزار برای آسان‌تر کردن انجام کارها طراحی شده است و برای بسیاری از افراد شامل تعامل با تاریخ‌ها و زمان‌ها نیز می‌شود. مقادیر تاریخ و زمان در نرم‌افزارهای مدرن همه‌جا دیده می‌شوند. برای مثال، پیگیری اینکه چه زمانی یک خودرو نیاز به سرویس دارد و اطلاع‌رسانی به صاحب آن، ثبت تغییرات در یک پایگاه‌داده برای ایجاد یک لاگ حسابرسی، یا صرفاً مقایسهٔ دو زمان برای تعیین مدت زمان یک فرایند. بنابراین بازیابی زمان کنونی، دستکاری مقادیر زمان برای استخراج اطلاعات و نمایش آنها به کاربران به شکلی قابل‌فهم برای انسان‌ها، از ویژگی‌های ضروری یک اپلیکیشن است.

در این آموزش، شما یک برنامهٔ Go خواهید ساخت تا زمان محلی فعلی کامپیوتر را بگیرد و سپس آن را به فرمت قابل‌خواندن برای انسان چاپ کند. سپس یک رشته را تفسیر خواهید کرد تا اطلاعات تاریخ و زمان را استخراج کنید. همچنین مقادیر تاریخ و زمان را بین دو ناحیهٔ زمانی ترجمه خواهید کرد و زمان را افزایش یا کاهش می‌دهید تا فاصلهٔ بین دو زمان را محاسبه کنید.

برای دنبال کردن این آموزش، به موارد زیر نیاز دارید:

در این بخش، زمان جاری را با استفاده از بستهٔ time در Go به‌دست می‌آورید

بستهٔ time در کتابخانهٔ استاندارد Go مجموعه‌ای از توابع مرتبط با تاریخ و زمان فراهم می‌کند و می‌تواند یک نقطهٔ مشخص در زمان را با استفاده از نوع time.Time نمایش دهد. علاوه بر زمان و تاریخ، این نوع می‌تواند اطلاعات مربوط به ناحیهٔ زمانی (time zone) را نیز در خود داشته باشد.

برای شروع ایجاد یک برنامه برای بررسی بستهٔ time، باید یک دایرکتوری برای فایل‌ها ایجاد کنید. این دایرکتوری می‌تواند در هر جایی از کامپیوترتان ایجاد شود، اما بسیاری از توسعه‌دهندگان یک دایرکتوری برای پروژه‌هایشان دارند. در این آموزش از دایرکتوری‌ای به نام projects استفاده می‌کنیم.

دایرکتوری projects را بسازید و به آن بروید:

mkdir projects
cd projects

از داخل دایرکتوری projects، با استفاده از mkdir یک دایرکتوری datetime بسازید و سپس با cd وارد آن شوید:

mkdir datetime
cd datetime

پس از ایجاد دایرکتوری پروژه، یک فایل main.go با ویرایشگر مورد‌علاقه‌تان باز کنید (مثلاً nano):

nano main.go

در فایل main.go یک تابع main اضافه کنید که زمان فعلی را بگیرد و آن را چاپ کند:

package main

import (
    "fmt"
    "time"
)

func main() {
    currentTime := time.Now()
    fmt.Println(currentTime)
}

در این برنامه، تابع time.Now از بستهٔ time برای گرفتن زمان محلی فعلی به عنوان یک مقدار time.Time استفاده می‌شود و سپس در متغیر currentTime ذخیره می‌شود. بعد از آن fmt.Println مقدار currentTime را با فرمت رشتهٔ پیش‌فرض time.Time چاپ می‌کند.

برنامه را با استفاده از go run اجرا کنید:

go run main.go

خروجی که زمان و تاریخ فعلی شما را نشان می‌دهد ممکن است مشابه نمونهٔ زیر باشد (با تاریخ/زمان و ناحیهٔ زمانی متفاوت):

خروجی شامل مقدار m= نیز ممکن است باشد. این مقدار ساعت مونوترونیک (monotonic clock) است که Go برای اندازه‌گیری اختلاف زمان به‌صورت داخلی استفاده می‌کند. ساعت مونوترونیک برای جبران هرگونه تغییر احتمالی در ساعت سیستم در حین اجرای برنامه طراحی شده است. با استفاده از ساعت مونوترونیک، مقایسهٔ time.Now() با time.Now() که پنج دقیقه بعد فراخوانی شده باشد، نتیجهٔ درستی خواهد داد حتی اگر ساعت سیستم در آن بازهٔ پنج دقیقه‌ای یک ساعت به جلو یا عقب تغییر کند. لازم نیست مفاهیم آن را کاملاً در این آموزش درک کنید، اما اگر مایل به یادگیری بیشتر هستید، بخش Monotonic Clocks در مستندات بستهٔ time توضیحات بیشتری ارائه می‌دهد: https://pkg.go.dev/time#hdr-Monotonic_Clocks

استخراج بخش‌های مختلف تاریخ و زمان

حالا که زمان فعلی را نمایش می‌دهید، ممکن است فرمت خروجی برای کاربران مفید نباشد. شاید فرمت مدنظر شما نباشد یا شامل قسمت‌هایی از تاریخ یا زمان باشد که نمی‌خواهید نشان داده شوند.

خوشبختانه، نوع time.Time متدهای متنوعی برای گرفتن بخش‌های خاصی از تاریخ یا زمان ارائه می‌دهد. برای مثال اگر تنها بخواهید بخش سال را بدانید، می‌توانید از متد Year استفاده کنید یا برای گرفتن ساعت فعلی از متد Hour استفاده کنید.

فایل main.go را مجدداً باز کنید و چند متد از time.Time را به خروجی اضافه کنید تا ببینید چه چیزی تولید می‌کنند:

package main

import (
    "fmt"
    "time"
)

func main() {
    currentTime := time.Now()
    fmt.Println(currentTime)
    fmt.Println("Year:", currentTime.Year())
    fmt.Println("Month:", currentTime.Month())
    fmt.Println("Day:", currentTime.Day())
    fmt.Println("Hour:", currentTime.Hour())
    fmt.Println("Minute:", currentTime.Minute())
    fmt.Println("Second:", currentTime.Second())
}

برنامه را دوباره اجرا کنید:

go run main.go

خروجی شامل تاریخ و زمان کامل و همچنین سال، ماه، روز، ساعت، دقیقه و ثانیه خواهد بود. توجه کنید که متد Month نوع time.Month بازمی‌گرداند که به صورت رشتهٔ انگلیسی ماه را چاپ می‌کند (مثل August).

استفاده از fmt.Printf برای سفارشی‌سازی چاپ

اکنون خروجی را با یک فراخوانی واحد fmt.Printf چاپ کنید تا فرمت نزدیک‌تری به آنچه می‌خواهید داشته باشید:

package main

import (
    "fmt"
    "time"
)

func main() {
    currentTime := time.Now()
    fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n",
        currentTime.Year(),
        currentTime.Month(),
        currentTime.Day(),
        currentTime.Hour(),
        currentTime.Minute(),
        currentTime.Second())
}

برنامه را اجرا کنید:

go run main.go

این روش ممکن است نزدیک‌تر به خروجی مطلوب باشد، اما برخی موارد می‌تواند سخت باشد: مثلاً نمایش ساعت به فرمت 12 ساعته به جای 24 ساعته نیاز به محاسبات اضافی دارد. دستور دادن فرمت‌های پیچیده با fmt.Printf ممکن است به زحمت و کد اضافی منتهی شود.

استفاده از متد Format برای نمایش تاریخ و زمان

علاوه بر متدهایی مانند Year و Hour، نوع time.Time متدی به نام Format نیز دارد. این متد به شما امکان می‌دهد یک رشتهٔ الگو (layout) ارائه دهید که مشخص می‌کند تاریخ و زمان چگونه نمایش داده شوند. در Go قالب‌بندی تاریخ و زمان کمی متفاوت است: به جای نمادهایی مانند %Y یا %m، از مقادیری استفاده می‌کنید که خودشان نمایندهٔ یک تاریخ مثال هستند. فرمت مرجع در Go عبارت است از: 01/02 03:04:05PM '06 -0700. این توالی بخش‌های مختلف تاریخ/زمان را نمایندگی می‌کند و به شما کمک می‌کند الگوی نمایش را راحت‌تر بسازید.

برای اینکه ببینید Format چگونه کار می‌کند، بهتر است یک زمان ثابت داشته باشید تا هر بار که برنامه را اجرا می‌کنید خروجی تغییر نکند. تابع time.Date به شما اجازه می‌دهد یک مقدار time.Time مشخص با پارامترهای دلخواه بسازید.

package main

import (
    "fmt"
    "time"
)

func main() {
    theTime := time.Date(2021, 8, 15, 14, 30, 0, 0, time.Local)
    fmt.Println(theTime)
}

پارامترهای time.Date به ترتیب شامل سال، ماه، روز، ساعت، دقیقه، ثانیه، نانوثانیه و ناحیهٔ زمانی هستند. بعد از ذخیرهٔ مقدار مشخص در متغیر theTime می‌توانید از آن برای امتحان الگوها استفاده کنید.

حالا از Format برای تولید یک خروجی سفارشی استفاده کنید:

fmt.Println(theTime.Format("January 2, 2006 15:04:05"))

در این الگو، استفاده از 15 برای ساعت نشان‌دهندهٔ فرمت 24 ساعته است.

اگر بخواهید فرمت منظم‌تری داشته باشید و از صفر پیش‌نماینده (leading zero) استفاده کنید یا ساعت 12 ساعته بخواهید، می‌توانید الگوی دیگری به کار ببرید:

fmt.Println(theTime.Format("01/02/2006 03:04:05PM"))

در این حالت اجزای عددی با صفر پر می‌شوند و ساعت به فرمت 12 ساعته نمایش داده می‌شود.

استفاده از قالب‌های از پیش تعریف‌شده (مثلاً RFC3339)

بعضی مواقع لازم است تاریخ‌ها را در قالبی ذخیره کنید که توسط دیگر برنامه‌ها هم قابل خواندن باشد. بستهٔ time قالب‌هایی از پیش تعریف‌شده دارد، از جمله time.RFC3339 و time.RFC3339Nano که نسخهٔ دوم شامل نانوثانیه نیز هست.

fmt.Println(theTime.Format(time.RFC3339Nano))

این قالب برای ذخیرهٔ زمان به صورت رشته‌ای مناسب و قابل‌خواندن توسط زبان‌ها و برنامه‌های دیگر است.

تبدیل رشته به time.Time با Parse

گاهی تاریخ‌ها در قالب رشته هستند و باید به time.Time تبدیل شوند. تابع time.Parse این کار را انجام می‌دهد و مانند Format یک الگو و یک رشته می‌گیرد. توجه داشته باشید که time.Parse علاوه بر مقدار time.Time، یک مقدار خطا (error) نیز برمی‌گرداند تا اگر رشته با الگو تطابق نداشت بتوانید آن را هندل کنید.

timeString := "8/15/2021 14:30:00"
t, err := time.Parse("1/2/2006 15:04:05", timeString)
if err != nil {
    // handle error
}
fmt.Println(t.Format(time.RFC3339Nano))

اگر رشتهٔ ورودی شامل ناحیهٔ زمانی نباشد، time.Parse از UTC (+0) به عنوان ناحیهٔ زمانی پیش‌فرض استفاده می‌کند. اگر می‌خواهید رشته‌ای را با یک ناحیهٔ زمانی مشخص پارس کنید، می‌توانید از time.ParseInLocation استفاده کنید: https://pkg.go.dev/time#ParseInLocation.

همچنین می‌توانید قالب‌های از پیش تعریف‌شده را هنگام پارس کردن استفاده کنید. برای مثال اگر خروجیِ time.RFC3339Nano را دارید:

timeString := "2021-08-15T14:30:00.123456789Z"
t, err := time.Parse(time.RFC3339Nano, timeString)
if err != nil {
    // handle error
}
fmt.Println(t.Format(time.RFC3339Nano))

تبدیل بین ناحیه‌های زمانی (UTC و Local)

در اپلیکیشن‌هایی که کاربران در نواحی زمانی مختلف دارند، معمولاً داده‌ها به صورت UTC ذخیره می‌شوند و هنگام نمایش به زمان محلی کاربر تبدیل می‌شوند. برای تبدیل یک مقدار time.Time به UTC از متد UTC استفاده کنید که یک کپی از زمان را در ناحیهٔ زمانی UTC برمی‌گرداند.

theTime := time.Date(2021, 8, 15, 14, 30, 0, 0, time.Local)
fmt.Println("Local:", theTime.Format(time.RFC3339Nano))
utcTime := theTime.UTC()
fmt.Println("UTC:", utcTime.Format(time.RFC3339Nano))

اگر نیاز دارید UTC را دوباره به زمان محلی تبدیل کنید، از متد Local استفاده کنید:

backToLocal := utcTime.Local()
fmt.Println("Back to Local:", backToLocal.Format(time.RFC3339Nano))

مقایسهٔ زمان‌ها: Before و After

بستهٔ time روش‌هایی برای مقایسهٔ زمان‌ها فراهم می‌کند. متدهای Before و After روی time.Time قابل فراخوانی هستند و یک مقدار bool برمی‌گردانند که نشان می‌دهد آیا زمان مورد نظر قبل/بعد از زمان دیگر است یا خیر.

firstTime := time.Date(2021, 8, 15, 0, 0, 0, 0, time.UTC)
secondTime := time.Date(2021, 12, 25, 0, 0, 0, 0, time.UTC)

fmt.Println(firstTime.Before(secondTime)) // true
fmt.Println(firstTime.After(secondTime))  // false
fmt.Println(secondTime.Before(firstTime)) // false
fmt.Println(secondTime.After(firstTime))  // true

محاسبهٔ فاصلهٔ بین دو زمان با Sub

متد Sub اختلاف بین دو زمان را محاسبه می‌کند و یک مقدار از نوع time.Duration برمی‌گرداند که نشان‌دهندهٔ بازهٔ زمانی است. برخلاف time.Time که یک نقطهٔ زمانی مطلق را نمایش می‌دهد، time.Duration بیان‌کنندهٔ اختلاف یا بازهٔ زمانی است.

duration := firstTime.Sub(secondTime)
fmt.Println(duration)

توجه داشته باشید که مقدار time.Duration بزرگ‌ترین واحدش ساعت است، بنابراین خروجی براساس ساعت نمایش داده می‌شود و به‌صورت روز یا ماه تجزیه نمی‌شود، زیرا تعداد روزها در ماه‌ها متغیر است و در مواردی مانند تغییر ساعت تابستان/زمستان (DST) معنی روز می‌تواند متفاوت باشد.

اضافه و کم کردن زمان با استفاده از time.Duration و Add

برای تعیین زمان گذشته یا آینده بر اساس یک زمان معلوم، می‌توانید مقادیر time.Duration بسازید. ساختن یک time.Duration شبیه نوشتن آن بر روی کاغذ است اما با ضرب در واحدها:

oneHour := 1 * time.Hour
oneMinute := 1 * time.Minute
oneSecond := 1 * time.Second

می‌توانید چند Duration را جمع کنید:

toAdd := 1*time.Hour
fmt.Println(toAdd)

toAdd = toAdd + 1*time.Minute
fmt.Println(toAdd)

toAdd = toAdd + 1*time.Second
fmt.Println(toAdd)

همچنین می‌توانید مقدارها را با هم جمع و تفریق کنید:

toAdd = 1*time.Hour + 1*time.Minute + 1*time.Second
fmt.Println(toAdd)

toAdd = toAdd - (1*time.Minute + 1*time.Second)
fmt.Println(toAdd) // back to 1 hour

برای اضافه کردن یا کم کردن از یک مقدار time.Time از متد Add استفاده کنید. برای اضافه کردن 24 ساعت:

theTime := time.Date(2021, 8, 15, 14, 30, 0, 0, time.UTC)
later := theTime.Add(24 * time.Hour)
fmt.Println(later)

برای کم کردن زمان، از Add با یک مقدار منفی استفاده کنید:

earlier := theTime.Add(-24 * time.Hour)
fmt.Println(earlier)

خلاصه

در این آموزش از time.Now برای گرفتن مقدار time.Time زمان محلی کنونی استفاده کردید و سپس با متدهایی مثل Year و Hour بخش‌هایی از آن را استخراج کردید. سپس با Format تاریخ و زمان را به فرمت دلخواه چاپ کردید و از قالب‌های از پیش تعریف‌شده استفاده نمودید. بعداً با time.Parse یک رشته را به time.Time تبدیل کردید و با UTC و Local بین ناحیهٔ زمانی UTC و لوکال تبدیل انجام دادید. هم‌چنین با Sub مدت زمان بین دو زمان را محاسبه کرده و با Add زمان‌های جدیدی قبل یا بعد از یک زمان معین به‌دست آوردید.

بستهٔ time در Go ویژگی‌های بیشتری نیز دارد که در مستندات مربوطه می‌توانید مطالعه کنید: https://pkg.go.dev/time

از همراهی شما در یادگیری با جامعهٔ پارمین کلود سپاسگزاریم.

Click to rate this post!
[Total: 0 Average: 0]

نظرات کاربران

دیدگاهی بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *