DateTime 转换为 Unix Epoch 添加 Phantom Hour
DateTime Conversion to Unix Epoch Adding Phantom Hour
我有以下转换方法来转换 Unix Epoch 时间戳
public static class DateTimeHelpers
{
public static DateTime UnixEpoch()
{
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
public static DateTime FromMillisecondsSinceUnixEpoch(long milliseconds)
{
return UnixEpoch().AddMilliseconds(milliseconds).ToLocalTime();
}
public static long ToMillisecondsSinceUnixEpoch(DateTime dateTime)
{
return (long)(dateTime - UnixEpoch()).TotalMilliseconds;
}
}
问题是(男孩,这看起来像是基本的东西),我设置了一个 DateTime
我想要然后尝试转换为 Unix-Time 但返回的毫秒时间戳是 +01:00 小时,我想要知道为什么吗?
我使用的代码是
DateTime startDate = new DateTime(2015, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc);
long startMillis = DateTimeHelpers.ToMillisecondsSinceUnixEpoch(startDate);
这给出 startMillis = 1443657600000
即 "Thursday October 01, 2015 01:00:00 (am) in time zone Europe/London (BST)"。我想要从 ToMillisecondsSinceUnixEpoch
返回的时间戳,即“2015/10/01 00:00:00”,我在这里缺少什么?
感谢您的宝贵时间。
编辑。我想做一些 Java 代码的等价物。这产生了 right 时间戳。为什么我可以在 Java 而不是 C# 中执行此操作?不管怎样代码
private static long ukTimeStringToUtcMillis(String s) {
SimpleDateFormat sdf = makeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
try {
return sdf.parse(s).getTime();
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
private static SimpleDateFormat makeSimpleDateFormat(String s) {
SimpleDateFormat sdf = new SimpleDateFormat(s);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
return sdf;
}
我是这样用的
long timestamp = ukTimeStringToUtcMillis("2015-10-01T00:00:00.000");
这给出 timestamp = 1443654000000
即 "Thursday October 01, 2015 00:00:00 (am) in time zone Europe/London (BST)"。我在 C# 中缺少什么?我试过了
var ukTimeZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime unixEpoch = TimeZoneInfo.ConvertTime(
new DateTime(1970, 1, 1, 0, 0, 0), ukTimeZone, ukTimeZone);
long startMillis = (long)(startDate - unixEpoch).TotalMilliseconds;
long endMillis = (long)(endDate - unixEpoch).TotalMilliseconds;
这增加了一个小时!?
如果我跟着你做的,你的测试代码以 UTC 时间开始:
DateTime startDate = new DateTime(2015, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc);
但是在 FromMillisecondsSinceUnixEpoch
你 return LocalTime。按原样使用代码,它不会进行往返:
Console.WriteLine(dt.ToUniversalTime());
Console.WriteLine("{0} {1}", ToMillisecondsSinceUnixEpoch(dt),
FromMillisecondsSinceUnixEpoch(ToMillisecondsSinceUnixEpoch(dt)));
10/1/2015 5:00:00 AM
1443657600000 10/1/2015 12:00:00 AM
如果我改变FromMillisecondsSinceUnixEpoch
:
public static DateTime FromMillisecondsSinceUnixEpoch(long milliseconds)
{
return UnixEpoch().AddMilliseconds(milliseconds).ToUniversalTime();
}
现在它将进行往返:
10/1/2015 5:00:00 AM
1443675600000 10/1/2015 5:00:00 AM
请注意,每个 MilliSecondsSince 都是相同的。你不能只看那个,因为没有上下文。
我在参考源中找不到它,但 DateTime 肯定足够聪明,可以在 TZ 不同时进行调整,然后再进行减法。 (否则很难解释 1443675600000
如何代表同一日期的 3 个时间跨度(2 个给我,1 个给你)。
几件事:
如果可能,您应该使用 DateTimeOffset
而不是 DateTime
来进行此类操作。 DateTimeOffset
总是一个特定的时刻,而 DateTime
可能 是,也可能不是,这取决于 Kind
以及您坚持的程度各种方法如何解释 Kind
的微妙之处。
如果您使用 DateTimeOffset
,并且您的目标是 .NET 4.6 或更高版本(或 .NET Core),那么您可以使用内置的 DateTimeOffset.FromUnixTimeMilliseconds
and ToUnixTimeMilliseconds
方法,而不是创建自己的。
您可以考虑使用 Noda Time 开源库,因为它为大多数使用日期和时间的应用程序增加了重要价值。
- 例如,如果您想使用 tzdb 时区,例如您提到的
"Europe/London"
时区,那么您可以使用 DateTimeZoneProviders.Tzdb["Europe/London"]
.
现在我的其余回答假设您没有采纳上述任何建议,并且与您在问题中提供的代码有关。
您已将 UnixEpoch
实现为静态方法。由于它的值永远不会改变,它可能应该作为一个静态 属性 来实现,并带有一个私有的只读支持字段。它也可以实现为 public 静态只读字段,尽管大多数人更喜欢通过属性公开这些字段。 (这些只是编码准则,但不会引入任何错误。)
在您的 FromMillisecondsSinceUnixEpoch
方法中,您正在调用 .ToLocalTime()
。那应该被省略。您也不需要调用 .ToUniversalTime()
。只是 return 添加毫秒的结果。 Kind
将是 Utc
。如果您需要使用本地时间,请稍后进行转换 - 不要在此函数内。
识别 ID "GMT Standard Time"
适用于伦敦,而不是 UTC。伦敦是 GMT (UTC+00:00) 或 BST (UTC+01:00),具体取决于相关日期和时间。
识别DateTime.ToLocalTime
和DateTime.ToUniversalTime
在代码为运行的机器上在UTC和当前本地时区之间转换。那可能是伦敦,也可能是其他地方,具体取决于您的用例。如果您 运行 在 服务器 上,例如在 ASP.Net 网络应用程序中,那么依赖系统本地时区并不是一个好习惯。
在您使用 TimeZoneInfo.ConvertTime
显示的代码中,由于您没有将 DateTimeKind.Utc
分配给输入,因此该值将具有 DateTimeKind.Unspecified
。 ConvertTime
会将其解释为已经属于源时区。由于您指定了相同的目的地时区,因此在大多数情况下这将是一个空操作。
在同一个函数中,由于前面指定的原因,根据伦敦时间定义 unixEpoch
是无效的。另请注意,在 1970 年 1 月 1 日,伦敦不在 GMT,但实际上在 BST(当时称为 "British Standard Time",而不是 "British Summer time")。 TZDB 知道这一点,但它对于 Windows 时区和 TimeZoneInfo
来说太落后了。 "GMT Standard Time"
Windows 区域仅反映了 BST/GMT 周围的当前规则,而不是当时有效的规则。
就转换您提供的 Java 代码而言,该函数以毫秒精度读取 ISO 8601 格式的字符串,在伦敦时区对其进行解释,将其转换为 UTC,并给出时间自 Unix 纪元以来的毫秒数。有几种方法可以做到这一点:
.Net 3.5+
public static long UkTimeStringToUtcMillis(string s)
{
string format = "yyyy-MM-dd'T'HH:mm:ss.FFF";
DateTime dt = DateTime.ParseExact(s, format, CultureInfo.InvariantCulture);
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz);
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return (long) (utc - epoch).TotalMilliseconds;
}
.Net 4.6+ / .Net CoreCLR
public static long UkTimeStringToUtcMillis(string s)
{
string format = "yyyy-MM-dd'T'HH:mm:ss.FFF";
DateTime dt = DateTime.ParseExact(s, format, CultureInfo.InvariantCulture);
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
TimeSpan offset = tz.GetUtcOffset(dt);
DateTimeOffset dto = new DateTimeOffset(dt, offset);
return dto.ToUnixTimeMilliseconds();
}
野田时间
public static long UkTimeStringToUtcMillis(string s)
{
LocalDateTimePattern pattern = LocalDateTimePattern.ExtendedIsoPattern;
LocalDateTime dt = pattern.Parse(s).Value;
DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/London"];
Instant i = dt.InZoneLeniently(tz).ToInstant();
return i.Ticks / NodaConstants.TicksPerMillisecond;
}
我有以下转换方法来转换 Unix Epoch 时间戳
public static class DateTimeHelpers
{
public static DateTime UnixEpoch()
{
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
public static DateTime FromMillisecondsSinceUnixEpoch(long milliseconds)
{
return UnixEpoch().AddMilliseconds(milliseconds).ToLocalTime();
}
public static long ToMillisecondsSinceUnixEpoch(DateTime dateTime)
{
return (long)(dateTime - UnixEpoch()).TotalMilliseconds;
}
}
问题是(男孩,这看起来像是基本的东西),我设置了一个 DateTime
我想要然后尝试转换为 Unix-Time 但返回的毫秒时间戳是 +01:00 小时,我想要知道为什么吗?
我使用的代码是
DateTime startDate = new DateTime(2015, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc);
long startMillis = DateTimeHelpers.ToMillisecondsSinceUnixEpoch(startDate);
这给出 startMillis = 1443657600000
即 "Thursday October 01, 2015 01:00:00 (am) in time zone Europe/London (BST)"。我想要从 ToMillisecondsSinceUnixEpoch
返回的时间戳,即“2015/10/01 00:00:00”,我在这里缺少什么?
感谢您的宝贵时间。
编辑。我想做一些 Java 代码的等价物。这产生了 right 时间戳。为什么我可以在 Java 而不是 C# 中执行此操作?不管怎样代码
private static long ukTimeStringToUtcMillis(String s) {
SimpleDateFormat sdf = makeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
try {
return sdf.parse(s).getTime();
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
private static SimpleDateFormat makeSimpleDateFormat(String s) {
SimpleDateFormat sdf = new SimpleDateFormat(s);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/London"));
return sdf;
}
我是这样用的
long timestamp = ukTimeStringToUtcMillis("2015-10-01T00:00:00.000");
这给出 timestamp = 1443654000000
即 "Thursday October 01, 2015 00:00:00 (am) in time zone Europe/London (BST)"。我在 C# 中缺少什么?我试过了
var ukTimeZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime unixEpoch = TimeZoneInfo.ConvertTime(
new DateTime(1970, 1, 1, 0, 0, 0), ukTimeZone, ukTimeZone);
long startMillis = (long)(startDate - unixEpoch).TotalMilliseconds;
long endMillis = (long)(endDate - unixEpoch).TotalMilliseconds;
这增加了一个小时!?
如果我跟着你做的,你的测试代码以 UTC 时间开始:
DateTime startDate = new DateTime(2015, 10, 1, 0, 0, 0, 0, DateTimeKind.Utc);
但是在 FromMillisecondsSinceUnixEpoch
你 return LocalTime。按原样使用代码,它不会进行往返:
Console.WriteLine(dt.ToUniversalTime());
Console.WriteLine("{0} {1}", ToMillisecondsSinceUnixEpoch(dt),
FromMillisecondsSinceUnixEpoch(ToMillisecondsSinceUnixEpoch(dt)));
10/1/2015 5:00:00 AM
1443657600000 10/1/2015 12:00:00 AM
如果我改变FromMillisecondsSinceUnixEpoch
:
public static DateTime FromMillisecondsSinceUnixEpoch(long milliseconds)
{
return UnixEpoch().AddMilliseconds(milliseconds).ToUniversalTime();
}
现在它将进行往返:
10/1/2015 5:00:00 AM
1443675600000 10/1/2015 5:00:00 AM
请注意,每个 MilliSecondsSince 都是相同的。你不能只看那个,因为没有上下文。
我在参考源中找不到它,但 DateTime 肯定足够聪明,可以在 TZ 不同时进行调整,然后再进行减法。 (否则很难解释 1443675600000
如何代表同一日期的 3 个时间跨度(2 个给我,1 个给你)。
几件事:
如果可能,您应该使用
DateTimeOffset
而不是DateTime
来进行此类操作。DateTimeOffset
总是一个特定的时刻,而DateTime
可能 是,也可能不是,这取决于Kind
以及您坚持的程度各种方法如何解释Kind
的微妙之处。如果您使用
DateTimeOffset
,并且您的目标是 .NET 4.6 或更高版本(或 .NET Core),那么您可以使用内置的DateTimeOffset.FromUnixTimeMilliseconds
andToUnixTimeMilliseconds
方法,而不是创建自己的。您可以考虑使用 Noda Time 开源库,因为它为大多数使用日期和时间的应用程序增加了重要价值。
- 例如,如果您想使用 tzdb 时区,例如您提到的
"Europe/London"
时区,那么您可以使用DateTimeZoneProviders.Tzdb["Europe/London"]
.
- 例如,如果您想使用 tzdb 时区,例如您提到的
现在我的其余回答假设您没有采纳上述任何建议,并且与您在问题中提供的代码有关。
您已将
UnixEpoch
实现为静态方法。由于它的值永远不会改变,它可能应该作为一个静态 属性 来实现,并带有一个私有的只读支持字段。它也可以实现为 public 静态只读字段,尽管大多数人更喜欢通过属性公开这些字段。 (这些只是编码准则,但不会引入任何错误。)在您的
FromMillisecondsSinceUnixEpoch
方法中,您正在调用.ToLocalTime()
。那应该被省略。您也不需要调用.ToUniversalTime()
。只是 return 添加毫秒的结果。Kind
将是Utc
。如果您需要使用本地时间,请稍后进行转换 - 不要在此函数内。识别 ID
"GMT Standard Time"
适用于伦敦,而不是 UTC。伦敦是 GMT (UTC+00:00) 或 BST (UTC+01:00),具体取决于相关日期和时间。识别
DateTime.ToLocalTime
和DateTime.ToUniversalTime
在代码为运行的机器上在UTC和当前本地时区之间转换。那可能是伦敦,也可能是其他地方,具体取决于您的用例。如果您 运行 在 服务器 上,例如在 ASP.Net 网络应用程序中,那么依赖系统本地时区并不是一个好习惯。在您使用
TimeZoneInfo.ConvertTime
显示的代码中,由于您没有将DateTimeKind.Utc
分配给输入,因此该值将具有DateTimeKind.Unspecified
。ConvertTime
会将其解释为已经属于源时区。由于您指定了相同的目的地时区,因此在大多数情况下这将是一个空操作。在同一个函数中,由于前面指定的原因,根据伦敦时间定义
unixEpoch
是无效的。另请注意,在 1970 年 1 月 1 日,伦敦不在 GMT,但实际上在 BST(当时称为 "British Standard Time",而不是 "British Summer time")。 TZDB 知道这一点,但它对于 Windows 时区和TimeZoneInfo
来说太落后了。"GMT Standard Time"
Windows 区域仅反映了 BST/GMT 周围的当前规则,而不是当时有效的规则。
就转换您提供的 Java 代码而言,该函数以毫秒精度读取 ISO 8601 格式的字符串,在伦敦时区对其进行解释,将其转换为 UTC,并给出时间自 Unix 纪元以来的毫秒数。有几种方法可以做到这一点:
.Net 3.5+
public static long UkTimeStringToUtcMillis(string s) { string format = "yyyy-MM-dd'T'HH:mm:ss.FFF"; DateTime dt = DateTime.ParseExact(s, format, CultureInfo.InvariantCulture); TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"); DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz); DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return (long) (utc - epoch).TotalMilliseconds; }
.Net 4.6+ / .Net CoreCLR
public static long UkTimeStringToUtcMillis(string s) { string format = "yyyy-MM-dd'T'HH:mm:ss.FFF"; DateTime dt = DateTime.ParseExact(s, format, CultureInfo.InvariantCulture); TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"); TimeSpan offset = tz.GetUtcOffset(dt); DateTimeOffset dto = new DateTimeOffset(dt, offset); return dto.ToUnixTimeMilliseconds(); }
野田时间
public static long UkTimeStringToUtcMillis(string s) { LocalDateTimePattern pattern = LocalDateTimePattern.ExtendedIsoPattern; LocalDateTime dt = pattern.Parse(s).Value; DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/London"]; Instant i = dt.InZoneLeniently(tz).ToInstant(); return i.Ticks / NodaConstants.TicksPerMillisecond; }