在 VBA 中,如何以简单的方式将 UTC UNIX 时间戳转换为本地时区日期?

Posted

技术标签:

【中文标题】在 VBA 中,如何以简单的方式将 UTC UNIX 时间戳转换为本地时区日期?【英文标题】:In VBA, How to convert a UTC UNIX timestamp to Local timezone Date in a simple way? 【发布时间】:2017-06-26 11:54:25 【问题描述】:

据我所知,我们可以使用下面将 UNIX 时间戳大致转换为 VB 日期

CDate([UNIX 时间戳]/60 / 60 / 24) + "1/1/1970"

但是,不考虑时区和日光信息。

时区不是什么大问题。但我无法获得特定 UNIX 时间戳的日光偏差信息。

虽然日期 1/1 的日光偏差与日期 6/1 明显不同,但是对于日期 3/12 或日期 11/5,日光偏差的计算非常复杂。

我尝试了几个 API,例如 “FileTimeToLocalFileTime” 和 “GetTimeZoneInformation” ,但它们都不起作用。

这是我无法处理日光偏差的代码

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

【问题讨论】:

您从哪里获得时间戳来自?为什么所有东西都声明为Currency 我没有看到任何货币? UNIX 时间戳来自 http json 文本,该文本来自 DB。 src@src As Currency 相同。有关类型提示,请参阅 documentation page。 您是否有 json 文本的样本以及如何处理它? 如果您正在处理历史数据,请确保您的日常工作考虑到 DST 更改日期(至少在美国)几年前已更改的事实。例如,美国在 2017 年 3 月 12 日开始夏令时,但过去在 4 月 2 日或 4 月 9 日(如果我没记错的话)会晚 3-4 周。不,抱歉,不记得转换的年份,但我相信那是在布什 2 期执政期间。 【参考方案1】:

可能最简单的方法是通过 COM 互操作来使用 .NET 中内置的 DateTime API。这个方法有一个很好的介绍in this answer。

创建一个 C# 类库。为简化起见,请以 .NET Framework 4.6 或更高版本为目标,这样您就可以在 DateTimeOffset 类上使用 FromUnixTimeSecondsToUnixTimeSeconds 方法。

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);
        
    

对于你所问的,你真的只需要上面代码中的 UnixTimeToDateTimeUtcToLocal 方法,但我想我会为可能处于类似情况的其他人添加一些方法。实际上,您可以在 .NET 中执行的任何操作都可以在此处执行。 .NET DateTimeDateTimeOffsetTimeZoneInfo API 比执行类似工作所需的 Win32 API 更容易、更可靠。

在构建之前检查Register for COM interop 选项(如前面链接的答案中所述),或者如果您要部署到另一台计算机,则通过RegAsm.exe 实用程序将您的DLL 注册到COM。

接下来,将 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 单元格中调用这些日期或任何您的上下文。我会把这部分留给你。

【讨论】:

感谢您的回答!经过我的验证,你是对的。

以上是关于在 VBA 中,如何以简单的方式将 UTC UNIX 时间戳转换为本地时区日期?的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式将 .xlam 插件(作为对象)添加到 vba?

excel中使用vba以只读方式打开工作薄的操作方法

在 Java/Scala 中,如何以最少的代码/以最优雅的方式在几秒钟内获得特定的 UTC 日期?

如何在 VBA 中删除文本 - 以编程方式?

如何使用 VBA 以编程方式添加引用

将excel VBA中的日期类型(Sun Apr 01 00:00:00 UTC 2018)转换为自定义函数