جافاسكريبت - كيفية تجاهل المنطقة الزمنية للمستخدم وإجبار Date() على استخدام منطقة زمنية معينة
في تطبيق JS، أتلقى الطابع الزمني (على سبيل المثال 1270544790922) من الخادم (Ajax).
استنادًا إلى تلك الطابع الزمني أقوم بإنشاء كائن Date باستخدام:
var _date = new Date();
_date.setTime(1270544790922);
الآن، _date هو الطابع الزمني المفكك في المنطقة الزمنية لمستخدم الحالي. أنا لا أريد ذلك.
أود أن يقوم _date بتحويل الطابع الزمني هذا إلى الوقت الحالي في مدينة هلسنكي في أوروبا (مع تجاهل المنطقة الزمنية الحالية للمستخدم).
كيف يمكنني فعل ذلك؟
الإجابة رقم 1
القيمة الأساسية لكائن التاريخ (Date) في الواقع تكون بالتوقيت العالمي المنسق (UTC). لإثبات ذلك، لاحظ أنه إذا كتبت new Date(0) سترى شيئًا مثل: Wed Dec 31 1969 16:00:00 GMT-0800 (PST). الرقم 0 يُعامل كصفر في توقيت GMT، لكن طريقة .toString() تعرض الوقت المحلي.
ملاحظة كبيرة، UTC تعني الوقت العالمي. الوقت الحالي الآن في مكانين مختلفين هو نفس UTC، ولكن يمكن تنسيق النتيجة بشكل مختلف.
ما نحتاجه هنا هو بعض التنسيق
var _date = new Date(1270544790922);
// outputs > "Tue Apr 06 2010 02:06:30 GMT-0700 (PDT)", for me
_date.toLocaleString('fi-FI', { timeZone: 'Europe/Helsinki' });
// outputs > "6.4.2010 klo 12.06.30"
_date.toLocaleString('en-US', { timeZone: 'Europe/Helsinki' });
// outputs > "4/6/2010, 12:06:30 PM"
هذا يعمل ولكن.... لا يمكنك حقًا استخدام أي من طرق التاريخ الأخرى لأغراضك لأنها تصف المنطقة الزمنية للمستخدم. ما تريده هو كائن تاريخ مرتبط بالمنطقة الزمنية لهلسنكي. خياراتك في هذه المرحلة هي استخدام مكتبة طرف ثالث (أوصي بذلك)، أو تعديل كائن التاريخ بحيث يمكنك استخدام معظم طرقه.
الخيار 1 - طرف ثالث مثل moment-timezone
moment(1270544790922).tz('Europe/Helsinki').format('YYYY-MM-DD HH:mm:ss')
// outputs > 2010-04-06 12:06:30
moment(1270544790922).tz('Europe/Helsinki').hour()
// outputs > 12
يبدو هذا أنيقًا أكثر بكثير من ما نحن على وشك القيام به بعد ذلك.
الخيار 2 - تعديل كائن التاريخ
var currentHelsinkiHoursOffset = 2; // sometimes it is 3
var date = new Date(1270544790922);
var helsenkiOffset = currentHelsinkiHoursOffset*60*60000;
var userOffset = _date.getTimezoneOffset()*60000; // [min*60000 = ms]
var helsenkiTime = new Date(date.getTime()+ helsenkiOffset + userOffset);
// Outputs > Tue Apr 06 2010 12:06:30 GMT-0700 (PDT)
لا يزال يعتقد أنه توقيت GMT-0700 (PDT)، ولكن إذا لم تحدق بشدة قد تتمكن من الخلط بينه وبين كائن تاريخ مفيد لأغراضك.
لقد تخطيت جزءًا بشكل مناسب. تحتاج إلى أن تكون قادرًا على تعريف currentHelsinkiOffset. إذا كان بإمكانك استخدام date.getTimezoneOffset() على جانب الخادم، أو مجرد استخدام بعض عبارات if لوصف متى ستحدث تغييرات المنطقة الزمنية، فإن ذلك يجب أن يحل مشكلتك.
الخاتمة - أعتقد أنه خاصة لهذا الغرض يجب عليك استخدام مكتبة تواريخ مثل moment-timezone.
الإجابة رقم 2
لحساب الأجزاء من الثانية ومنطقة زمن المستخدم، استخدم التالي:
var _userOffset = _date.getTimezoneOffset()*60*1000; // user's offset time
var _centralOffset = 6*60*60*1000; // 6 for central time - use whatever you need
_date = new Date(_date.getTime() - _userOffset + _centralOffset); // redefine variable
الإجابة رقم 3
مجرد نهج آخر
function parseTimestamp(timestampStr) {
return new Date(new Date(timestampStr).getTime() + (new Date(timestampStr).getTimezoneOffset() * 60 * 1000));
};
//Sun Jan 01 2017 12:00:00
var timestamp = 1483272000000;
date = parseTimestamp(timestamp);
document.write(date);في صحتك!
الإجابة رقم 4
لدي شك في أن الإجابة لا تعطي النتيجة الصحيحة. في السؤال، يريد السائل تحويل الطابع الزمني من الخادم إلى الوقت الحالي في هلسنكي بغض النظر عن المنطقة الزمنية الحالية للمستخدم.
الحقيقة هي أن المنطقة الزمنية للمستخدم يمكن أن تكون أي شيء لذلك لا يمكننا الوثوق بها.
إذا كان على سبيل المثال الطابع الزمني 1270544790922 ولدينا دالة:
var _date = new Date();
_date.setTime(1270544790922);
var _helsenkiOffset = 2*60*60;//maybe 3
var _userOffset = _date.getTimezoneOffset()*60*60;
var _helsenkiTime = new Date(_date.getTime()+_helsenkiOffset+_userOffset);
عندما يزور شخص من نيويورك الصفحة، يقوم alert(_helsenkiTime) بطباعة:
Tue Apr 06 2010 05:21:02 GMT-0400 (EDT)
وعندما يزور فنلندي الصفحة، يقوم alert(_helsenkiTime) بطباعة:
Tue Apr 06 2010 11:55:50 GMT+0300 (EEST)
لذلك تكون الدالة صحيحة فقط إذا كان زائر الصفحة لديه المنطقة الزمنية المستهدفة (أوروبا/هلسنكي) في جهازه، لكنها تفشل في تقريبًا كل جزء آخر من العالم. وبما أن الطابع الزمني على الخادم عادةً ما يكون طابعًا زمنيًا من نوع UNIX، والذي يكون حسب التعريف في توقيت UTC، وهو عدد الثواني منذ بداية عصر Unix (1 يناير 1970 00:00:00 بتوقيت جرينتش)، فلا يمكننا تحديد التوقيت الصيفي أو غير الصيفي من الطابع الزمني.
لذلك الحل هو تجاهل المنطقة الزمنية الحالية للمستخدم وتنفيذ طريقة لحساب فرق التوقيت عن UTC سواء كان التاريخ ضمن التوقيت الصيفي أم لا. جافا سكريبت لا تحتوي على طريقة أصلية لتحديد تاريخ الانتقال للتوقيت الصيفي لأي منطقة زمنية غير المنطقة الزمنية الحالية للمستخدم. يمكننا تحقيق هذا بأبسط طريقة باستخدام سكريبت على جانب الخادم، لأن لدينا وصولًا سهلاً إلى قاعدة بيانات المنطقة الزمنية الخاصة بالخادم مع تاريخ الانتقالات الكامل لجميع المناطق الزمنية.
ولكن إذا لم يكن لديك وصول إلى قاعدة بيانات المنطقة الزمنية للخادم (أو أي خادم آخر) وكان الطابع الزمني بتوقيت UTC، يمكنك الحصول على وظيفة مشابهة من خلال ترميز قواعد التوقيت الصيفي في جافاسكريبت.
لتغطية التواريخ في السنوات 1998 - 2099 في أوروبا/هلسنكي يمكنك استخدام الدالة التالية (jsfiddled):
function timestampToHellsinki(server_timestamp) {
function pad(num) {
num = num.toString();
if (num.length == 1) return "0" + num;
return num;
}
var _date = new Date();
_date.setTime(server_timestamp);
var _year = _date.getUTCFullYear();
// Return false, if DST rules have been different than nowadays:
if (_year<=1998 && _year>2099) return false;
// Calculate DST start day, it is the last sunday of March
var start_day = (31 - ((((5 * _year) / 4) + 4) % 7));
var SUMMER_start = new Date(Date.UTC(_year, 2, start_day, 1, 0, 0));
// Calculate DST end day, it is the last sunday of October
var end_day = (31 - ((((5 * _year) / 4) + 1) % 7))
var SUMMER_end = new Date(Date.UTC(_year, 9, end_day, 1, 0, 0));
// Check if the time is between SUMMER_start and SUMMER_end
// If the time is in summer, the offset is 2 hours
// else offset is 3 hours
var hellsinkiOffset = 2 * 60 * 60 * 1000;
if (_date > SUMMER_start && _date < SUMMER_end) hellsinkiOffset =
3 * 60 * 60 * 1000;
// Add server timestamp to midnight January 1, 1970
// Add Hellsinki offset to that
_date.setTime(server_timestamp + hellsinkiOffset);
var hellsinkiTime = pad(_date.getUTCDate()) + "." +
pad(_date.getUTCMonth()) + "." + _date.getUTCFullYear() +
" " + pad(_date.getUTCHours()) + ":" +
pad(_date.getUTCMinutes()) + ":" + pad(_date.getUTCSeconds());
return hellsinkiTime;
}
أمثلة على الاستخدام:
var server_timestamp = 1270544790922;
document.getElementById("time").innerHTML = "The timestamp " +
server_timestamp + " is in Hellsinki " +
timestampToHellsinki(server_timestamp);
server_timestamp = 1349841923 * 1000;
document.getElementById("time").innerHTML += "<br><br>The timestamp " +
server_timestamp + " is in Hellsinki " + timestampToHellsinki(server_timestamp);
var now = new Date();
server_timestamp = now.getTime();
document.getElementById("time").innerHTML += "<br><br>The timestamp is now " +
server_timestamp + " and the current local time in Hellsinki is " +
timestampToHellsinki(server_timestamp);
وطبع هذا ما يلي بغض النظر عن منطقة المستخدم الزمنية:
The timestamp 1270544790922 is in Hellsinki 06.03.2010 12:06:30
The timestamp 1349841923000 is in Hellsinki 10.09.2012 07:05:23
The timestamp is now 1349853751034 and the current local time in Hellsinki is 10.09.2012 10:22:31
بالطبع، إذا كان بإمكانك إعادة الطابع الزمني في شكل تمت فيه إضافة الفرق الزمني (التوقيت الصيفي أو غير الصيفي) بالفعل إلى الطابع الزمني على الخادم، فلن تضطر إلى حسابه على جانب العميل ويمكنك تبسيط الوظيفة كثيرًا. لكن تذكر ألا تستخدم timezoneOffset()، لأنه عندها ستضطر إلى التعامل مع المنطقة الزمنية للمستخدم وهذا ليس السلوك المرغوب فيه.
