2015-01-22 12 views
7

हमारे पास ASP.NET में एक ऐप है जो विंडोज उपयोगकर्ता प्रारूप (टाइमज़ोनइन्फो.आईडी द्वारा) में सभी उपयोगकर्ता टाइमज़ोन डेटा स्टोर करता है।विंडोज टाइमज़ोन को moment.js टाइमज़ोन में कनवर्ट करें?

हम पल.जेएस और पल.जेएस टाइमजोन लाइब्रेरी का भी उपयोग करते हैं ताकि यूटीसी डेटा को क्लाइंट साइड पर उपयोगकर्ता डेटा में परिवर्तित किया जा सके। यह एक जटिल AngularJs अनुप्रयोग है जिसे क्लाइंट साइड पर टाइमज़ोन रूपांतरण करने की आवश्यकता है।

अब तक हमने विंडोज टाइमज़ोन आईडी को Moment.js टाइमज़ोन आईडी में कनवर्ट करने के लिए नोडाटाइम .NET लाइब्रेरी का उपयोग किया है। यह सबसे आम समय क्षेत्रों के लिए अच्छा काम किया। लेकिन हमें यह रूपांतरण 100% संगत बनाने की आवश्यकता है।

वर्तमान में ऐसा प्रतीत होता है कि आईएएनए समय क्षेत्र डेटा में विंडोज टाइमज़ोन आईडी को मैप करने का कोई विश्वसनीय तरीका नहीं है। बहुत सारी विसंगतियां हैं।

मेरा मानना ​​है कि आधुनिक जेएस ऐप्स अक्सर समय क्षेत्र के साथ सौदा करते हैं। और कभी-कभी सर्वर-साइड (सी #) और क्लाइंट-साइड (जेएस) पर टीजेड को कन्वर्ट करने की आवश्यकता होती है।

क्या Moment.js timezone ऑब्जेक्ट में .NET TimeZoneInfo कड़ाई से मानचित्र/रूपांतरित करने का कोई तरीका है?

+1

पल-टाइमज़ोन आईएएनए मानक समय क्षेत्र का उपयोग करता है। यदि आपको एक विशिष्ट समय क्षेत्र मिलता है जो लिंक किए गए उत्तर में 'WindowsToIana' फ़ंक्शन का उपयोग करके सही ढंग से मानचित्र नहीं करता है, तो कृपया मुझे बताएं कि कौन सा है। सभी 'TimeZoneInfo' आईडी को मैप करने योग्य होना चाहिए। –

+0

मैट, यह पूरी तरह से सच नहीं है। जबकि moment.js आईएएनए प्रारूप का उपयोग करता है, विंडोज टाइमज़ोन आईएएनए टाइमज़ोन में 100% मैप नहीं किया जा सकता है। कई विसंगतियां होंगी। पूरी तरह से समस्या को हल करने के लिए, हम timeZoneInfo नियमों को moment.js जोन डेटा ऑब्जेक्ट में बदल सकते हैं। तो 100% विंडोज-> Moment.js मैपिंग होगा। – Evgenyt

+0

@Evgenyt - यह वांछित ग्रैन्युलरिटी और इतिहास के स्तर पर निर्भर करता है। एक उपयोगकर्ता के दृष्टिकोण उनके वर्तमान समय क्षेत्र का चयन करके और आधुनिक समय के भीतर डेटा के साथ काम करने से, तो [CLDR मैपिंग] (http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml) कि यूनिकोड रखरखाव उचित रूप से सटीक हैं, और सभी जोन खिड़कियों-> iana दिशा में मैपबल हैं। यदि आप "प्राथमिक" (001) क्षेत्र चुनने के बजाय देश कोड का उपयोग करते हैं तो आप बेहतर सटीकता भी प्राप्त कर सकते हैं। यह केवल iana-> विंडोज़ दिशा में है कि अपरिवर्तनीय जोन हैं। –

उत्तर

6

टी एल; डॉ:

  • Noda समय का उपयोग कर सर्वर साइड
  • पर रखें बीसीएल डेटा या IANA डेटा का उपयोग करें या नहीं; मैं व्यक्तिगत रूप से आईएएनए की सिफारिश करता हूं, लेकिन यह आपका फोन है। (किसी और चीज के अलावा, आईएएनए डेटा अधिक स्पष्ट रूप से संस्करणित है।)
  • पल.जेएस डेटा उत्पन्न करने के लिए नोडा समय का उपयोग करें ताकि आप बिल्कुल क्लाइंट का उपयोग करेंगे, और यह आपके द्वारा किए गए कार्यों के अनुरूप होगा सर्वर
  • कार्य बाहर क्या होता है जब डेटा में परिवर्तन

विवरण के लिए एक रणनीति:

और कभी-कभी सर्व पर बिल्कुल TZ बदलने की आवश्यकता एर-साइड (सी #) और क्लाइंट-साइड (जेएस)।

आप दोनों पक्षों और दोनों पक्षों पर बराबर कार्यान्वयन पर ठीक उसी समय क्षेत्र डेटा प्राप्त करने के की जरूरत है। यह समस्या है क्योंकि:

  • IANA समय क्षेत्र डेटा नियमित रूप से अद्यतन किया जाता है
  • विंडोज समय क्षेत्र डेटा नियमित रूप से अद्यतन किया जाता है
  • (ताकि आप कहने के लिए उदाहरण के लिए "उपयोग डेटा 2015a" में सक्षम होना चाहिए चाहते हैं) मैं आंशिक रूप से कुछ odd bugs और आंशिक रूप से include more data को दूर करने के लिए भले ही वे किया जाना चाहिए
  • मैं जानते हैं कि TimeZoneInfo कार्यान्वयन समय के साथ बदल गया है, शर्त करने के लिए बिल्कुल वैसा ही है कि IANA नियमों के हर कार्यान्वयन नहीं करना चाहते हैं,। (.NET 4.6 इसके मानक बदल रहा है एक समय क्षेत्र इतिहास में ऑफसेट की अवधारणा को समझता है, पिछले संस्करणों नहीं है)

Noda समय के साथ आप बहुत आसानी से या तो बीसीएल या IANA समय क्षेत्र प्रारूप moment.js करने के लिए डेटा परिवर्तित कर सकते हैं - और Evgenyt के कोड से अधिक भरोसेमंद करें, क्योंकि TimeZoneInfo आपको संक्रमण का अनुरोध करने की अनुमति नहीं देता है। (TimeZoneInfo में बग के कारण, छोटे जेब होते हैं जहां ऑफसेट कुछ घंटों के लिए बदल सकते हैं - उन्हें नहीं करना चाहिए, लेकिन यदि आप TimeZoneInfo व्यवहार से बिल्कुल मेल खाना चाहते हैं, तो आपको उन सभी को खोजने में सक्षम होना चाहिए - Evgenyt का कोड हमेशा उनको नहीं खोजेगा।) अगर नोडा समय TimeZoneInfo बिल्कुल दर्पण नहीं करता है, तो यह स्वयं के साथ संगत होना चाहिए।

पल.जेएस प्रारूप बहुत सरल दिखता है, इसलिए जब तक आप क्लाइंट को डेटा शिपिंग नहीं करते हैं, तो यह निश्चित रूप से एक विकल्प है। डेटा बदलने के दौरान आपको क्या करना है इसके बारे में सोचना चाहिए:

  • आप इसे सर्वर पर कैसे उठाते हैं?
  • पुराने डेटा का उपयोग करके अस्थायी रूप से क्लाइंट के साथ आप कैसे सामना करते हैं?

यदि सटीक स्थिरता आपके लिए वास्तव में महत्वपूर्ण है, तो आप समय क्षेत्र डेटा संस्करण के साथ समय क्षेत्र डेटा को शिप करना चाहते हैं ... जो क्लाइंट पोस्ट करते समय सर्वर पर वापस पेश कर सकता है डेटा।(मुझे लगता है कि यह ऐसा कर रहा है, निश्चित रूप से।) सर्वर या तो उस संस्करण का उपयोग कर सकता है, या ग्राहक के अनुरोध को अस्वीकार कर सकता है और कह सकता है कि हालिया डेटा है।

यहाँ moment.js में Noda समय क्षेत्र डेटा कन्वर्ट करने के लिए कुछ नमूना कोड - यह मेरे लिए ठीक लग रहा है, लेकिन मैं इसे साथ ज्यादा नहीं किया है। यह momentjs.com में प्रलेखन से मेल खाता है ... ध्यान दें कि ऑफ़सेट को उलट दिया जाना चाहिए क्योंकि moment.js सकारात्मक टाइम ज़ोन के लिए ऑफसेट्स का उपयोग करने का निर्णय लेता है जो यूटीसी के पीछे किसी कारण से है।

using System; 
using System.Linq; 

using NodaTime; 
using Newtonsoft.Json; 

class Test 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine(GenerateMomentJsZoneData("Europe/London", 2010, 2020)); 
    } 

    static string GenerateMomentJsZoneData(string tzdbId, int fromYear, int toYear) 
    { 
     var intervals = DateTimeZoneProviders 
      .Tzdb[tzdbId] 
      .GetZoneIntervals(Instant.FromUtc(fromYear, 1, 1, 0, 0), 
           Instant.FromUtc(toYear + 1, 1, 1, 0, 0)) 
      .ToList(); 

     var abbrs = intervals.Select(interval => interval.Name); 
     var untils = intervals.Select(interval => interval.End.Ticks/NodaConstants.TicksPerMillisecond); 
     var offsets = intervals.Select(interval => -interval.WallOffset.Ticks/NodaConstants.TicksPerMinute); 
     var result = new { name = tzdbId, abbrs, untils, offsets }; 
     return JsonConvert.SerializeObject(result); 
    } 
} 
+0

धन्यवाद जॉन, मैं अब NodaTime और अपने कोड का उपयोग कर रहा untils/ऑफसेट की गणना करने में। हालांकि .NET/जावास्क्रिप्ट परीक्षण चलाते समय आपका कोड विफल रहता है। https://github.com/ScreenshotMonitor/WindowsTimeZoneToMomentJs/blob/noda/src/Pranas.WindowsTimeZoneToMomentJs/TimeZoneToMomentConverter.cs – Evgenyt

+0

@Evgenyt: परीक्षण परीक्षण क्या हैं, वास्तव में, और विफलता क्या है? (और नोडा टाइम का कौन सा संस्करण आप उपयोग कर रहे हैं, और .NET के बहुत सारे संस्करण बदल रहे हैं ...) –

+0

नोडाटाइम 1.3.1 और .NET 4.5, बस दिनों के माध्यम से लूप का परीक्षण करता है, और पलज का उपयोग करके कनवर्टन के परिणामों की तुलना करता है और TimeZoneInfo। https://github.com/ScreenshotMonitor/WindowsTimeZoneToMomentJs/blob/noda/src/Pranas.WindowsTimeZoneToMomentJs.Test/TimeZoneToMomentConverterTest.cs – Evgenyt

4

अद्यतन

जॉन सुझाव तुम दोनों momentjs और .NET में NodaTime बीसीएल या IANA डेटा का उपयोग करना होगा। अन्यथा आप विसंगतियां प्राप्त करेंगे। मुझे इसके साथ सहमत होना चाहिए।

आप टाइमज़ोनइन्फो का उपयोग कर 100% विश्वसनीय रूप से .NET 4.5 में कनवर्ट नहीं कर सकते हैं। यहां तक ​​कि यदि आप इसे NodaTime का उपयोग करके सूचित करते हैं, या नीचे दिए गए TimeZoneToMomentConverter का उपयोग करते हैं।


मूल जवाब

IANA और Windows समय क्षेत्र डेटा अद्यतन समय के साथ और विभिन्न विवरण के स्तर को की है।

तो तुम नेट और moment.js में बिल्कुल वैसा ही रूपांतरण चाहते हैं - आप (सुझाव के रूप में मैट NodaTime के साथ) हर जगह

  • उपयोग IANA के लिए या तो है,
  • उपयोग विंडोज समय क्षेत्र हर जगह (परिवर्तित TimeZoneInfo moment.js प्रारूप के नियम)।

हम दूसरे तरीके से गए, और कनवर्टर लागू किया।

यह थ्रेड-सुरक्षित कैश को और अधिक कुशल बनाने के लिए जोड़ता है, क्योंकि यह मूल रूप से तिथियों के माध्यम से loops (TimeZoneInfo नियमों को स्वयं परिवर्तित करने की कोशिश करने के बजाय)। हमारे परीक्षणों में यह वर्तमान विंडोज टाइमज़ोन को 100% सटीकता के साथ परिवर्तित करता है (GitHub पर परीक्षण देखें)। GitHub पर कोड के लिए

PM> Install-Package Pranas.WindowsTimeZoneToMomentJs 

और ब्राउज़र स्रोतों और परीक्षण:

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Web.Script.Serialization; 

namespace Pranas.WindowsTimeZoneToMomentJs 
{ 
    /// <summary> 
    /// Tool to generates JavaScript that adds MomentJs timezone into moment.tz store. 
    /// As per http://momentjs.com/timezone/docs/ 
    /// </summary> 
    public static class TimeZoneToMomentConverter 
    { 
     private static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); 
     private static readonly JavaScriptSerializer Serializer = new JavaScriptSerializer(); 
     private static readonly ConcurrentDictionary<Tuple<string, int, int, string>, string> Cache = new ConcurrentDictionary<Tuple<string, int, int, string>, string>(); 

     /// <summary> 
     /// Generates JavaScript that adds MomentJs timezone into moment.tz store. 
     /// It caches the result by TimeZoneInfo.Id 
     /// </summary> 
     /// <param name="tz">TimeZone</param> 
     /// <param name="yearFrom">Minimum year</param> 
     /// <param name="yearTo">Maximum year (inclusive)</param> 
     /// <param name="overrideName">Name of the generated MomentJs Zone; TimeZoneInfo.Id by default</param> 
     /// <returns>JavaScript</returns> 
     public static string GenerateAddMomentZoneScript(TimeZoneInfo tz, int yearFrom, int yearTo, string overrideName = null) 
     { 
      var key = new Tuple<string, int, int, string>(tz.Id, yearFrom, yearTo, overrideName); 

      return Cache.GetOrAdd(key, x => 
      { 
       var untils = EnumerateUntils(tz, yearFrom, yearTo).ToArray(); 

       return string.Format(
@"(function(){{ 
    var z = new moment.tz.Zone(); 
    z.name = {0}; 
    z.abbrs = {1}; 
    z.untils = {2}; 
    z.offsets = {3}; 
    moment.tz._zones[z.name.toLowerCase().replace(/\//g, '_')] = z; 
}})();", 
        Serializer.Serialize(overrideName ?? tz.Id), 
        Serializer.Serialize(untils.Select(u => "-")), 
        Serializer.Serialize(untils.Select(u => u.Item1)), 
        Serializer.Serialize(untils.Select(u => u.Item2))); 
      }); 
     } 

     private static IEnumerable<Tuple<long, int>> EnumerateUntils(TimeZoneInfo timeZone, int yearFrom, int yearTo) 
     { 
      // return until-offset pairs 
      int maxStep = (int)TimeSpan.FromDays(7).TotalMinutes; 
      Func<DateTimeOffset, int> offset = t => (int)TimeZoneInfo.ConvertTime(t, timeZone).Offset.TotalMinutes; 

      var t1 = new DateTimeOffset(yearFrom, 1, 1, 0, 0, 0, TimeSpan.Zero); 

      while (t1.Year <= yearTo) 
      { 
       int step = maxStep; 

       var t2 = t1.AddMinutes(step); 
       while (offset(t1) != offset(t2) && step > 1) 
       { 
        step = step/2; 
        t2 = t1.AddMinutes(step); 
       } 

       if (step == 1 && offset(t1) != offset(t2)) 
       { 
        yield return new Tuple<long, int>((long)(t2 - UnixEpoch).TotalMilliseconds, -offset(t1)); 
       } 
       t1 = t2; 
      } 

      yield return new Tuple<long, int>((long)(t1 - UnixEpoch).TotalMilliseconds, -offset(t1)); 
     } 
    } 
} 

तुम भी NuGet के माध्यम से इसे प्राप्त कर सकते हैं:

यह उपकरण का कोड है।

+0

मेरा मानना ​​है कि यह कुछ छोटे कोने मामलों में जहां 'TimeZoneInfo' बस कुछ ही घंटों के लिए ऑफसेट स्विच करने का फैसला करता में असफल हो जायेगी - यह आसानी से उन त्वरित बदलाव आगे और पीछे छूट जाते हैं। अधिक जानकारी के और एक छोटे से उपकरण आपको उन मामलों खोजने में मदद मिलेगी, जिसके लिए http://codeblog.jonskeet.uk/2014/09/30/the-mysteries-of-bcl-time-zone-data/ देखें। –

संबंधित मुद्दे