在VBA中,如何简单地将UTC UNIX时间戳转换为本地时区日期?
In VBA, How to convert a UTC UNIX timestamp to Local timezone Date in a simple way?
据我所知,我们可以使用下面的方法将 UNIX 时间戳粗略地转换为 VB 日期
CDate([UNIX 时间戳]/ 60 / 60 / 24) + "1/1/1970"
但是,不考虑时区和夏令时信息。
时区不是什么大问题。但是我无法获取特定 UNIX 时间戳的夏令时偏差信息。
虽然Date 1/1的夏令时偏差与Date 6/1明显不同,但是对于Date 3/12或Date 11/5,夏令时偏差计算非常复杂。
我尝试了几个 API,例如“FileTimeToLocalFileTime”和“GetTimeZoneInformation”,但 none 有效。
这是我无法处理日光偏差的代码
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function LocalFileTimeToFileTime Lib "kernel32" (src@, tgt@) As Long
Private Declare PtrSafe Function FileTimeToLocalFileTime Lib "kernel32" (src@, tgt@) As Long
#Else
Private Declare Function LocalFileTimeToFileTime Lib "kernel32" (src@, tgt@) As Long
Private Declare Function FileTimeToLocalFileTime Lib "kernel32" (src@, tgt@) As Long
#End If
Public Function ToUTC(ByVal datetime As Date) As Date
Dim ftLoc@, ftUtc@
ftLoc = (datetime - #1/1/1601#) * 86400000
LocalFileTimeToFileTime ftLoc, ftUtc
ToUTC = ftUtc / 86400000# + #1/1/1601#
End Function
Public Function FromUTC(ByVal datetime As Date) As Date
Dim ftUtc@, ftLoc@
ftUtc = (datetime - #1/1/1601#) * 86400000
FileTimeToLocalFileTime ftUtc, ftLoc
FromUTC = ftLoc / 86400000# + #1/1/1601#
End Function
Function getDateFromTimestamp(ByVal value) As Date
Dim t1, t2
t1 = CDate(value / 60 / 60 / 24) + "1/1/1970"
t2 = FromUTC(t1)
Debug.Print t2 - t1
getDateFromTimestamp = t2
End Function
可能最简单的方法是通过 COM Interop 使用 .NET 中内置的 DateTime
API。这种方法有一个很好的介绍 in this answer。
创建 C# class 库。为了使事情更简单,目标为 .NET Framework 4.6 或更高版本,因此您可以在 DateTimeOffset
class.
上使用 FromUnixTimeSeconds
和 ToUnixTimeSeconds
方法
using System;
using System.Runtime.InteropServices;
namespace MyDateTimeLibrary
{
[Serializable]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class DateTimeFunctions
{
public DateTime UnixTimeToDateTime(int unixTimeInSeconds)
{
return DateTimeOffset.FromUnixTimeSeconds(unixTimeInSeconds).UtcDateTime;
}
public long DateTimeUtcToUnixTime(DateTime utcDateTime)
{
return new DateTimeOffset(utcDateTime, TimeSpan.Zero).ToUnixTimeSeconds();
}
public DateTime UtcToLocal(DateTime utcDateTime)
{
return utcDateTime.ToLocalTime();
}
public DateTime LocalToUtc(DateTime localDateTime)
{
return localDateTime.ToUniversalTime();
}
public DateTime TZSpecificDateTimeToUTC(DateTime sourceDateTime, string sourceTimeZoneId)
{
var tzi = TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZoneId);
return TimeZoneInfo.ConvertTimeToUtc(sourceDateTime, tzi);
}
public DateTime UTCToTZSpecificDateTime(DateTime utcDateTime, string destinationTimeZoneId)
{
var tzi = TimeZoneInfo.FindSystemTimeZoneById(destinationTimeZoneId);
return TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tzi);
}
}
}
对于你的要求,你真的只需要上面代码中的 UnixTimeToDateTime
和 UtcToLocal
方法,但我想我会为其他可能类似的方法投入更多情况。真的,您可以在 .NET 中做任何事情,在这里也可以做。 .NET DateTime
、DateTimeOffset
和 TimeZoneInfo
API 比您需要执行类似工作的 Win32 API 更容易和更可靠。
在构建之前检查 Register for COM interop
选项(如前面链接的答案中所述),或者如果您要部署到另一台机器,则通过 RegAsm.exe
实用程序向 COM 注册您的 DLL。
接下来,将您的 VBA 项目的引用添加到您新注册的 MyDateTimeLibrary
。然后使用它,像这样(例如):
Sub Test()
Dim dtf As New MyDateTimeLibrary.DateTimeFunctions
Dim utcDt As Date, localDt As Date
utcDt = dtf.UnixTimeToDateTime(1487010504)
localDt = dtf.UtcToLocal(utcDt)
MsgBox ("UTC: " + CStr(utcDt) + vbCrLf + "Local: " + CStr(localDt))
Set dtf = Nothing
End Sub
我相信您可以重构它来编写一些有用的 VBA 函数,这些函数传入和传出要转换的日期,从 Excel 单元格或任何您的上下文中调用它们。我会把那部分留给你。
据我所知,我们可以使用下面的方法将 UNIX 时间戳粗略地转换为 VB 日期
CDate([UNIX 时间戳]/ 60 / 60 / 24) + "1/1/1970"
但是,不考虑时区和夏令时信息。
时区不是什么大问题。但是我无法获取特定 UNIX 时间戳的夏令时偏差信息。
虽然Date 1/1的夏令时偏差与Date 6/1明显不同,但是对于Date 3/12或Date 11/5,夏令时偏差计算非常复杂。
我尝试了几个 API,例如“FileTimeToLocalFileTime”和“GetTimeZoneInformation”,但 none 有效。
这是我无法处理日光偏差的代码
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function LocalFileTimeToFileTime Lib "kernel32" (src@, tgt@) As Long
Private Declare PtrSafe Function FileTimeToLocalFileTime Lib "kernel32" (src@, tgt@) As Long
#Else
Private Declare Function LocalFileTimeToFileTime Lib "kernel32" (src@, tgt@) As Long
Private Declare Function FileTimeToLocalFileTime Lib "kernel32" (src@, tgt@) As Long
#End If
Public Function ToUTC(ByVal datetime As Date) As Date
Dim ftLoc@, ftUtc@
ftLoc = (datetime - #1/1/1601#) * 86400000
LocalFileTimeToFileTime ftLoc, ftUtc
ToUTC = ftUtc / 86400000# + #1/1/1601#
End Function
Public Function FromUTC(ByVal datetime As Date) As Date
Dim ftUtc@, ftLoc@
ftUtc = (datetime - #1/1/1601#) * 86400000
FileTimeToLocalFileTime ftUtc, ftLoc
FromUTC = ftLoc / 86400000# + #1/1/1601#
End Function
Function getDateFromTimestamp(ByVal value) As Date
Dim t1, t2
t1 = CDate(value / 60 / 60 / 24) + "1/1/1970"
t2 = FromUTC(t1)
Debug.Print t2 - t1
getDateFromTimestamp = t2
End Function
可能最简单的方法是通过 COM Interop 使用 .NET 中内置的 DateTime
API。这种方法有一个很好的介绍 in this answer。
创建 C# class 库。为了使事情更简单,目标为 .NET Framework 4.6 或更高版本,因此您可以在 DateTimeOffset
class.
FromUnixTimeSeconds
和 ToUnixTimeSeconds
方法
using System;
using System.Runtime.InteropServices;
namespace MyDateTimeLibrary
{
[Serializable]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class DateTimeFunctions
{
public DateTime UnixTimeToDateTime(int unixTimeInSeconds)
{
return DateTimeOffset.FromUnixTimeSeconds(unixTimeInSeconds).UtcDateTime;
}
public long DateTimeUtcToUnixTime(DateTime utcDateTime)
{
return new DateTimeOffset(utcDateTime, TimeSpan.Zero).ToUnixTimeSeconds();
}
public DateTime UtcToLocal(DateTime utcDateTime)
{
return utcDateTime.ToLocalTime();
}
public DateTime LocalToUtc(DateTime localDateTime)
{
return localDateTime.ToUniversalTime();
}
public DateTime TZSpecificDateTimeToUTC(DateTime sourceDateTime, string sourceTimeZoneId)
{
var tzi = TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZoneId);
return TimeZoneInfo.ConvertTimeToUtc(sourceDateTime, tzi);
}
public DateTime UTCToTZSpecificDateTime(DateTime utcDateTime, string destinationTimeZoneId)
{
var tzi = TimeZoneInfo.FindSystemTimeZoneById(destinationTimeZoneId);
return TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tzi);
}
}
}
对于你的要求,你真的只需要上面代码中的 UnixTimeToDateTime
和 UtcToLocal
方法,但我想我会为其他可能类似的方法投入更多情况。真的,您可以在 .NET 中做任何事情,在这里也可以做。 .NET DateTime
、DateTimeOffset
和 TimeZoneInfo
API 比您需要执行类似工作的 Win32 API 更容易和更可靠。
在构建之前检查 Register for COM interop
选项(如前面链接的答案中所述),或者如果您要部署到另一台机器,则通过 RegAsm.exe
实用程序向 COM 注册您的 DLL。
接下来,将您的 VBA 项目的引用添加到您新注册的 MyDateTimeLibrary
。然后使用它,像这样(例如):
Sub Test()
Dim dtf As New MyDateTimeLibrary.DateTimeFunctions
Dim utcDt As Date, localDt As Date
utcDt = dtf.UnixTimeToDateTime(1487010504)
localDt = dtf.UtcToLocal(utcDt)
MsgBox ("UTC: " + CStr(utcDt) + vbCrLf + "Local: " + CStr(localDt))
Set dtf = Nothing
End Sub
我相信您可以重构它来编写一些有用的 VBA 函数,这些函数传入和传出要转换的日期,从 Excel 单元格或任何您的上下文中调用它们。我会把那部分留给你。