CẬP NHẬT
Jon gợi ý rằng bạn phải sử dụng dữ liệu NodaTime BCL hoặc IANA trong cả momentjs và .NET. Nếu không, bạn sẽ nhận được sự khác biệt. Tôi nên đồng ý với điều này.
Bạn không thể 100% đáng tin cậy chuyển đổi thời gian trong .NET 4.5 bằng cách sử dụng TimeZoneInfo. Ngay cả khi bạn chuyển đổi nó bằng cách sử dụng NodaTime
như được đề xuất hoặc TimeZoneToMomentConverter
như bên dưới.
ORIGINAL ĐÁP
IANA và Windows múi giờ cập nhật dữ liệu theo thời gian và có granularity khác nhau.
Vì vậy, nếu bạn muốn chính xác cùng một chuyển đổi trong moment.js NET và - bạn có một trong hai để
- sử dụng IANA ở khắp mọi nơi (với NodaTime như đề nghị Matt),
- sử dụng Windows múi giờ ở khắp mọi nơi (chuyển đổi TimeZoneInfo quy tắc định dạng moment.js).
Chúng tôi đã đi theo cách thứ hai và triển khai trình chuyển đổi.
Nó bổ sung bộ nhớ cache an toàn cho chủ đề hiệu quả hơn vì nó cơ bản lặp qua các ngày (thay vì cố gắng tự chuyển đổi các quy tắc TimeZoneInfo
). Trong các thử nghiệm của chúng tôi, nó chuyển đổi múi giờ Windows hiện tại với độ chính xác 100% (xem các bài kiểm tra trên GitHub).
Đây là mã của công cụ này:
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));
}
}
}
Bạn cũng có thể nhận được nó qua NuGet:
PM> Install-Package Pranas.WindowsTimeZoneToMomentJs
Và trình duyệt nguồn cho mã và thử nghiệm trên GitHub.
múi giờ sử dụng múi giờ chuẩn IANA. Nếu bạn tìm thấy một múi giờ cụ thể không ánh xạ chính xác bằng cách sử dụng hàm 'WindowsToIana' trong câu trả lời được liên kết, hãy cho tôi biết cái nào. Tất cả các 'TimeZoneInfo' id nên được mappable. –
Matt, điều này không hoàn toàn đúng. Trong khi moment.js sử dụng định dạng IANA, múi giờ Windows không thể được ánh xạ 100% theo múi giờ IANA. Sẽ có nhiều sự khác biệt. Để giải quyết vấn đề hoàn toàn, chúng ta có thể chuyển đổi các quy tắc TimeZoneInfo thành đối tượng dữ liệu vùng moment.js. Vì vậy, sẽ có bản đồ Windows-> Moment.js 100%. – Evgenyt
@Evgenyt - Nó phụ thuộc vào mức độ chi tiết và lịch sử mong muốn. Từ quan điểm của người dùng chọn múi giờ hiện tại của họ và làm việc với dữ liệu trong thời gian hiện đại, sau đó [ánh xạ CLDR] (http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml) Unicode duy trì là chính xác hợp lý, và tất cả các vùng đều có thể được biến đổi theo hướng cửa sổ-> iana. Bạn thậm chí có thể có độ chính xác tốt hơn nếu bạn sử dụng mã quốc gia, thay vì chọn lãnh thổ "chính" (001). Chỉ của nó theo hướng iana-> cửa sổ có những vùng không thể điều chỉnh được. –