【问题标题】:Convert a posix style timezone to timezoneinfo in c# .net在 c# .net 中将 posix 样式的时区转换为 timezoneinfo
【发布时间】:2014-12-25 03:37:58
【问题描述】:

我从另一台机器获取时区信息,格式为:

"CET-1CEST,M3.5.0/2,M10.5.0/3"

(Posix 风格的时区)

我需要对其进行解析并将其转换为c# .net TimeZoneInfo class

有没有办法做到这一点?

【问题讨论】:

    标签: c# .net timezone posix


    【解决方案1】:

    根据这篇文章:http://www.ibm.com/developerworks/aix/library/au-aix-posix/ POSIX time like "CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00" 具有以下规格:

    • CST6CDT 是时区的名称
    • CST 是夏令时关闭时使用的缩写
    • 6 小时是与 GMT 的时差
    • CDT 是 DST 开启时使用的缩写
    • ,M3是第三个月
    • .2 是该月第二次出现的日期
    • .0 是星期日
    • /2:00:00 是时间
    • ,M11 是第十一个月
    • .1 是该月中第一次出现的日期
    • .0 是星期日
    • /2:00:00 是时间

    日期为 Mm.n.d 格式,其中:

    • mm (1-12) 12个月
    • n (1-5) 1 表示该月的第一周,5 表示该月的最后一周
    • d (0-6) 0 表示周日,6 表示周六

    嗯,根据这些信息和 TimeZoneInfo 类中可以找到的调整规则,您可以使用以下代码进行转换:

    public static TimeZoneInfo ConvertPosixToTimeZoneInfo(string posix)
        {
            string[] tokens = posix.Split(',');
            tokens[0] = tokens[0].Replace("/", ".");
            var match = Regex.Match(tokens[0], @"[-+]?[0-9]*\.?[0-9]+").Value;
            var ticks = (long)(decimal.Parse(match, CultureInfo.InvariantCulture) * 60) * 600000000;
            var baseOffset = new TimeSpan(ticks);
    
            var systemTimeZones = TimeZoneInfo.GetSystemTimeZones().Where(t => t.BaseUtcOffset == baseOffset).ToList();
    
            var startRuleTokens = tokens[1].TrimStart('M').Split('/');
            var startDateRuleTokens = startRuleTokens[0].Split('.');
            var startTimeRuleTokens = startRuleTokens[1].Split(':');
    
            var endRuleTokens = tokens[2].TrimStart('M').Split('/');
            var endDateRuleTokens = endRuleTokens[0].Split('.');
            var endTimeRuleTokens = endRuleTokens[1].Split(':');
    
            int? targetIndex = null;
            for (int i = 0; i < systemTimeZones.Count; i++)
            {
                var adjustmentRules = systemTimeZones[i].GetAdjustmentRules();
                foreach (var rule in adjustmentRules)
                {
                    if (rule.DaylightTransitionStart.Month == int.Parse(startDateRuleTokens[0]) &&
                        rule.DaylightTransitionStart.Week == int.Parse(startDateRuleTokens[1]) &&
                        rule.DaylightTransitionStart.DayOfWeek == (DayOfWeek)int.Parse(startDateRuleTokens[2]) &&
                        rule.DaylightTransitionStart.TimeOfDay.Hour == int.Parse(startTimeRuleTokens[0]) &&
                        rule.DaylightTransitionStart.TimeOfDay.Minute == int.Parse(startTimeRuleTokens[1]) &&
                        rule.DaylightTransitionStart.TimeOfDay.Second == int.Parse(startTimeRuleTokens[2]) &&
    
                        rule.DaylightTransitionEnd.Month == int.Parse(endDateRuleTokens[0]) &&
                        rule.DaylightTransitionEnd.Week == int.Parse(endDateRuleTokens[1]) &&
                        rule.DaylightTransitionEnd.DayOfWeek == (DayOfWeek)int.Parse(endDateRuleTokens[2]) &&
                        rule.DaylightTransitionEnd.TimeOfDay.Hour == int.Parse(endTimeRuleTokens[0]) &&
                        rule.DaylightTransitionEnd.TimeOfDay.Minute == int.Parse(endTimeRuleTokens[1]) &&
                        rule.DaylightTransitionEnd.TimeOfDay.Second == int.Parse(endTimeRuleTokens[2]))
                    {
                        targetIndex = i;
                        break;
                    }
                }
            }
    
            if (targetIndex.HasValue)
                return systemTimeZones[targetIndex.Value];
            return null;
        }
    

    【讨论】:

      【解决方案2】:

      我会根据它的格式来解析它:http://www.ibm.com/developerworks/aix/library/au-aix-posix/

      也许你也可以考虑:http://nodatime.org/ - 我目前不知道他们是否支持这个。

      【讨论】:

      • 一旦您解析了 POSIX 以获取 standard.Offsetdaylight.OffsettoDaylight.DatetoStandard.Date,那么您需要遍历可用的 TimeZoneInfo 以找到匹配项。如果没有匹配,则忽略两个日期再次迭代。如果您有多个匹配项,那么您将需要选择一个。如果没有匹配到,那么它可能是一个常识字符串,而不是 POSIX 字符串。 :)
      【解决方案3】:

      下面的代码应该可以解决问题。

      请记住,虽然这将为您提供有效的TimeZoneInfo 对象,但它确实不会将信息映射到现有的 Windows 时区。您可以使用TimeZoneInfo.ConvertTime 等各种转换函数,但不要指望它会神奇地知道PST8PDT 应该与"Pacific Standard Time" 对齐。

      public static TimeZoneInfo PosixToTzi(string posixTz)
      {
          var parts = posixTz.Split(',');
          var zoneparts = Regex.Split(parts[0], @"([0-9\+\-\.]+)");
      
          double baseOffsetHours;
          if (zoneparts.Length > 1)
          {
              if (!Double.TryParse(zoneparts[1], out baseOffsetHours))
                  throw new FormatException();
          }
          else
          {
              // recognize RFC822 time zone abbreviations
              switch (zoneparts[0].ToUpper())
              {
                  case "UT":
                  case "UTC":
                  case "GMT":
                      baseOffsetHours = 0;
                      break;
                  case "EDT":
                      baseOffsetHours = 4;
                      break;
                  case "EST":
                  case "CDT":
                      baseOffsetHours = 5;
                      break;
                  case "CST":
                  case "MDT":
                      baseOffsetHours = 6;
                      break;
                  case "MST":
                  case "PDT":
                      baseOffsetHours = 7;
                      break;
                  case "PST":
                      baseOffsetHours = 8;
                      break;
                  default:
                      throw new FormatException();
              }
          }
      
          double dstOffsetHours = baseOffsetHours - 1;
          if (zoneparts.Length == 4)
          {
              if (!Double.TryParse(zoneparts[3], out dstOffsetHours))
                  throw new FormatException();
          }
      
          var baseOffset = TimeSpan.FromHours(-baseOffsetHours);
          var dstDelta = TimeSpan.FromHours(baseOffsetHours - dstOffsetHours);
      
          var rules = new List<TimeZoneInfo.AdjustmentRule>();
          if (parts.Length == 3)
          {
              var rule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(
                  DateTime.MinValue.Date, DateTime.MaxValue.Date, dstDelta,
                  ParsePosixTransition(parts[1]), ParsePosixTransition(parts[2]));
      
              rules.Add(rule);
          }
      
          return TimeZoneInfo.CreateCustomTimeZone(posixTz, baseOffset, parts[0], zoneparts[0],
              zoneparts[zoneparts.Length == 3 ? 2 : 0], rules.ToArray());
      }
      
      private static TimeZoneInfo.TransitionTime ParsePosixTransition(string transition)
      {
          var parts = transition.Split('/');
          if (parts.Length > 2)
                  throw new FormatException();
      
          double hours = 0;
          if (parts.Length == 2)
          {
              if (!Double.TryParse(parts[1], out hours))
                  throw new FormatException();
          }
          var time = DateTime.MinValue.AddHours(hours);
      
          if (transition.StartsWith("M", StringComparison.OrdinalIgnoreCase))
          {
              var dateParts = parts[0].Substring(1).Split('.');
              if (dateParts.Length > 3)
                  throw new FormatException();
      
              int month;
              if (!Int32.TryParse(dateParts[0], out month))
                  throw new FormatException();
      
              int week;
              if (!Int32.TryParse(dateParts[1], out week))
                  throw new FormatException();
      
              int dow;
              if (!Int32.TryParse(dateParts[2], out dow))
                  throw new FormatException();
      
              return TimeZoneInfo.TransitionTime.CreateFloatingDateRule(time, month, week, (DayOfWeek) dow);
          }
      
          if (transition.StartsWith("J", StringComparison.OrdinalIgnoreCase))
          {
              int dayNum;
              if (!Int32.TryParse(parts[0].Substring(1), out dayNum))
                  throw new FormatException();
              var date = DateTime.MinValue.AddDays(dayNum);
      
              return TimeZoneInfo.TransitionTime.CreateFixedDateRule(time, date.Month, date.Day);
          }
      
          throw new FormatException();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 2019-07-12
        • 1970-01-01
        相关资源
        最近更新 更多