将posix样式时区转换为c#.net中的timezoneinfo

我从以下格式获取另一台机器的时区信息:

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

(Posix风格时区)

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

有没有办法实现这个目标?

根据这篇文章: http : //www.ibm.com/developerworks/aix/library/au-aix-posix/一个POSIX时间,如“CST6CDT,M3.2.0 / 2:00:00,M11.1.0 / 2: 00:00“有以下规格:

  • CST6CDT是时区的名称
  • CST是DST关闭时使用的缩写
  • 距格林威治标准时间6小时
  • CDT是DST打开时使用的缩写
  • ,M3是第三个月
  • .2是该月中第二次出现的日子
  • .0是星期天
  • / 2:00:00是时候
  • ,M11是第十一个月
  • .1是该月当天的第一次出现
  • .0是星期天
  • / 2:00:00是时候

并且日期是Mm.nd格式,其中:

  • 嗯(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; } 

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

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

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

请记住,虽然这将为您提供有效的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(); 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(); }