Convert Time to UTC vbScript

早过忘川 提交于 2019-12-07 02:32:22

问题


I have the following function which does fine at converting current time to UTC time.

Function toUtc(byVal dDate)
    Dim oShell : Set oShell = CreateObject("WScript.Shell")
    toUtc = dateadd("n", oShell.RegRead("HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias"), cDate(dDate))
End Function

However, I am thinking that this does not adequately handle conversion of future or historical dates to UTC, since the function would need to know the offset of the server's timezone at the time of the date that is being converted, and whether or not it was during daylight savings time or not.

How can I get around this?

I am using vbScript/Classic ASP on windows server with IIS7.5

To clarify, this has nothing to do with formatting the date. It is all about converting to UTC from the server timezone for historical dates. During Daylight savings time, the offset will be off by 60 minutes if I try to convert a datetime which occurred in standard time.

For example:

Lets say that today, 2013-02-19 (non daylight-savings time), I want to covert a timestamp from say, 2008-06-05 (during daylight savings period) from PDT (my server timezone) to UTC. using the method in my function will give me a value which is 60 minutes off from the correct value (because the CURRENT time is PST (not PDT), the offset will be incorrect for that historical date).

In other words, the timezone offset of the input date could be UTC-7 or UTC-8 depending on whether or not DST was observed on that particular date. I need to calculate the correct UTC date for the input date and I need the code to work whether or not DST is observed on current date.


回答1:


Here is the solution I ended up implementing at the suggestion of @AardVark71.

I put this in my func.asp file:

<script language="javascript" runat="server">
    function toUtcString(d) {
        var dDate = new Date(d);
        return Math.round(dDate.getTime() / 1000);
    };
</script>

It outputs a timestamp value. Then from anywhere in the classic asp code, I can do this:

response.Write DateAdd("s", toUtcString(cDate("11/11/2012 06:25 PM")), "01/01/1970 00:00:00") 'expect 11/11/2012 10:25:00 PM gmt/utc time
response.Write DateAdd("s", toUtcString(cDate("06/11/2012 06:25 PM")), "01/01/1970 00:00:00") 'expect  6/11/2012  9:25:00 PM gmt/utc time

This (I believe) is simple, and it produces the expected values and fully factors in the DST.




回答2:


In ASP classic you can mix VBScript and JScript code inside one page. The JScript Date object could be used for local ⇄ UTC date conversion. Complete example:

<%@ language="vbscript" %>
<%
Option Explicit
Dim local_date
Dim utc_date
For Each local_date In Array("11/11/2012 06:25 PM", "06/11/2012 06:25 PM")
    utc_date = local_to_utc(local_date)
    Response.Write "Local: " & local_date & ", UTC: " & utc_date & vbNewLine
Next
For Each utc_date In Array("2012-11-12T02:25:00Z", "2012-06-12T01:25:00+00:00")
    local_date = utc_to_local(utc_date)
    Response.Write "UTC: " & utc_date & ", Local: " & local_date & vbNewLine
Next
%>
<script language="jscript" runat="server">
    function local_to_utc(datestr) {
        // note that this function lets JScript engine parse the date, correctly or otherwise
        var date = new Date(datestr);
        var result = date.getUTCFullYear() + "-" + (date.getUTCMonth() + 1) + "-" + date.getUTCDate() + "T" + date.getUTCHours() + ":" + date.getUTCMinutes() + ":" + date.getUTCSeconds();
        return result.replace(/(\D)(\d)(?!\d)/g, "$10$2") + "Z";
    }
    function utc_to_local(datestr) {
        // note that this function parses only a subset of ISO 8601 date format
        var match = datestr.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?(?:Z|([-+])(\d{2}):(\d{2}))$/);
        var offset = match[8] ? (match[8] === "+" ? 1 : -1) * (match[9] * 60 + match[10] * 1) : 0;
        var date = new Date(Date.UTC(match[1], match[2] - 1, match[3], match[4], match[5] - offset, match[6], match[7] || 0));
        var result = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
        return result.replace(/(\D)(\d)(?!\d)/g, "$10$2");
    }
</script>

Expected result:

  • Nov 11 2012 18:25 PST = Nov 12 2012 02:25 UTC
  • Jun 11 2012 18:25 PDT = Jun 12 2012 01:25 UTC

And the output when executed on a server using PST/PDT time:

Local: 11/11/2012 06:25 PM,       UTC:   2012-11-12T02:25:00Z
Local: 06/11/2012 06:25 PM,       UTC:   2012-06-12T01:25:00Z
UTC:   2012-11-12T02:25:00Z,      Local: 2012-11-11 18:25:00
UTC:   2012-06-12T01:25:00+00:00, Local: 2012-06-11 18:25:00



回答3:


If I not miss something, you need to get local time on remote machine and it time zone offset, right?

Here I see self-answer based on WMI Win32_TimeZone Bias property. But according to this MSDN example: "Converting Local Time to UTC Time", quote:

Use Win32_ComputerSystem CurrentTimeZone property, because it automatically adjusts the Time Zone bias for daylight saving time Win32_TimeZone Bias property does not.

So, next example function get current date time, and I'll leave to you to modify it for concrete date time measuring.

' "." mean local computer
WScript.Echo FormatDateTime(UTCDate("."), 0)

Function UTCDate(strComputer)
    Dim objWMIService, ColDate, ColCS

    On Error Resume Next
    Set objWMIService = _
        GetObject("winmgmts:{impersonationLevel=impersonate}!\\" _
        & strComputer & "\root\cimv2")

    Set ColDate = objWMIService.ExecQuery("Select * From Win32_LocalTime")

    UTCDate = DateSerial(100, 1, 1)
    For Each objDate In ColDate
        UTCDate = DateAdd("yyyy", ObjDate.Year - 100, UTCDate)
        UTCDate = DateAdd("m", ObjDate.Month - 1, UTCDate)
        UTCDate = DateAdd("d", ObjDate.Day - 1, UTCDate)
        UTCDate = DateAdd("h", ObjDate.Hour, UTCDate)
        UTCDate = DateAdd("n", ObjDate.Minute, UTCDate)
        UTCDate = DateAdd("s", ObjDate.Second, UTCDate)
    Next

    ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms696015(v=vs.85).aspx
    Set ColCS = objWMIService.ExecQuery("Select * From Win32_ComputerSystem")

    Dim TimeZoneOffset, LocalTimeZone
    For Each LocalTimeZone In ColCS
        TimeZoneOffset = LocalTimeZone.CurrentTimeZone
    Next

    If TimeZoneOffset < 0 Then
        TimeZoneOffset = Abs(TimeZoneOffset)
    Else
        TimeZoneOffset = -Abs(TimeZoneOffset)
    End If

    UTCDate = DateAdd("n", TimeZoneOffset, UTCDate)
    If Err Then UTCDate = vbNull
    Set objWMIService = Nothing
End Function

[EDIT] Well, looks like should not trust on MSDN then (or maybe this work only for current time). However, I see it's not the answer you looking for. If you not find better idea, you may automate this online calculator.

P.S. Ok, strike out above code and lets try another "iteration".

Just to note that I made a code compatible with my time zone (Bulgaria), where PDT start at last Sunday of March and end at last Sunday of October. This allow me to set easy my PDT range. Shortly, the idea is to make algorithm applicable to your region. The rest is clearly, I hope.

With New DateDrill
    Debug.WriteLine .UTCDate("2008-6-28")
    Debug.WriteLine .UTCDate("2014-1-21")
End With

Class DateDrill

    Public Function UTCDate(ByVal dtDate)
        If Not IsDate(dtDate) Then Err.Raise 5
        dtDate = CDate(dtDate)
        Dim ZoneBias: ZoneBias = TimeZoneBias()
        If IsPDT(Now) <> IsPDT(dtDate) Then
            ZoneBias = ZoneBias - 60
        End If
        UTCDate = DateAdd("n", ZoneBias, dtDate)
    End Function

    Private Function IsPDT(ByVal dtDate)
        If Not IsDate(dtDate) Then Err.Raise 5
        dtDate = CDate(dtDate)
        Dim pdtLow, pdtUpr, nDaysBack

        pdtLow = DateSerial(Year(dtDate),  3, 31)
        pdtUpr = DateSerial(Year(dtDate), 10, 31)
        pdtLow = DateAdd("h", 2, pdtLow)
        pdtUpr = DateAdd("h", 2, pdtUpr)

        nDaysBack = Weekday(pdtLow) - 1
        If nDaysBack <> 0 Then
            pdtLow = DateAdd("d", -nDaysBack, pdtLow)
        End If

        nDaysBack = Weekday(pdtUpr) - 1
        If nDaysBack <> 0 Then
            pdtUpr = DateAdd("d", -nDaysBack, pdtUpr)
        End If

        IsPDT = (dtDate >= pdtLow And dtDate <= pdtUpr)
    End Function

    Private Function TimeZoneBias()
        Dim LTZone
        With GetObject("winmgmts:" & _
                "{impersonationLevel=impersonate}!\\.\root\cimv2")
            For Each LTZone In .ExecQuery(_
                    "Select * From Win32_ComputerSystem")
                TimeZoneBias = LTZone.CurrentTimeZone
            Next
        End With
        TimeZoneBias = TimeZoneBias * -1
    End Function

End Class



回答4:


This little collection of ASP VBScript functions may do what you need. I'm drawing a Unix Timestamp from a MySQL database. Depending on you database it may be a slightly different query, but most DBs can return a Unix Timestamp. On my server the query only takes one or two milliseconds.

Note: pquery is my own function for running SQL queries. Obviously substitute in your own method.

I'm calculating the offset each time unixTime() runs. You could of course calculate it once in a "bootstrap" file and set a global variable if you prefer.

Function unixTimestamp()
    ' Unix Timestamp, drawn from MySQL because ASP doesn't natively handle time zones
    rs = pquery( "SELECT unix_timestamp() AS `ts`", null )
    unixTimestamp = cLng( rs("ts") )
End Function

Function utc_offset()
    ' Offset of Server time from UTC, in seconds
    dim unixNow : unixNow = unixTimestamp()
    dim aspNow  : aspNow  = cLng( DateDiff( "s", "01/01/1970 00:00:00", now() ) )
    utc_offset = cLng( ( aspNow - unixNow ))
End Function

Function unixTime( byVal dt )
    dim offset : offset = utc_offset()
    if( VarType( dt ) = vbDate ) then
        unixTime = cLng( DateDiff( "s", "01/01/1970 00:00:00", dt ) - offset )
    else
        if( isDate( dt ) ) then
            dt = cDate( dt )
            unixTime = cLng( DateDiff( "s", "01/01/1970 00:00:00", dt ) - offset )
        else
            unixTime = null
        end if
    end if
End Function

Function datetimeFromUnix( byVal udt, offset )
    datetimeFromUnix = cDate( DateAdd( "s", udt + offset , "01/01/1970 00:00:00" ) )
End Function

Now run:

utcTimestamp = unixTime( dDate )
utcDate = datetimeFromUnix( utcTimestamp, 0 )   ' UTC
localDate = datetimeFromUnix( utcTimestamp, utc_offset() )  ' back where we started


来源:https://stackoverflow.com/questions/14949706/convert-time-to-utc-vbscript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!