c# - معنى - كم عدد المناطق الزمنية الموجودة في العالم؟




كيفية التعامل بأناقة مع المناطق الزمنية (4)

بعد عدة ملاحظات ، هنا هو الحل النهائي الذي أعتقد أنه نظيف وبسيط ويغطي قضايا توفير الطاقة في ضوء النهار.

1 - نتعامل مع التحويل على مستوى النموذج. لذا ، في فئة النموذج ، نكتب:

    public class Quote
    {
        ...
        public DateTime DateCreated
        {
            get { return CRM.Global.ToLocalTime(_DateCreated); }
            set { _DateCreated = value.ToUniversalTime(); }
        }
        private DateTime _DateCreated { get; set; }
        ...
    }

2 - في مساعد عالمي نقوم بعمل وظيفتنا المخصصة "ToLocalTime":

    public static DateTime ToLocalTime(DateTime utcDate)
    {
        var localTimeZoneId = "China Standard Time";
        var localTimeZone = TimeZoneInfo.FindSystemTimeZoneById(localTimeZoneId);
        var localTime = TimeZoneInfo.ConvertTimeFromUtc(utcDate, localTimeZone);
        return localTime;
    }

3 - يمكننا تحسين ذلك بشكل أكبر ، عن طريق حفظ معرف المنطقة الزمنية في كل ملف تعريف للمستخدم حتى يمكننا استرداده من فئة المستخدم بدلاً من استخدام "التوقيت الرسمي الصيني" الثابت:

public class Contact
{
    ...
    public string TimeZone { get; set; }
    ...
}

4 - هنا يمكننا الحصول على قائمة المنطقة الزمنية لعرضها على المستخدم للاختيار من مربع القائمة المنسدلة:

public class ListHelper
{
    public IEnumerable<SelectListItem> GetTimeZoneList()
    {
        var list = from tz in TimeZoneInfo.GetSystemTimeZones()
                   select new SelectListItem { Value = tz.Id, Text = tz.DisplayName };

        return list;
    }
}

لذلك ، الآن في 9:25 صباحًا في الصين ، موقع الويب المستضاف في الولايات المتحدة الأمريكية ، التاريخ محفوظ في UTC في قاعدة البيانات ، وهنا النتيجة النهائية:

5/9/2013 6:25:58 PM (Server - in USA) 
5/10/2013 1:25:58 AM (Database - Converted UTC)
5/10/2013 9:25:58 AM (Local - in China)

تصحيح

بفضل Matt Johnson للإشارة إلى الأجزاء الضعيفة من الحل الأصلي ، ونأسف على حذف المشاركة الأصلية ، ولكن حصلت على مشكلات في الحصول على تنسيق عرض الشفرة الصحيحة ... تبين للمحرر مشكلات في خلط "الرموز النقطية" مع "الرمز المسبق" ، لذلك إزالة الثيران وكان على ما يرام.

لدي موقع ويب مستضاف في منطقة زمنية مختلفة عن المستخدمين الذين يستخدمون التطبيق. بالإضافة إلى ذلك ، يمكن للمستخدمين الحصول على منطقة زمنية محددة. كنت أتساءل كيف نهج مستخدمي SO وتطبيقات أخرى؟ الجزء الأكثر وضوحا هو أنه داخل DB ، يتم تخزين التاريخ / الأوقات بالتوقيت العالمي المنسق. عندما تكون على الخادم ، يجب التعامل مع كافة التواريخ / الأوقات بالتوقيت العالمي المنسق (UTC). ومع ذلك ، أرى ثلاث مشاكل أحاول التغلب عليها:

  1. الحصول على الوقت الحالي في UTC (حلها بسهولة مع DateTime.UtcNow ).

  2. سحب التاريخ / الأوقات من قاعدة البيانات وعرضها على المستخدم. من المحتمل أن يكون هناك الكثير من المكالمات لطباعة التواريخ على طرق عرض مختلفة. كنت أفكر في بعض الطبقات بين وجهة نظر وأجهزة التحكم التي يمكن أن تحل هذه المسألة. أو وجود طريقة ملحق مخصصة في DateTime (انظر أدناه). الجانب السلبي الرئيسي هو أنه في كل مكان باستخدام وقت / وقت في طريقة العرض ، يجب استدعاء طريقة الإضافة!

    هذا من شأنه أيضا أن يضيف صعوبة في استخدام شيء مثل JsonResult . لم يعد بإمكانك الاتصال بـ Json(myEnumerable) ، يجب أن يكون Json(myEnumerable.Select(transformAllDates)) . ربما يمكن AutoMapper مساعدة في هذه الحالة؟

  3. الحصول على مدخلات من المستخدم (محلي إلى UTC). على سبيل المثال ، يتطلب POSTing نموذج مع تاريخ تحويل التاريخ إلى UTC قبل. أول ما يتبادر إلى الذهن هو إنشاء ModelBinder مخصص.

إليك الإضافات التي فكرت باستخدامها في المشاهدات:

public static class DateTimeExtensions
{
    public static DateTime UtcToLocal(this DateTime source, 
        TimeZoneInfo localTimeZone)
    {
        return TimeZoneInfo.ConvertTimeFromUtc(source, localTimeZone);
    }

    public static DateTime LocalToUtc(this DateTime source, 
        TimeZoneInfo localTimeZone)
    {
        source = DateTime.SpecifyKind(source, DateTimeKind.Unspecified);
        return TimeZoneInfo.ConvertTimeToUtc(source, localTimeZone);
    }
}

أعتقد أن التعامل مع المناطق الزمنية قد يكون أمرًا شائعًا نظرًا لأن الكثير من التطبيقات تعتمد الآن على السحاب ، حيث يمكن أن يكون الوقت المحلي للخادم مختلفًا كثيرًا عن المنطقة الزمنية المتوقعة.

هل تم حل هذا بشكل رائع من قبل؟ هل هناك شيء أفتقده؟ هي محل تقدير كبير الأفكار والأفكار.

تحرير: لتوضيح بعض الارتباك واعتقدت إضافة بعض التفاصيل أكثر. لا تكمن المشكلة في الوقت الحالي في كيفية تخزين توقيت UTC في db ، بل يتعلق بشكل أكبر بعملية الانتقال من التوقيت العالمي المنسق-> Local and Local-> UTC. كما يشيرMax Zerbini ، من الواضح أنه من الذكي وضع رمز UTC المحلي في طريقة العرض ، ولكن هل تستخدم DateTimeExtensions بالفعل الإجابة؟ عند الحصول على مدخلات من المستخدم ، هل من المنطقي قبول التواريخ ModelBinder المستخدم المحلي (بما أن هذا هو ما ModelBinder JS) ثم استخدام ModelBinder للتحويل إلى UTC؟ يتم تخزين المنطقة الزمنية للمستخدم في DB ويتم استردادها بسهولة.


في قسم الأحداث في sf4answers ، يقوم المستخدمون بإدخال عنوان لحدث ، بالإضافة إلى تاريخ بدء وتاريخ انتهاء اختياري. يتم ترجمة هذه الأوقات إلى datetimeoffset في ملقم SQL الذي يمثل الإزاحة من UTC.

هذه هي نفس المشكلة التي تواجهها (على الرغم من أنك تتخذ منهجًا مختلفًا ، في أنك تستخدم DateTime.UtcNow ) ؛ لديك موقع وتحتاج إلى ترجمة وقت من منطقة زمنية إلى أخرى.

هناك أمران رئيسيان فعلتهما بالنسبة لي. أولاً ، استخدم بنية DateTimeOffset ، دائمًا. وهي تمثل إزاحةً عن التوقيت العالمي المنسق (UTC) ، وإذا تمكنت من الحصول على هذه المعلومات من عميلك ، فإنها تجعل حياتك أسهل قليلاً.

ثانيًا ، عند إجراء الترجمات ، بافتراض أنك تعرف الموقع / المنطقة الزمنية التي يوجد فيها العميل ، يمكنك استخدام قاعدة بيانات المنطقة الزمنية للمعلومات العامة لترجمة وقت من التوقيت العالمي المنسق إلى منطقة زمنية أخرى (أو تثليث ، إذا شئتم ، بين المناطق الزمنية). إن الشيء العظيم في قاعدة بيانات tz (يشار إليها أحيانًا باسم قاعدة بيانات Olson ) هو أنها تمثل التغييرات في المناطق الزمنية عبر التاريخ ؛ الحصول على إزاحة دالة على التاريخ الذي ترغب في الحصول على الإزاحة منه (انظر فقط إلى قانون سياسة الطاقة لعام 2005 الذي غير التواريخ عندما يدخل التوقيت الصيفي حيز التنفيذ في الولايات المتحدة ).

مع قاعدة البيانات في متناول اليد ، يمكنك استخدام ZoneInfo (قاعدة بيانات قاعدة بيانات tz / Olson) . NET API . لاحظ أنه لا يوجد توزيع ثنائي ، سيكون عليك تنزيل أحدث إصدار وتجميعه بنفسك.

في وقت كتابة هذه السطور ، يقوم حاليًا بتوزيع جميع الملفات في أحدث توزيع للبيانات (قمت ftp://elsie.nci.nih.gov/pub/tzdata2011k.tar.gz بالفعل مقابل ملف ftp://elsie.nci.nih.gov/pub/tzdata2011k.tar.gz في 25 سبتمبر ، 2011 ؛ في مارس 2017 ، ستحصل عليه عبر https://iana.org/time-zones أو من ftp://fpt.iana.org/tz/releases/tzdata2017a.tar.gz ).

لذا على sf4answers ، بعد الحصول على العنوان ، يتم تشفير البيانات الجغرافية إلى مجموعة خطوط الطول والعرض ثم إرسالها إلى خدمة ويب خارجية للحصول على منطقة زمنية تتوافق مع إدخال في قاعدة بيانات tz. من هناك ، يتم تحويل أوقات البدء والانتهاء إلى مثيلات DateTimeOffset مع إزاحة UTC المناسبة ثم تخزينها في قاعدة البيانات.

بالنسبة للتعامل مع SO والمواقع الإلكترونية ، يعتمد ذلك على الجمهور وما تحاول عرضه. إذا لاحظت ، تعرض معظم مواقع ويب الاجتماعية (و SO ، وقسم الأحداث في sf4answers) الأحداث في وقت نسبي ، أو ، إذا تم استخدام قيمة مطلقة ، فعادةً ما يكون UTC.

ومع ذلك ، إذا كان جمهورك يتوقع أوقاتًا محلية ، فسيكون استخدام DateTimeOffset مع طريقة إضافة تأخذ المنطقة الزمنية للتحويل إلى ما يرام ؛ سوف يترجم datetimeoffset نوع بيانات SQL إلى .NET DateTimeOffset التي يمكنك بعد ذلك الحصول على الوقت العالمي لاستخدام أسلوب GetUniversalTime . من هناك ، ما عليك سوى استخدام الطرق على فئة ZoneInfo لتحويلها من التوقيت العالمي المنسق إلى التوقيت المحلي (سيكون عليك القيام ببعض الأعمال للحصول عليها في DateTimeOffset ، ولكنها بسيطة بما يكفي للقيام بها).

أين نفعل التحول؟ هذه هي التكلفة التي يجب عليك دفعها في مكان ما ، وليس هناك طريقة "أفضل". ولكنني سأختار العرض ، مع إزاحة المنطقة الزمنية كجزء من نموذج العرض المعروض في العرض. وبهذه الطريقة ، إذا تغيرت متطلبات العرض ، فلن تضطر إلى تغيير نموذج العرض الخاص بك لاستيعاب التغيير. كان JsonResult الخاص بك ببساطة تحتوي على نموذج مع IEnumerable<T> والإزاحة.

على جانب المدخلات ، باستخدام الموثق نموذج؟ أود أن أقول على الإطلاق بأي حال من الأحوال. لا يمكنك ضمان أن جميع التواريخ (الآن أو في المستقبل) يجب أن يتم تحويلها بهذه الطريقة ، يجب أن تكون وظيفة صريحة لوحدة التحكم الخاصة بك لتنفيذ هذا الإجراء. مرة أخرى ، إذا تغيرت المتطلبات ، فلن تضطر إلى تعديل ModelBinder أو ModelBinder مثيلات ModelBinder لضبط منطق عملك. وهو منطق الأعمال ، مما يعني أنه يجب أن يكون في وحدة التحكم.


للإخراج ، قم بإنشاء قالب عرض / محرر مثل هذا

@inherits System.Web.Mvc.WebViewPage<System.DateTime>
@Html.Label(Model.ToLocalTime().ToLongTimeString()))

يمكنك ربطها استنادًا إلى السمات الموجودة في النموذج الخاص بك إذا كنت ترغب فقط في استخدام نماذج معينة لهذه النماذج.

انظر here here لمزيد من التفاصيل حول إنشاء قوالب محرر مخصصة.

بدلا من ذلك ، بما أنك تريد أن تعمل من أجل كل من المدخلات والمخرجات ، أود أن أقترح توسيع السيطرة أو حتى إنشاء الخاصة بك. بهذه الطريقة يمكنك اعتراض كل من المدخلات والمخرجات وتحويل النص / القيمة حسب الحاجة.

من المؤمل أن يدفعك هذا الارتباط في الاتجاه الصحيح إذا كنت ترغب في السير في هذا الطريق.

في كلتا الحالتين ، إذا كنت تريد حلا رائعا ، سيكون جزء من العمل. على الجانب المشرق ، بمجرد الانتهاء من ذلك يمكنك الاحتفاظ بها في مكتبة التعليمات البرمجية للاستخدام في المستقبل!


هذا على الأرجح مطرقة لكسر صامولة ولكن يمكنك حقن طبقة بين طبقات واجهة المستخدم وطبقة العمل التي تقوم بتحويل شفافية datetimes إلى التوقيت المحلي على الرسوم البيانية للأجسام المرتجعة ، وإلى UTC على معلمات datetime المدخلات.

أتصور أن هذا يمكن أن يتحقق باستخدام PostSharp أو بعض انعكاس حاوية التحكم.

أنا شخصياً ، سأذهب فقط مع تحويل تواريخك في واجهة المستخدم صراحة ...





timezone