Excel에서 ISO8601 날짜 / 시간 (TimeZone 포함) 구문 분석


85

Excel / VBA에 포함 된 표준 시간대 (외부 소스에서)를 사용하여 ISO8601 날짜 / 시간 형식을 일반 Excel 날짜로 구문 분석해야합니다. 내가 알 수있는 한, Excel XP (우리가 사용하고있는 것)에는 해당 기본 제공 루틴이 없으므로 구문 분석을위한 사용자 지정 VBA 함수를 찾고있는 것 같습니다.

ISO8601 날짜 시간은 다음 중 하나와 같습니다.

2011-01-01
2011-01-01T12:00:00Z
2011-01-01T12:00:00+05:00
2011-01-01T12:00:00-05:00
2011-01-01T12:00:00.05381+05:00

2
이제 2020 년이되었고 Office 365를 통한 최신 버전의 Excel 에는 여전히TryParseExactDate( "yyyy-MM-dd'T'HH:mm:ss", A1 ) 방대한 수식 라이브러리에 간단한 기능 이 없습니다 . Microsoft의 변명은 무엇입니까? :(
Dai

답변:


172

매크로 대신 수식을 사용하여 시간대없이 ISO 타임 스탬프를 구문 분석하는 (합리적으로) 간단한 방법이 있습니다. 이것은 원본 포스터가 요청한 것과 정확히 일치 하지 않지만 Excel에서 ISO 타임 스탬프를 구문 분석 할 때이 질문을 발견 하고이 솔루션이 유용하다는 것을 알았 으므로 여기서 공유 할 것이라고 생각했습니다.

다음 공식은 다시 시간대없이 ISO 타임 스탬프를 구문 분석합니다.

=DATEVALUE(MID(A1,1,10))+TIMEVALUE(MID(A1,12,8))

이렇게하면 부동 소수점 형식으로 날짜가 생성되고 일반 Excel 형식을 사용하여 날짜 형식을 지정할 수 있습니다.


4
이것이 받아 들여진 대답이되지 않았다는 것이 이상합니다. 나머지보다 훨씬 간단합니다.
Travis Griggs

6
그러나이 솔루션은 시간대 변환을 고려하지 않습니다.
Goku

1
시간대가 관련이 없거나 모두 동일한 경우 (예 : 현지 시간대) 합리적인 대안입니다.
kevinarpe 2015

5
필요하고 입력에 포함되는 경우 밀리 초를 포함 8하도록 변경할 수 있습니다 12.
gilly3

3
나는 이것을 시간 코드를 변환하는 데 사용했습니다. 마지막 부분에 HH : MM 차이를 입력하고 시간대에 따라 더하거나 뺍니다. 제 경우에는 6 시간 뒤져서 빼겠습니다. =DATEVALUE(MID(C2,1,10))+TIMEVALUE(MID(C2,12,8))-TIMEVALUE("6:00")
chaiboy

44

많은 인터넷 검색에서 아무것도 나오지 않아서 나만의 루틴을 작성합니다. 향후 참조를 위해 여기에 게시 :

Option Explicit

'---------------------------------------------------------------------
' Declarations must be at the top -- see below
'---------------------------------------------------------------------
Public Declare Function SystemTimeToFileTime Lib _
  "kernel32" (lpSystemTime As SYSTEMTIME, _
  lpFileTime As FILETIME) As Long

Public Declare Function FileTimeToLocalFileTime Lib _
  "kernel32" (lpLocalFileTime As FILETIME, _
  lpFileTime As FILETIME) As Long

Public Declare Function FileTimeToSystemTime Lib _
  "kernel32" (lpFileTime As FILETIME, lpSystemTime _
  As SYSTEMTIME) As Long

Public Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Public Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

'---------------------------------------------------------------------
' Convert ISO8601 dateTimes to Excel Dates
'---------------------------------------------------------------------
Public Function ISODATE(iso As String)
    ' Find location of delimiters in input string
    Dim tPos As Integer: tPos = InStr(iso, "T")
    If tPos = 0 Then tPos = Len(iso) + 1
    Dim zPos As Integer: zPos = InStr(iso, "Z")
    If zPos = 0 Then zPos = InStr(iso, "+")
    If zPos = 0 Then zPos = InStr(tPos, iso, "-")
    If zPos = 0 Then zPos = Len(iso) + 1
    If zPos = tPos Then zPos = tPos + 1

    ' Get the relevant parts out
    Dim datePart As String: datePart = Mid(iso, 1, tPos - 1)
    Dim timePart As String: timePart = Mid(iso, tPos + 1, zPos - tPos - 1)
    Dim dotPos As Integer: dotPos = InStr(timePart, ".")
    If dotPos = 0 Then dotPos = Len(timePart) + 1
    timePart = Left(timePart, dotPos - 1)

    ' Have them parsed separately by Excel
    Dim d As Date: d = DateValue(datePart)
    Dim t As Date: If timePart <> "" Then t = TimeValue(timePart)
    Dim dt As Date: dt = d + t

    ' Add the timezone
    Dim tz As String: tz = Mid(iso, zPos)
    If tz <> "" And Left(tz, 1) <> "Z" Then
        Dim colonPos As Integer: colonPos = InStr(tz, ":")
        If colonPos = 0 Then colonPos = Len(tz) + 1

        Dim minutes As Integer: minutes = CInt(Mid(tz, 2, colonPos - 2)) * 60 + CInt(Mid(tz, colonPos + 1))
        If Left(tz, 1) = "+" Then minutes = -minutes
        dt = DateAdd("n", minutes, dt)
    End If

    ' Return value is the ISO8601 date in the local time zone
    dt = UTCToLocalTime(dt)
    ISODATE = dt
End Function

'---------------------------------------------------------------------
' Got this function to convert local date to UTC date from
' http://excel.tips.net/Pages/T002185_Automatically_Converting_to_GMT.html
'---------------------------------------------------------------------
Public Function UTCToLocalTime(dteTime As Date) As Date
    Dim infile As FILETIME
    Dim outfile As FILETIME
    Dim insys As SYSTEMTIME
    Dim outsys As SYSTEMTIME

    insys.wYear = CInt(Year(dteTime))
    insys.wMonth = CInt(Month(dteTime))
    insys.wDay = CInt(Day(dteTime))
    insys.wHour = CInt(Hour(dteTime))
    insys.wMinute = CInt(Minute(dteTime))
    insys.wSecond = CInt(Second(dteTime))

    Call SystemTimeToFileTime(insys, infile)
    Call FileTimeToLocalFileTime(infile, outfile)
    Call FileTimeToSystemTime(outfile, outsys)

    UTCToLocalTime = CDate(outsys.wMonth & "/" & _
      outsys.wDay & "/" & _
      outsys.wYear & " " & _
      outsys.wHour & ":" & _
      outsys.wMinute & ":" & _
      outsys.wSecond)
End Function

'---------------------------------------------------------------------
' Tests for the ISO Date functions
'---------------------------------------------------------------------
Public Sub ISODateTest()
    ' [[ Verify that all dateTime formats parse sucesfully ]]
    Dim d1 As Date: d1 = ISODATE("2011-01-01")
    Dim d2 As Date: d2 = ISODATE("2011-01-01T00:00:00")
    Dim d3 As Date: d3 = ISODATE("2011-01-01T00:00:00Z")
    Dim d4 As Date: d4 = ISODATE("2011-01-01T12:00:00Z")
    Dim d5 As Date: d5 = ISODATE("2011-01-01T12:00:00+05:00")
    Dim d6 As Date: d6 = ISODATE("2011-01-01T12:00:00-05:00")
    Dim d7 As Date: d7 = ISODATE("2011-01-01T12:00:00.05381+05:00")
    AssertEqual "Date and midnight", d1, d2
    AssertEqual "With and without Z", d2, d3
    AssertEqual "With timezone", -5, DateDiff("h", d4, d5)
    AssertEqual "Timezone Difference", 10, DateDiff("h", d5, d6)
    AssertEqual "Ignore subsecond", d5, d7

    ' [[ Independence of local DST ]]
    ' Verify that a date in winter and a date in summer parse to the same Hour value
    Dim w As Date: w = ISODATE("2010-02-23T21:04:48+01:00")
    Dim s As Date: s = ISODATE("2010-07-23T21:04:48+01:00")
    AssertEqual "Winter/Summer hours", Hour(w), Hour(s)

    MsgBox "All tests passed succesfully!"
End Sub

Sub AssertEqual(name, x, y)
    If x <> y Then Err.Raise 1234, Description:="Failed: " & name & ": '" & x & "' <> '" & y & "'"
End Sub

내 시스템에 PtrSafe각각 추가 해야했습니다 Declare.
Raman

1
네, 작동하지 않습니다. Dim d8 As Date: d8 = ISODATE("2020-01-02T16:46:00")1 월 2 일에 유효한 ISO 날짜 인 테스트를 추가하면 2 월 1 일이 반환됩니다. 테스트는 매우 낙관적입니다.
Liam

5

나는 이것을 코멘트로 게시했을 것입니다. 그러나 나는 충분한 담당자가 없습니다-죄송합니다!. 이것은 나에게 정말 유용했습니다. rix0rrr에게 감사드립니다.하지만 마지막에 날짜를 구성 할 때 UTCToLocalTime 함수가 지역 설정을 고려해야한다는 것을 알았습니다. 영국에서 사용하는 버전은 다음과 같습니다. wDay와 wMonth의 순서가 반대입니다.

Public Function UTCToLocalTime(dteTime As Date) As Date
  Dim infile As FILETIME
  Dim outfile As FILETIME
  Dim insys As SYSTEMTIME
  Dim outsys As SYSTEMTIME

  insys.wYear = CInt(Year(dteTime))
  insys.wMonth = CInt(Month(dteTime))
  insys.wDay = CInt(Day(dteTime))
  insys.wHour = CInt(Hour(dteTime))
  insys.wMinute = CInt(Minute(dteTime))
  insys.wSecond = CInt(Second(dteTime))

  Call SystemTimeToFileTime(insys, infile)
  Call FileTimeToLocalFileTime(infile, outfile)
  Call FileTimeToSystemTime(outfile, outsys)

  UTCToLocalTime = CDate(outsys.wDay & "/" & _
    outsys.wMonth & "/" & _
    outsys.wYear & " " & _
    outsys.wHour & ":" & _
    outsys.wMinute & ":" & _
    outsys.wSecond)
  End Function

저자가 필드 순서가 일관된 ISO8601 형식의 datetime 문자열에 대해 물었다는 점을 지적하고 싶습니다. 확실히 당신의 데이터가 당신의 데이터를 위해 작동한다는 것은 훌륭하지만, 다른 누군가가 이것을 읽고 혼란 스러우면 en.wikipedia.org/wiki/ISO_8601xkcd.com/1179를 확인해야합니다 .
Hovis Biddle 2015

2
우와! 과거로부터의 폭발. 어쨌든 ISO 날짜의 필드 순서는 변경하지 않았습니다. 지역 규칙을 따라야 하는 지역 버전입니다. 이상적으로는 코드가 이것을 알아 내야하지만, 이것이 영국에서 사용하기위한 것이라고
말했었습니다

2

대답 하여 rix0rrr은 매우 중요하지만, 그것은 대장하지 않거나 시간과 시간대 오프셋을 지원하지 않습니다. 이러한 형식에 대한 지원을 추가하기 위해 기능을 약간 개선했습니다.

'---------------------------------------------------------------------
' Declarations must be at the top -- see below
'---------------------------------------------------------------------
Public Declare Function SystemTimeToFileTime Lib _
  "kernel32" (lpSystemTime As SYSTEMTIME, _
  lpFileTime As FILETIME) As Long

Public Declare Function FileTimeToLocalFileTime Lib _
  "kernel32" (lpLocalFileTime As FILETIME, _
  lpFileTime As FILETIME) As Long

Public Declare Function FileTimeToSystemTime Lib _
  "kernel32" (lpFileTime As FILETIME, lpSystemTime _
  As SYSTEMTIME) As Long

Public Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Public Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

'---------------------------------------------------------------------
' Convert ISO8601 dateTimes to Excel Dates
'---------------------------------------------------------------------
Public Function ISODATE(iso As String)
    ' Find location of delimiters in input string
    Dim tPos As Integer: tPos = InStr(iso, "T")
    If tPos = 0 Then tPos = Len(iso) + 1
    Dim zPos As Integer: zPos = InStr(iso, "Z")
    If zPos = 0 Then zPos = InStr(iso, "+")
    If zPos = 0 Then zPos = InStr(tPos, iso, "-")
    If zPos = 0 Then zPos = Len(iso) + 1
    If zPos = tPos Then zPos = tPos + 1

    ' Get the relevant parts out
    Dim datePart As String: datePart = Mid(iso, 1, tPos - 1)
    Dim timePart As String: timePart = Mid(iso, tPos + 1, zPos - tPos - 1)
    Dim dotPos As Integer: dotPos = InStr(timePart, ".")
    If dotPos = 0 Then dotPos = Len(timePart) + 1
    timePart = Left(timePart, dotPos - 1)

    ' Have them parsed separately by Excel
    Dim d As Date: d = DateValue(datePart)
    Dim t As Date: If timePart <> "" Then t = TimeValue(timePart)
    Dim dt As Date: dt = d + t

    ' Add the timezone
    Dim tz As String: tz = Mid(iso, zPos)
    If tz <> "" And Left(tz, 1) <> "Z" Then
        Dim colonPos As Integer: colonPos = InStr(tz, ":")
        Dim minutes As Integer
        If colonPos = 0 Then
            If (Len(tz) = 3) Then
                minutes = CInt(Mid(tz, 2)) * 60
            Else
                minutes = CInt(Mid(tz, 2, 5)) * 60 + CInt(Mid(tz, 4))
            End If
        Else
            minutes = CInt(Mid(tz, 2, colonPos - 2)) * 60 + CInt(Mid(tz, colonPos + 1))
        End If

        If Left(tz, 1) = "+" Then minutes = -minutes
        dt = DateAdd("n", minutes, dt)
    End If

    ' Return value is the ISO8601 date in the local time zone
    dt = UTCToLocalTime(dt)
    ISODATE = dt
End Function

'---------------------------------------------------------------------
' Got this function to convert local date to UTC date from
' http://excel.tips.net/Pages/T002185_Automatically_Converting_to_GMT.html
'---------------------------------------------------------------------
Public Function UTCToLocalTime(dteTime As Date) As Date
    Dim infile As FILETIME
    Dim outfile As FILETIME
    Dim insys As SYSTEMTIME
    Dim outsys As SYSTEMTIME

    insys.wYear = CInt(Year(dteTime))
    insys.wMonth = CInt(Month(dteTime))
    insys.wDay = CInt(Day(dteTime))
    insys.wHour = CInt(Hour(dteTime))
    insys.wMinute = CInt(Minute(dteTime))
    insys.wSecond = CInt(Second(dteTime))

    Call SystemTimeToFileTime(insys, infile)
    Call FileTimeToLocalFileTime(infile, outfile)
    Call FileTimeToSystemTime(outfile, outsys)

    UTCToLocalTime = CDate(outsys.wMonth & "/" & _
      outsys.wDay & "/" & _
      outsys.wYear & " " & _
      outsys.wHour & ":" & _
      outsys.wMinute & ":" & _
      outsys.wSecond)
End Function

'---------------------------------------------------------------------
' Tests for the ISO Date functions
'---------------------------------------------------------------------
Public Sub ISODateTest()
    ' [[ Verify that all dateTime formats parse sucesfully ]]
    Dim d1 As Date: d1 = ISODATE("2011-01-01")
    Dim d2 As Date: d2 = ISODATE("2011-01-01T00:00:00")
    Dim d3 As Date: d3 = ISODATE("2011-01-01T00:00:00Z")
    Dim d4 As Date: d4 = ISODATE("2011-01-01T12:00:00Z")
    Dim d5 As Date: d5 = ISODATE("2011-01-01T12:00:00+05:00")
    Dim d6 As Date: d6 = ISODATE("2011-01-01T12:00:00-05:00")
    Dim d7 As Date: d7 = ISODATE("2011-01-01T12:00:00.05381+05:00")
    Dim d8 As Date: d8 = ISODATE("2011-01-01T12:00:00-0500")
    Dim d9 As Date: d9 = ISODATE("2011-01-01T12:00:00-05")
    AssertEqual "Date and midnight", d1, d2
    AssertEqual "With and without Z", d2, d3
    AssertEqual "With timezone", -5, DateDiff("h", d4, d5)
    AssertEqual "Timezone Difference", 10, DateDiff("h", d5, d6)
    AssertEqual "Ignore subsecond", d5, d7
    AssertEqual "No colon in timezone offset", d5, d8
    AssertEqual "No minutes in timezone offset", d5, d9

    ' [[ Independence of local DST ]]
    ' Verify that a date in winter and a date in summer parse to the same Hour value
    Dim w As Date: w = ISODATE("2010-02-23T21:04:48+01:00")
    Dim s As Date: s = ISODATE("2010-07-23T21:04:48+01:00")
    AssertEqual "Winter/Summer hours", Hour(w), Hour(s)

    MsgBox "All tests passed succesfully!"
End Sub

Sub AssertEqual(name, x, y)
    If x <> y Then Err.Raise 1234, Description:="Failed: " & name & ": '" & x & "' <> '" & y & "'"
End Sub

2

나는 그것이 VB 모듈만큼 우아하지 않다는 것을 알고 있지만 누군가 '+'이후의 시간대를 고려하는 빠른 공식을 찾고 있다면 이것이 될 수 있습니다.

= DATEVALUE(MID(D3,1,10))+TIMEVALUE(MID(D3,12,5))+TIME(MID(D3,18,2),0,0)

바뀔 것이다

2017-12-01T11:03+1100

...에

2/12/2017 07:03:00 AM

(시간대를 고려한 현지 시간)

분명히, u는 밀리 초를 얻었거나 + 후에 더 긴 시간이 있으면 다른 트리밍 섹션의 길이를 수정할 수 있습니다.

sigpwned시간대를 무시 하려면 공식을 사용하십시오 .


2

애플리케이션 용 VB없이이 작업을 수행 할 수 있습니다.

예를 들어 다음을 구문 분석합니다.

2011-01-01T12:00:00+05:00
2011-01-01T12:00:00-05:00

하다:

=IF(MID(A1,20,1)="+",TIMEVALUE(MID(A1,21,5))+DATEVALUE(LEFT(A1,10))+TIMEVALUE(MID(A1,12,8)),-TIMEVALUE(MID(A1,21,5))+DATEVALUE(LEFT(A1,10))+TIMEVALUE(MID(A1,12,8)))

에 대한

2011-01-01T12:00:00Z

하다:

=DATEVALUE(LEFT(A1,10))+TIMEVALUE(MID(A1,12,8))

에 대한

2011-01-01

하다:

=DATEVALUE(LEFT(A1,10))

그러나 상위 날짜 형식은 Excel에서 자동으로 구문 분석됩니다.

그런 다음 날짜 및 시간 형식을 지정할 수있는 Excel 날짜 / 시간 값을 얻습니다.

자세한 정보 및 샘플 파일 : http://blog.hani-ibrahim.de/iso-8601-parsing-in-excel-and-calc.html


불행히도 끝에 Z가있는 솔루션에 대한 링크는 더 이상 존재하지 않습니다. @hani-이 답변이 그 가치를 유지하도록 솔루션을 직접 삽입 하시겠습니까?
luksch

1

내 날짜는 20130221T133551Z (YYYYMMDD'T'HHMMSS'Z ') 형식이므로이 변형을 만들었습니다.

Public Function ISODATEZ(iso As String) As Date
    Dim yearPart As Integer: yearPart = CInt(Mid(iso, 1, 4))
    Dim monPart As Integer: monPart = CInt(Mid(iso, 5, 2))
    Dim dayPart As Integer: dayPart = CInt(Mid(iso, 7, 2))
    Dim hourPart As Integer: hourPart = CInt(Mid(iso, 10, 2))
    Dim minPart As Integer: minPart = CInt(Mid(iso, 12, 2))
    Dim secPart As Integer: secPart = CInt(Mid(iso, 14, 2))
    Dim tz As String: tz = Mid(iso, 16)

    Dim dt As Date: dt = DateSerial(yearPart, monPart, dayPart) + TimeSerial(hourPart, minPart, secPart)

    ' Add the timezone
    If tz <> "" And Left(tz, 1) <> "Z" Then
        Dim colonPos As Integer: colonPos = InStr(tz, ":")
        If colonPos = 0 Then colonPos = Len(tz) + 1

        Dim minutes As Integer: minutes = CInt(Mid(tz, 2, colonPos - 2)) * 60 + CInt(Mid(tz, colonPos + 1))
        If Left(tz, 1) = "+" Then minutes = -minutes
        dt = DateAdd("n", minutes, dt)
    End If

    ' Return value is the ISO8601 date in the local time zone
    ' dt = UTCToLocalTime(dt)
    ISODATEZ = dt
End Function

(시간대 변환은 테스트되지 않았으며 예기치 않은 입력의 경우 오류 처리가 없습니다)


0

특정 (고정) 형식 만 UTC로 변환하는 것으로 충분하면 간단한 VBA 함수 또는 수식을 작성할 수 있습니다.

아래 함수 / 공식은 이러한 형식에 대해 작동합니다 (밀리 초는 생략 됨).

2011-01-01T12:00:00.053+0500
2011-01-01T12:00:00.05381+0500

VBA 기능

가독성을 높이기 위해 더 길게 :

Public Function CDateUTC(dISO As String) As Date

  Dim d, t, tz As String
  Dim tzInt As Integer
  Dim dLocal As Date

  d = Left(dISO, 10)
  t = Mid(dISO, 12, 8)
  tz = Right(dISO, 5)
  tzInt = - CInt(tz) \ 100
  dLocal = CDate(d & " " & t)

  CDateUTC = DateAdd("h", tzInt, dLocal)    

End Function

... 또는 "oneliner":

Public Function CDateUTC(dISO As String) As Date
  CDateUTC = DateAdd("h", -CInt(Right(dISO, 5)) \ 100, CDate(Left(dISO, 10) & " " & Mid(dISO, 12, 8)))    
End Function

공식

=DATEVALUE(LEFT([@ISO], 10)) + TIMEVALUE(MID([@ISO], 12, 8)) - VALUE(RIGHT([@ISO], 5)/100)/24

[@ISO] ISO8601 형식의 현지 시간으로 날짜 / 시간을 포함하는 셀 (테이블 내)입니다.

둘 다 새로운 날짜 / 시간 유형 값을 생성합니다. 필요에 따라 기능을 자유롭게 조정하십시오 (특정 날짜 / 시간 형식).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.