时区代码转换为时区信息

3
在我们的MS Dynamics CRM项目中,我们创建了一个批量上传用户的批处理程序。
该批处理程序从Excel文件中读取数据,并进行用户的批量上传。
其中一个需要设置的是timezonecode。
在Excel文件中,时区将被写成例如“UTC + 1”的形式。
CRM使用的代码似乎是SQL Server使用的timezonecode,可以在此处找到。
最清晰的映射方式是什么?
目前我的想法有:
  • 硬编码转换存储
  • 以某种方式从CRM获取代码
  • 以某种方式从SQL获取代码
目前,我们只是实现了自己的转换类,并使用了硬编码的值。
是否有更好的方法?我们能否利用.NET TimezoneInfo类?
更新
为了获取所有CRM时区,我们执行了以下操作:
var colSet = new ColumnSet(true);
var query = new QueryExpression(TimeZoneDefinition.EntityLogicalName) { ColumnSet = colSet};
var timeZoneDefs = service.RetrieveMultiple(query).Entities.Select(tz => tz.ToEntity<TimeZoneDefinition>());

但似乎只有Id、Code、StandardName和UserInterfaceName这些属性被填充了。

看起来只有UI名称包含我们正在寻找的偏移量。

有没有办法确保加载Bias属性?


在我研究您的问题时,我觉得CRM拥有自己的时区实现非常奇怪。我想知道为什么他们一开始没有使用TimeZoneInfo?您对此有何想法? - Matt Johnson-Pint
1
@MattJohnson,我非常确定原因在历史上的某个地方。一些基于.NET 1的架构决策已经做出。其中一些已被更改,但有些则基本相同。顺便说一句,希望能在那里看到NodaTime ;) - ccellar
Daryl的代码非常好,但您需要确认CRM的“standardname”字段确实与“TimeZoneInfo.Id”对齐。我没有列表进行比较,但请检查亚利桑那州。如果“standardname”中写着“Arizona”,那么就不行。如果它说“美国山区标准时间”,那么您应该可以继续。其中必须有“US”,它必须与HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones的注册表键匹配。 - Matt Johnson-Pint
Noda Time 在 CRM 中?哈哈,我怀疑微软会同意! :) 但是我正在努力使其通过 SQL Server(通过 SQLCLR)可用。 - Matt Johnson-Pint
1
顺便提一下 - SQL Server 没有时区的概念。你提供的链接是针对 SQL Server "Notification Services" 的,这在 SQL 2005 中已经停用了。然而,我将其与 CRM 4.0 的此列表进行了比较,发现它们非常相似。我猜想,CRM 曾经使用过 SQL Notification Services,所以他们让它们保持一致。我找不到类似的 CRM 2011 列表,所以我不知道它们是否有所改变。 - Matt Johnson-Pint
@MattJohnson CRM 2011需要SQL Server 2008 R2或更高版本,这可能是您找不到列表的原因。此外,从我的编辑中,您可以看到亚利桑那州时区正确显示为“美国山区标准时间”。 - Daryl
3个回答

10

这里是我用来获取用户时区信息的代码。它根据名称从CRM检索TimeZoneDefinition,但我认为您可以通过与Excel文件中的UTC偏移匹配的Bias进行查找。

public static TimeZoneInfo GetUserTimeZone(IOrganizationService service, Guid userId)
{
    int timeZoneCode = 35; //default timezone to eastern just incase one doesnt exists for user
    var userSettings = service.Retrieve(UserSettings.EntityLogicalName, userId, new ColumnSet("timezonecode")).ToEntity<UserSettings>();

    if ((userSettings != null) && (userSettings.TimeZoneCode != null))
    {
        timeZoneCode = userSettings.TimeZoneCode.Value;
    }

    return GetTimeZone(service, timeZoneCode);
}

public static TimeZoneInfo GetTimeZone(IOrganizationService service, int crmTimeZoneCode)
{
    var qe = new QueryExpression(TimeZoneDefinition.EntityLogicalName);
    qe.ColumnSet = new ColumnSet("standardname");
    qe.Criteria.AddCondition("timezonecode", ConditionOperator.Equal, crmTimeZoneCode);
    return TimeZoneInfo.FindSystemTimeZoneById(service.RetrieveMultiple(qe).Entities.First().ToEntity<TimeZoneDefinition>().StandardName);
}

编辑 - 偏差为null

这可能只是我们的本地版CRM,但目前在CRM中为时区为-5、-6、-7或-8的任何时间填充了这些内容。 CRM TimeZoneDefinition

这将使偏差查找无效。

顺便说一句,我们99%的用户都在东部时间,但我们有一些位于加利福尼亚州的用户,我还没有听说过任何问题。但现在我想知道我们是否在夏令时之前和之后进行了测试...


1
有趣的是你在 CRM 中查询,但是似乎将 CRM 中的“标准名称”属性与时区的“ID”属性进行匹配。 你确定它们会匹配吗?在 TimeZoneInfo 中,StandardNameDaylightName 是特定于操作系统的语言(而不是线程文化或 UI 文化)。 只有 Id 是安全的匹配方式。此外,按 UTC 偏移量进行匹配并不是一个好建议,因为有许多共享相同偏移量的时区。 - Matt Johnson-Pint
实际上,我们意识到偏差也不够优秀。由于我们的工具只需要五个可能的区域,我们选择硬编码这些值。但是为了将来的参考,我们仍然想要跟进我们最初的问题。 - Boris Callens
1
感谢您的验证。因此,结论是:标准名称是唯一可行且应该可靠的方式。 - Boris Callens

4
如果您需要一个包含所有CRM时区及其相应SQL标识符的列表:我们编写了一个小程序来循环遍历CRM中的所有选项并输出名称、分号和SQL代码。
(GMT+10:00) Canberra, Melbourne, Sydney (Commonwealth Games 2006); 256 
(GMT-04:00) Georgetown, La Paz, Manaus, San Juan; 55 
(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London; 85 
(GMT+06:00) Ekaterinburg; 180 
(GMT+03:00) Baghdad; 158 
(GMT+06:00) Dhaka; 196 
(GMT-06:00) Guadalajara, Mexico City, Monterrey; 29 
(GMT+04:00) Abu Dhabi, Muscat; 165 
(GMT+02:00) Beirut; 131 
(GMT) Coordinated Universal Time; 92 
(GMT+10:00) Canberra, Melbourne, Sydney; 255 
(GMT+10:00) Brisbane; 260 
(GMT-01:00) Cape Verde Is.; 83 
(GMT+01:00) Brussels, Copenhagen, Madrid, Paris; 105 
(GMT+12:00) Auckland, Wellington; 290 
(GMT+2:00) Nicosia; 115 
(GMT+02:00) Athens, Bucharest; 130 
(GMT+13:00) Nukualofa; 300 
(GMT+10:00) Hobart; 265 
(GMT+04:00) Port Louis; 172 
(GMT+07:00) Novosibirsk; 201 
(GMT-03:30) Newfoundland; 60 
(GMT+03:30) Tehran; 160 
(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi; 210 
(GMT+02:00) Amman; 129 
(GMT-06:00) Central America; 33 
(GMT+08:00) Krasnoyarsk; 207 
(GMT-11:00) Coordinated Universal Time-11; 6 
(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb; 100 
(GMT-05:00) Bogota, Lima, Quito; 45 
(GMT+10:00) Yakutsk; 240 
(GMT+04:00) Yerevan; 170 
(GMT+09:00) Osaka, Sapporo, Tokyo; 235 
(GMT+10:00) Guam, Port Moresby; 275 
(GMT+12:00) Fiji; 285 
(GMT+04:30) Kabul; 175 
(GMT-05:00) Eastern Time (US & Canada); 35 
(GMT+11:00) Vladivostok; 270 
(GMT-01:00) Azores; 80 
(GMT+02:00) Jerusalem; 135 
(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague; 95 
(GMT+07:00) Bangkok, Hanoi, Jakarta; 205 
(GMT-03:00) Buenos Aires; 69 
(GMT+09:00) Irkutsk; 227 
(GMT+12:00) Magadan; 281 
(GMT-03:00) Cayenne, Fortaleza; 70 
(GMT-07:00) Chihuahua, La Paz, Mazatlan; 12 
(GMT+02:00) Cairo; 120 
(GMT-08:00) Baja California; 5 
(GMT+12:00) Petropavlovsk-Kamchatsky - Old; 295 
(GMT+05:00) Tashkent; 185 
(GMT+03:00) Nairobi; 155 
(GMT-04:00) Atlantic Time (Canada); 50 
(GMT-12:00) International Date Line West; 0 
(GMT+03:00) Kaliningrad, Minsk; 159 
(GMT-02:00) Coordinated Universal Time-02; 76 
(GMT+02:00) Istanbul; 134 
(GMT+08:00) Perth; 225 
(GMT+13:00) Samoa; 1 
(GMT) Casablanca; 84 
(GMT+06:00) Astana; 195 
(GMT+09:30) Darwin; 245 
(GMT-04:30) Caracas; 47 
(GMT-10:00) Hawaii; 2 
(GMT+09:00) Seoul; 230 
(GMT+08:00) Kuala Lumpur, Singapore; 215 
(GMT-06:00) Guadalajara, Mexico City, Monterrey - Old; 30 
(GMT+06:30) Yangon (Rangoon); 203 
(GMT+04:00) Moscow, St. Petersburg, Volgograd; 145 
(GMT+02:00) Damascus; 133 
(GMT-07:00) Arizona; 15 
(GMT+04:00) Tbilisi; 173 
(GMT-09:00) Alaska; 3 
(GMT-03:00) Brasilia; 65 
(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna; 110 
(GMT-07:00) Chihuahua, La Paz, Mazatlan - Old; 13 
(GMT+08:00) Ulaanbaatar; 228 
(GMT-03:00) Greenland; 73 
(GMT+09:30) Adelaide; 250 
(GMT-02:00) Mid-Atlantic; 75 
(GMT+12:00) Coordinated Universal Time+12; 284 
(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius; 125 
(GMT+02:00) Harare, Pretoria; 140 
(GMT-05:00) Indiana (East); 40 
(GMT+01:00) West Central Africa; 113 
(GMT-08:00) Pacific Time (US & Canada); 4 
(GMT+05:00) Islamabad, Karachi; 184 
(GMT+11:00) Solomon Is., New Caledonia; 280 
(GMT+03:00) Kuwait, Riyadh; 150 
(GMT+08:00) Taipei; 220 
(GMT-03:00) Montevideo; 74 
(GMT+09:30) Adelaide (Commonwealth Games 2006); 251 
(GMT+10:00) Hobart (Commonwealth Games 2006); 266 
(GMT+05:30) Sri Jayawardenepura; 200 
(GMT+04:00) Baku; 169 
(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi; 190 
(GMT-06:00) Central Time (US & Canada); 20 
(GMT-07:00) Mountain Time (US & Canada); 10 
(GMT-06:00) Saskatchewan; 25 
(GMT-04:00) Cuiaba; 58 
(GMT-04:00) Asuncion; 59 
(GMT+01:00) Windhoek; 141 
(GMT-04:00) Santiago; 56 
(GMT+05:45) Kathmandu; 193 
Bahia Standard Time; 71 
(GMT) Monrovia, Reykjavik; 90 

0

时区:使用Dynamics CRM Web API:

编辑:Dynamics CRM使用Windows时区名称。Javascript几乎肯定会使用IANA。添加一个Windows->IANA映射库(例如windows-iana)可能会使此输出更有用。

//Get available timezone codes, as array
const { findOneIana } = require("windows-iana");

await axios.get(`${crmAPIUrl}timezonedefinitions?$select=timezonecode,standardname,userinterfacename,bias`)
.then(r => r.value)
.then(arr => arr.map(i => (
  {
    timezonecode: i.timezonecode,
    userinterfacename: i.userinterfacename,
    standardname: i.standardname,
    ianaTZ: findOneIana(i.standardname)
)))

这将返回动态 CRM 所看到的时区数组,包括匹配的 IANA 值。 ianaTZ 值可在您的 JS 代码中余下使用。

[
  ...
  {
    timezonecode: 145,
    userinterfacename: '(GMT+03:00) Moscow, St. Petersburg',
    standardname: 'Russian Standard Time',
    ianaTZ: 'Europe/Moscow'
  },
  {
    timezonecode: 133,
    userinterfacename: '(GMT+02:00) Damascus',
    standardname: 'Syria Standard Time',
    ianaTZ: 'Asia/Damascus'
  },
  ...
]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接