Excel'de bir ISO8601 tarih / saatini (TimeZone dahil) ayrıştırma


85

Excel / VBA'da dahil edilen bir saat dilimiyle (harici bir kaynaktan) bir ISO8601 tarih / saat biçimini normal bir Excel Tarihine ayrıştırmam gerekiyor. Anlayabildiğim kadarıyla, Excel XP'nin (kullandığımız şey) bu yerleşik için bir rutini yok, bu yüzden ayrıştırma için özel bir VBA işlevine bakıyorum sanırım.

ISO8601 veri saatleri şunlardan birine benzer:

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
Şimdi 2020 ve Office 365 aracılığıyla Excel'in en son sürümü , başka türlü genişleyen formül kitaplığında hala basit bir TryParseExactDate( "yyyy-MM-dd'T'HH:mm:ss", A1 )işleve sahip değil . Microsoft'un bahanesi nedir? :(
Dai

Yanıtlar:


172

Makrolar yerine formüller kullanarak saat dilimi OLMADAN bir ISO zaman damgasını ayrıştırmanın (makul) basit bir yolu vardır. Orijinal göndericinin sorduğu tam olarak bu değil , ancak bu soruyu Excel'de ISO zaman damgalarını ayrıştırmaya çalışırken buldum ve bu çözümü yararlı buldum , bu yüzden burada paylaşacağımı düşündüm.

Aşağıdaki formül, yine saat dilimi OLMADAN bir ISO zaman damgasını ayrıştırır:

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

Bu, tarihi kayan nokta biçiminde üretecektir ve ardından normal Excel biçimlerini kullanarak tarih olarak biçimlendirebilirsiniz.


4
Bunun kabul edilen cevap olmaması garip. Diğerlerinden çok daha basit.
Travis Griggs

6
Ancak bu çözüm, saat dilimi dönüşümünü dikkate almıyor.
Goku

1
Saat dilimi alakasızsa veya tamamen aynıysa, örneğin yerel saat dilimi gibi, bu makul bir alternatiftir.
kevinarpe

5
Bunu değiştirebilir 8a 12bunu gerekiyorsa, milisaniye dahil etmek ve girişinizi bunu içerir.
gilly3

3
Bunu zaman kodunu dönüştürmek için kullandım. HH: MM farkını son kısma koyun ve saat dilimine bağlı olarak toplayın veya çıkarın. Benim durumumda 6 saat gerideyim, bu yüzden onu çıkarıyorum. =DATEVALUE(MID(C2,1,10))+TIMEVALUE(MID(C2,12,8))-TIMEVALUE("6:00")
chaiboy

44

Çoğu Google, hiçbir şey ortaya çıkarmadı, bu yüzden kendi rutinimi yazıyorum. İleride başvurmak üzere buraya postalamak:

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

Sistemimde PtrSafeher birinin önüne eklemek zorunda kaldım Declare.
Raman

1
Evet, bu çalışmıyor. Dim d8 As Date: d8 = ISODATE("2020-01-02T16:46:00")2 Ocak için geçerli bir ISO tarihi olan bir test eklerseniz , 1 Şubat'ı döndürür ... Testleriniz çok iyimser.
Liam

5

Bunu bir yorum olarak gönderirdim, ancak yeterli temsilcim yok - üzgünüm !. Bu benim için gerçekten yararlı oldu - teşekkürler rix0rrr, ancak UTCToLocalTime işlevinin, tarihi oluştururken bölgesel ayarları hesaba katması gerektiğini fark ettim. İşte Birleşik Krallık'ta kullandığım sürüm - wDay ve wMonth sıralarının tersine çevrildiğine dikkat edin:

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

Yazarın, alan sıralamasında tutarlı olan ISO8601 formatlı tarih saat dizelerini sorduğunu belirtmek isterim. Elbette sizin verilerinizin verileriniz için çalışması harika, ancak başka biri bunu okursa ve kafası karışırsa, en.wikipedia.org/wiki/ISO_8601 ve ayrıca xkcd.com/1179'a göz atmalısınız .
Hovis Biddle

2
Whoa! Geçmişten patlama. Her neyse, ISO tarihinin alan sırasını değiştirmedim. Bu var yerel yerel kuralları takip etmek gerekiyor sürümü. İdeal ... Kod yararlanırız gerekirdi, ama bu İngiltere'de kullanılmak üzere demiştiniz
dsl101

2

Cevap tarafından rix0rrr harika ama o kolon olmaksızın ya da sadece saat ile saat dilimi uzaklıklar desteklemez. Bu formatlar için destek eklemek için işlevi biraz geliştirdim:

'---------------------------------------------------------------------
' 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 modülü kadar zarif olmadığını biliyorum, ancak biri '+' dan sonra saat dilimini de dikkate alan hızlı bir formül arıyorsanız, o zaman bu olabilir.

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

değişecek

2017-12-01T11:03+1100

-e

2/12/2017 07:03:00 AM

(yerel saat, saat dilimi dikkate alınarak)

Açıkçası, u da milisaniyeler varsa veya + sonrasında daha uzun zamanınız varsa, farklı kırpma bölümlerinin uzunluğunu değiştirebilirsiniz.

sigpwnedsaat dilimini yok saymak istiyorsanız formülü kullanın .


2

Bunu Uygulamalar için VB olmadan yapabilirsiniz:

Örneğin aşağıdakileri ayrıştırmak için:

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

yapmak:

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

İçin

2011-01-01T12:00:00Z

yapmak:

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

İçin

2011-01-01

yapmak:

=DATEVALUE(LEFT(A1,10))

ancak üst tarih biçimi Excel'in otomatik olarak ayrıştırması gerekir.

Ardından, tarih ve saate göre biçimlendirebileceğiniz bir Excel tarih / saat değeri alırsınız.

Ayrıntılı bilgi ve örnek dosyalar için: http://blog.hani-ibrahim.de/iso-8601-parsing-in-excel-and-calc.html


Maalesef sonunda Z ile çözüm için bağlantı artık yok. @hani - bu cevabın değerini koruması için çözümü doğrudan eklemek ister misiniz?
luksch

1

Tarihlerim 20130221T133551Z (YYYYAAGG'T'HHMMSS'Z ') biçimindedir, bu yüzden bu varyantı oluşturdum:

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

(saat dilimi dönüştürme test edilmez ve beklenmedik bir girdi olması durumunda herhangi bir hata işleme yoktur)


0

Yalnızca belirli (sabit) biçimleri UTC'ye dönüştürmeniz yeterliyse, basit bir VBA işlevi veya formülü yazabilirsiniz.

Aşağıdaki işlev / formül bu biçimler için çalışacaktır (milisaniyeler yine de atlanacaktır):

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

VBA işlevi

Daha iyi okunabilirlik için daha uzun:

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

... veya bir "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

Formül

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

[@ISO] ISO8601 biçiminde yerel saatte tarih / saati içeren hücredir (tablo içinde).

Her ikisi de yeni tarih / saat türü değeri oluşturacaktır. İhtiyaçlarınıza göre işlevleri ayarlamaktan çekinmeyin (belirli tarih / saat biçimi).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.