在c#.net中,将posix风格的时区转换为时区信息



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

"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/22:00:00,M11.1.0/22:0:00",具有以下规范:

  • CST6CDT是时区的名称
  • CST是DST关闭时使用的缩写
  • 与格林尼治标准时间相差6小时
  • CDT是DST开启时使用的缩写
  • ,M3为第三个月
  • .2是该月中第二次出现的日期
  • 0是星期日
  • /时间是2:00
  • ,M11是第十一个月
  • .1是该月中第一次出现的日期
  • 0是星期日
  • /时间是2: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;
    }

我会根据它的格式来解析它: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<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();
}

相关内容

  • 没有找到相关文章

最新更新