یکی از مشکلات متداولی که برنامه نویسان برنامه های
تحت وب و به طور کلی برنامه های توزیع شده (Distributed Applications)
با آن روبرو هستند، ذخیره سازی تاریخ و زمان در پایگاه داده می باشد.
ادامه مقاله را با ذکر دو مثال دنبال می کنیم.
اگر شما برای یک منطقه خاص جغرافیایی (Time
Zone) برنامه نویسی انجام می دهید، شاید نحوه ذخیره سازی تاریخ و
زمان چندان مسئله ساز نباشد و شما براحتی با استفاده از دستور
DateTime.Now
در فریم ورک DotNet و یا با استفاده از متد
GETDATE
در پایگاه داده SQL Server مشکل خود را حل نمایید. دلیل این
موضوع این است که در یک منطقه جغرافیایی خاص، تاریخ و زمان در تمام نقاط یکسان می
باشد. به طور مثال هنگامی که در تهران ساعت 4:30 باشد، در مشهد، اصفهان و تبریز نیز
ساعت 4:30 است و در نتیجه تاریخ و زمان ذخیره سازی برای ساعت 4:30 کاملا معتبر می
باشد.
ولی در مورد کشور هایی که در مناطق مختلف جغرافیایی
قرار دارند، این موضوع متفاوت می باشد زیرا مناطق مختلف جغرافیایی با یکدیگر اختلاف
زمانی دارند. مثلا هنگامی که در کشور ایران ساعت 4:30 می باشد، در کشور های کانادا
و ایالت های مختلف امریکا، ساعت چندین ساعت جلوتر و یا عقب تر می باشد.
فرض کنید قرار است کنفرانسی در تاریخ 9 آوریل و ساعت
18:30 قرار است در یک از ایالت های امریکا برگزار گردد و شما قصد دارید از تمام افراد واجد شرایط در کشورهای
مختلف دنیا برای این کنفرانس دعوت به عمل آورید. ایالت های مختلف امریکا در منطقه
های جغرافیایی مختلفی قرار دارند. یعنی مثلا اگر کنفرانس قرار باشد در ساعت 18:30
به وقت تگزاس برگزار گردد، این ساعت ممکن است برابر ساعت 14:30 به وقت ایالت دالاس
باشد! پس چگونه باید تاریخ و زمان را اعلام نمود؟
به مثالی دیگر توجه فرمایید.
فرض کنید شما در حال طراحی یک برنامه تالار گفتمان
هستید. شخصی در یک منطقه زمانی خاص سوالی را در این تالار مطرح می کند و شما تاریخ
مطرح شدن سوال را براحتی ذخیره می کنید. این تاریخ می تواند 9 آوریل 2010 ساعت
16:30 باشد. اکنون شخص دیگری وارد تالار گفتمان می شود که در منطقه جغرافیایی
متفاوتی قرار دارد. این شخص ملاحظه می کند که تاریخ ثبت شده برای این سوال، مربوط
به چند ساعت بعد می باشد که هنوز فرا نرسیده است! چگونه باید این مشکل را رفع نمود؟
قبل از اینکه به جواب دادن به سوالات فوق بپردازم
لازم می باشد که مقدمه ای را بیان کنم.
برای ذخیره نمودن تاریخ در پایگاه داده SQL
Server معمولا از دو روش استفاده می شود. یا تاریخ توسط برنامه
برای پایگاه داده ارسال می شود (به طور مثال با استفاده از دستور
DateTime.Now) و یا اینکه با استفاده از
تابع GETDATE مربوط به SQL Server
و از طریق پایگاه داده این عمل انجام می شود. (در اینجا فرض بر این است که نوع فیلد
پایگاه داده از جنس DateTime می باشد و بنده قصد معرفی
انواع مختلف فیلد های پایگاه داده SQL Server را که جهت
نگهداری انواع تاریخ و زمان استفاده می شود را ندارم)
اگر وب سایت و پایگاه داده بر روی یک سرور و یا در یک
منطقه جغرافیایی باشند، تفاوت چندانی بین دو روش ذکر شده در بالا وجود ندارد ولی
اگر سرور وب سایت در یک منطقه جغرافیایی و سرور پایگاه داده در یک منطقه دیگر
جغرافیایی قرار داشته باشد، بدیهی است که زمان ذخیره شده در پایگاه داده با زمانی
که کاربر در وب سایت اطلاعات را وارد نموده است، متفاوت می شود.
بهترین روش:
اما بهترین روش برای حل مشکلاتی که در بالا ذکر شد،
استفاده از زمان استاندارد بین المللی یعنی Coordinated Universal Time یا به عبارت
دیگری UTC می باشد. این زمان در تمام مناطق جغرافیایی یکسان
می باشد. یعنی اگر زمان UTC در حال حاضر 9 آوریل 2010 و
ساعت 16:30 باشد، این ساعت در تمام دنیا یکسان می باشد. در فریم ورک DotNet
با استفاده از دستور DateTime.UtcNow و در پایگاه داده SQL Server
با استفاده از تابع GETutcDATE می توانید به این
تاریخ و زمان دسترسی پیدا نمایید.
در شکل زیر حاصل دو دستور مختلف ذکر شده برای پاگاه
داده SQL Server را ملاحظه می نمایید.
دقت کنید که حاصل اجرای دستور
GETDATE
ساعت 22:58 می باشد در حالیکه حاصل اجرای دستور
GETutcDATE
ساعت 18:28 می باشد. مبنای تشخیص ساعت تمام مناطق جغرافیایی
بر اساس تاریخ و زمان UTC می باشد و ما همواره با اضافه و
یا کم نموده میزان مشخصی از زمان UTC می توانیم تاریخ و
زمان هر منطقه جغرافیایی را بدست آوریم.(در ادامه مقاله این موضوع را ملاحظه می
نمایید)
توجه:
میزان زمانی که با توجه به تاریک و روشن بودن هوا در
کشور های مختلف از ساعت رسمی کم و زیاد می شوند (Daylight
Saving) در تاریخ و زمان
UTC مورد محاسبه قرار نمی گیرد. به طور مثال در کشور ما در
ابتدای سال یک ساعت زمان رسمی به جلو کشیده شده و در ابتدای شش ماهه دوم، این زمان
مجددا به عقب کشیده می شود.
تاکنون مشکل ذخیره سازی تاریخ و زمان را حل نموده ایم
ولی مسئله ای که هم اکنون وجود دارد این است که چگونه این زمان را نمایش دهیم. به
عبارت دیگر، کاربران و مخاطبین وب سایت معمولا آگاهی چندانی در مورد تبدیل زمان بین
المللی UTC به زمان منطقه جغرافیایی خود ندارند و شما صرفا
با نمایش تاریخ UTC کمک چندانی به مخاطبین خود ننموده اید.
در اینجا نحوه های مختلف نمایش زمان را
ملاحظه می نمایید.
-
نمایش تاریخ و زمان دقیقا همانطور که در پایگاه
داده ذخیره شده است
-
نمایش تاریخ و زمان به صورت نسبی و تقریبی
-
نمایش تاریخ و زمان متناسب با هر منطقه جغرافیایی
اکنون به معرفی هر کدام از این روش ها می پردازیم.
نمایش تاریخ و زمان دقیقا همانطور که در پایگاه داده
ذخیره شده است:
در این روش، تاریخ را همانگونه که در پایگاه داده
ذخیره شده است، در برنامه نمایش می دهیم. بدیهی است که این روش جهت وب سایت هایی که
فعالیت در چند منطقه جغرافیایی مختلف دارند، چندان مناسب نمی باشد.
نمایش تاریخ و زمان به صورت نسبی و تقریبی:
در این روش نمایش تاریخ و زمان بسیار جالب می باشد. اگر
به سایت های مربوط به شبکه های اجتماعی و یا تالارهای گفتمان مراجعه نموده باشید،
بارها با این موضوع روبرو شده اید که زمان گذاشتن مطلب به شکل های زیر نمایش داده
می شود:
-
یک دقیقه پیش
-
سه ساعت پیش
-
دو روز پیش
-
30 ثانیه پیش
این سبک نمایش تاریخ بسیار متداول می باشد و در سایت
هایی کاربرد دارد که زمان دقیق، چندان مورد اهمیت نمی باشد. در این روش شما با
استفاده از تاریخ ثبت شدن سوال و زمان حال، می توانید مدت زمان سپری شده را محاسبه
نمایید و دیگر توفیری ندارد که شخص بازدیدکننده در وب سایت در کدام منطقه جغرافیایی
قرار دارد.
در قسمت زیر یک پیاده سازی به زبان #C
و به صورت
متدهای توسعه گر(Extension Method) برای کلاس
DateTime
ملاحظه می نمایید.
public static class DateTimeExtensions
{
#region Methods
public static string ToRelativeDateString(this DateTime date)
{
return GetRelativeDateValue(date, DateTime.Now);
}
public static string ToRelativeDateStringUtc(this DateTime date)
{
return GetRelativeDateValue(date, DateTime.UtcNow);
}
private static string GetRelativeDateValue(DateTime date, DateTime comparedTo)
{
TimeSpan diff = comparedTo.Subtract(date);
if (diff.Days >= 7)
return string.Concat("on ", date.ToString("MMMM dd, yyyy"));
else if (diff.Days > 1)
return string.Concat(diff.Days, " days ago");
else if (diff.Days == 1)
return "yesterday";
else if (diff.Hours >= 2)
return string.Concat(diff.Hours, " hours ago");
else if (diff.Minutes >= 60)
return "more than an hour ago";
else if (diff.Minutes >= 5)
return string.Concat(diff.Minutes, " minutes ago");
if (diff.Minutes >= 1)
return "a few minutes ago";
else
return "less than a minute ago";
}
#endregion Methods
}
اگر زمان را به شکل معمولی ذخیره نموده باشیم، با
استفاده از متد ToRelativeDateString می توانیم زمانی نسبی مورد نظر را بدست آوریم
و در صورتی که زمان را به شکل UTC ذخیره نموده باشیم، با
فراخوانی تابع ToRelativeDateStringUtc می توانیم زمان نسبی سپری شده را بدست
آوریم.
در قسمت زیر نحوه استفاده از متدهای بالا را ملاحظه
می نمایید:
DateTime dateTime = GetDatabaseTime();
string relativeTimeString= DateTime.ToRelativeDateString(dateTime);
DateTime dateTime = GetDatabaseUtcTime();
string relativeTimeString = DateTime.ToRelativeDateStringUtc(dateTime);
نمایش تاریخ و زمان متناسب با هر منطقه جغرافیایی:
در این روش ما زمان را به صورت UTC
در دیتابیس ذخیره می کنیم و سپس منطقه جغرافیایی کاربر را بدست می آوریم. اکنون زمان
ذخیره شده به شکل UTC را تبدیل به زمان مورد استفاده کاربر
در یک محدوده جغرافیایی خاص می کنیم.
فرض کنید که یک کاربر از کشور ایران وارد وب سایت می
شود و قصد داریم زمانی که در دیتابیس به شکل UTC ذخیره شده
است را به زمان منطقه جغرافیایی ایران تبدیل کنیم. برای انجام این کار باید مشابه
زیر عمل نماییم.
TimeZoneInfo timeZoneInfo;
DateTime dateTime;
//Set the time zone information to Iran Standard Time
timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Iran Standard Time");
//Get date and time in Iran Standard Time
dateTime = TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZoneInfo);
//Print out the date and time
Label1.Text = dateTime.ToString();
در قطعه کد بالا ما شناسه منطقه جغرافیایی
(Time Zone ID) ایران("Iran Standard Time")
را به تابع FindSystemTimeZoneById ارسال نموده ایم و این تابع، مشخصات منطقه
جغرافیایی ایران را برگردانده است. سپس با استفاده از دستور TimeZoneInfo.ConvertTime
زمان دخیره شده به شکل UTC تبدیل به زمان مورد استفاده در
منطقه جغرافیایی ما می شود.
لیست کامل شناسه مناطق مختلف
جغرافیایی جهان را در قسمت زیر ملاحظه می نمایید.
Time Zone ID |
Time Zone Display
Name |
Morocco Standard Time |
(GMT) Casablanca |
GMT Standard Time |
(GMT) Greenwich Mean Time : Dublin,
Edinburgh, Lisbon, London |
Greenwich Standard Time |
(GMT) Monrovia, Reykjavik |
W. Europe Standard Time |
(GMT+01:00) Amsterdam, Berlin, Bern,
Rome, Stockholm, Vienna |
Central Europe Standard Time |
(GMT+01:00) Belgrade, Bratislava,
Budapest, Ljubljana, Prague |
Romance Standard Time |
(GMT+01:00) Brussels, Copenhagen, Madrid,
Paris |
Central European Standard Time |
(GMT+01:00) Sarajevo, Skopje, Warsaw,
Zagreb |
W. Central Africa Standard Time |
(GMT+01:00) West Central Africa |
Jordan Standard Time |
(GMT+02:00) Amman |
GTB Standard Time |
(GMT+02:00) Athens, Bucharest, Istanbul |
Middle East Standard Time |
(GMT+02:00) Beirut |
Egypt Standard Time |
(GMT+02:00) Cairo |
South Africa Standard Time |
(GMT+02:00) Harare, Pretoria |
FLE Standard Time |
(GMT+02:00) Helsinki, Kyiv, Riga, Sofia,
Tallinn, Vilnius |
Israel Standard Time |
(GMT+02:00) Jerusalem |
E. Europe Standard Time |
(GMT+02:00) Minsk |
Namibia Standard Time |
(GMT+02:00) Windhoek |
Arabic Standard Time |
(GMT+03:00) Baghdad |
Arab Standard Time |
(GMT+03:00) Kuwait, Riyadh |
Russian Standard Time |
(GMT+03:00) Moscow, St. Petersburg,
Volgograd |
E. Africa Standard Time |
(GMT+03:00) Nairobi |
Georgian Standard Time |
(GMT+03:00) Tbilisi |
Iran Standard Time |
(GMT+03:30) Tehran |
Arabian Standard Time |
(GMT+04:00) Abu Dhabi, Muscat |
Azerbaijan Standard Time |
(GMT+04:00) Baku |
Mauritius Standard Time |
(GMT+04:00) Port Louis |
Caucasus Standard Time |
(GMT+04:00) Yerevan |
Afghanistan Standard Time |
(GMT+04:30) Kabul |
Ekaterinburg Standard Time |
(GMT+05:00) Ekaterinburg |
Pakistan Standard Time |
(GMT+05:00) Islamabad, Karachi |
West Asia Standard Time |
(GMT+05:00) Tashkent |
India Standard Time |
(GMT+05:30) Chennai, Kolkata, Mumbai, New
Delhi |
Sri Lanka Standard Time |
(GMT+05:30) Sri Jayawardenepura |
Nepal Standard Time |
(GMT+05:45) Kathmandu |
N. Central Asia Standard Time |
(GMT+06:00) Almaty, Novosibirsk |
Central Asia Standard Time |
(GMT+06:00) Astana, Dhaka |
Myanmar Standard Time |
(GMT+06:30) Yangon (Rangoon) |
SE Asia Standard Time |
(GMT+07:00) Bangkok, Hanoi, Jakarta |
North Asia Standard Time |
(GMT+07:00) Krasnoyarsk |
China Standard Time |
(GMT+08:00) Beijing, Chongqing, Hong
Kong, Urumqi |
North Asia East Standard Time |
(GMT+08:00) Irkutsk, Ulaan Bataar |
Singapore Standard Time |
(GMT+08:00) Kuala Lumpur, Singapore |
W. Australia Standard Time |
(GMT+08:00) Perth |
Taipei Standard Time |
(GMT+08:00) Taipei |
Tokyo Standard Time |
(GMT+09:00) Osaka, Sapporo, Tokyo |
Korea Standard Time |
(GMT+09:00) Seoul |
Yakutsk Standard Time |
(GMT+09:00) Yakutsk |
Cen. Australia Standard Time |
(GMT+09:30) Adelaide |
AUS Central Standard Time |
(GMT+09:30) Darwin |
E. Australia Standard Time |
(GMT+10:00) Brisbane |
AUS Eastern Standard Time |
(GMT+10:00) Canberra, Melbourne, Sydney |
West Pacific Standard Time |
(GMT+10:00) Guam, Port Moresby |
Tasmania Standard Time |
(GMT+10:00) Hobart |
Vladivostok Standard Time |
(GMT+10:00) Vladivostok |
Central Pacific Standard Time |
(GMT+11:00) Magadan, Solomon Is., New
Caledonia |
New Zealand Standard Time |
(GMT+12:00) Auckland, Wellington |
Fiji Standard Time |
(GMT+12:00) Fiji, Kamchatka, Marshall Is. |
Tonga Standard Time |
(GMT+13:00) Nuku'alofa |
Azores Standard Time |
(GMT-01:00) Azores |
Cape Verde Standard Time |
(GMT-01:00) Cape Verde Is. |
Mid-Atlantic Standard Time |
(GMT-02:00) Mid-Atlantic |
E. South America Standard Time |
(GMT-03:00) Brasilia |
Argentina Standard Time |
(GMT-03:00) Buenos Aires |
SA Eastern Standard Time |
(GMT-03:00) Georgetown |
Greenland Standard Time |
(GMT-03:00) Greenland |
Montevideo Standard Time |
(GMT-03:00) Montevideo |
Newfoundland Standard Time |
(GMT-03:30) Newfoundland |
Atlantic Standard Time |
(GMT-04:00) Atlantic Time (Canada) |
SA Western Standard Time |
(GMT-04:00) La Paz |
Central Brazilian Standard Time |
(GMT-04:00) Manaus |
Pacific SA Standard Time |
(GMT-04:00) Santiago |
Venezuela Standard Time |
(GMT-04:30) Caracas |
SA Pacific Standard Time |
(GMT-05:00) Bogota, Lima, Quito, Rio
Branco |
Eastern Standard Time |
(GMT-05:00) Eastern Time (US & Canada) |
US Eastern Standard Time |
(GMT-05:00) Indiana (East) |
Central America Standard Time |
(GMT-06:00) Central America |
Central Standard Time |
(GMT-06:00) Central Time (US & Canada) |
Central Standard Time (Mexico) |
(GMT-06:00) Guadalajara, Mexico City,
Monterrey |
Canada Central Standard Time |
(GMT-06:00) Saskatchewan |
US Mountain Standard Time |
(GMT-07:00) Arizona |
Mountain Standard Time (Mexico) |
(GMT-07:00) Chihuahua, La Paz, Mazatlan |
Mountain Standard Time |
(GMT-07:00) Mountain Time (US & Canada) |
Pacific Standard Time |
(GMT-08:00) Pacific Time (US & Canada) |
Pacific Standard Time (Mexico) |
(GMT-08:00) Tijuana, Baja California |
Alaskan Standard Time |
(GMT-09:00) Alaska |
Hawaiian Standard Time |
(GMT-10:00) Hawaii |
Samoa Standard Time |
(GMT-11:00) Midway Island, Samoa |
Dateline Standard Time |
(GMT-12:00) International Date Line West |
در صورتی که شما قصد دارید از این روش جهت نمایش
تاریخ استفاده نمایید، بهتر است از کاربران هنگام ثبت نام در سایت، نام منطقه
جغرافیایی مورد نظر آن ها را سوال نموده و ذخیره نمایید (حتما تاکنون هنگام ثبت نام
در تالار های گفتمان با گزینه انتخاب Time Zone مواجه شده
اید)
در غیر اینصورت باید با توجه به IP
کاربر منطقه جغرافیایی را محاصبه نموده و سایر موارد را اعمال نمایید.
نمایش تاریخ با فرمت مناسب:
یکی دیگر از مشکلاتی که هنگام نمایش تاریخ با آن
روبرو هستیم، فرمت نمایش تاریخ می باشد.
به نحوه نمایش تاریخ در چند فرهنگ زیر توجه نمایید:
// Iran --> YYYY/MM/DD -- روز/ماه/سال
// USA --> MM/DD/YYYY -- سال/روز/ماه
// United Kingdom --> DD/MM/YYYY -- سال/ماه/روز
همانطور که ملاحظه می نمایید، تاریخ 5/7/2010 در کشور
انگلستان و امریکا کاملا معنی متفاوتی می دهد. در انگلستان این تاریخ برابر پنجم
ماه جولای سال 2010 می باشد در حالیکه در امریکا این تاریخ هفتم ماه MAY
سال 2010 معنا می دهد!
برای نمایش تاریخ های میلادی پیشنهاد می شود از فرمت
های زیر بنابر نیاز خود استفاده نمایید.
string result = DateTime.Now.ToString("d MMM yyyy h:mm tt"); // result is --> 10 Apr 2010 12:26 AM
//OR
string result = DateTime.Now.ToString("MMMM d, yyyy h:mm tt"); // result is --> April 10, 2010 12:29 AM
منابع: 30sharp.com
4GuysFromRolla
xiirus