Advertisement
actorcat

Crypto Prices

Apr 13th, 2024 (edited)
723
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. '''must change module name to JsonConverter
  2. '''must reference Microsoft Scripting Runtime, Microsoft Internet Controls, Microsoft HTML Object Library...
  3. '''https://www.etsy.com/shop/ExcelByActorkitten
  4.  
  5.  
  6. '''COPY SUB AND PASTE INTO ANOTHER MODULE THEN UNCOMMENT...
  7. 'Sub Crypto_Prices()
  8. '''put crypto on column A,,, BTC, ETH, DOGE,,, ETC...
  9. '''put currency on row 1,,, USD, CAD, JPY,,, ETC...
  10. 'Dim strURL As String, strJSON As String, strTicker As String, strCurrency As String, strLength As String
  11. 'Dim i As Integer
  12. 'Dim i2 As Integer
  13. 'Dim http As Object
  14. 'Dim Json As Object
  15. 'Dim lastcolumn As Long
  16. 'Dim lastrow As Long
  17. 'With ActiveSheet
  18. '    lastcolumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
  19. '    lastrow = .Cells(Rows.Count, 1).End(xlUp).Row
  20. 'End With
  21. 'For i = 2 To lastrow
  22. '    For i2 = 2 To lastcolumn
  23. '    strCurrency = Cells(1, i2).Value
  24. '    strTicker = Cells(i, 1).Value
  25. '    strURL = "https://min-api.cryptocompare.com/data/histoday?fsym=" & strTicker & "&tsym=" & strCurrency & "&limit=60&aggregate=3&e=CCCAGG"
  26. '    Set http = CreateObject("MSXML2.XMLHTTP")
  27. '    http.Open "GET", strURL, False
  28. '    http.Send
  29. '    strJSON = http.responsetext
  30.  
  31. '''Debug.Print http.responsetext: Stop
  32.  
  33. '    Set Json = JsonConverter.ParseJson(strJSON)
  34. '    On Error Resume Next
  35. '    Cells(i, i2).Value = Json("Data")(61)("close")
  36. 'If Err.Number = 13 Then MsgBox strTicker & " error"
  37. '    DoEvents
  38. '    Next i2
  39. 'DoEvents
  40. 'Next i
  41. 'End Sub
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49. ' VBA-JSON v2.3.1
  50. ' (c) Tim Hall - https://github.com/VBA-tools/VBA-JSON
  51. '
  52. ' JSON Converter for VBA
  53. '
  54. ' Errors:
  55. ' 10001 - JSON parse error
  56. '
  57. ' @class JsonConverter
  58. ' @author tim.hall.engr@gmail.com
  59. ' @license MIT (http://www.opensource.org/licenses/mit-license.php)
  60. '' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
  61. '
  62. ' Based originally on vba-json (with extensive changes)
  63. ' BSD license included below
  64. '
  65. ' JSONLib, http://code.google.com/p/vba-json/
  66. '
  67. ' Copyright (c) 2013, Ryo Yokoyama
  68. ' All rights reserved.
  69. '
  70. ' Redistribution and use in source and binary forms, with or without
  71. ' modification, are permitted provided that the following conditions are met:
  72. '     * Redistributions of source code must retain the above copyright
  73. '       notice, this list of conditions and the following disclaimer.
  74. '     * Redistributions in binary form must reproduce the above copyright
  75. '       notice, this list of conditions and the following disclaimer in the
  76. '       documentation and/or other materials provided with the distribution.
  77. '     * Neither the name of the <organization> nor the
  78. '       names of its contributors may be used to endorse or promote products
  79. '       derived from this software without specific prior written permission.
  80. '
  81. ' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  82. ' ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  83. ' WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  84. ' DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  85. ' DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  86. ' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  87. ' LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  88. ' ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  89. ' (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  90. ' SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  91. ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
  92. Option Explicit
  93.  
  94. ' === VBA-UTC Headers
  95. #If Mac Then
  96.  
  97. #If VBA7 Then
  98.  
  99. ' 64-bit Mac (2016)
  100. Private Declare PtrSafe Function utc_popen Lib "/usr/lib/libc.dylib" Alias "popen" _
  101.     (ByVal utc_Command As String, ByVal utc_Mode As String) As LongPtr
  102. Private Declare PtrSafe Function utc_pclose Lib "/usr/lib/libc.dylib" Alias "pclose" _
  103.     (ByVal utc_File As LongPtr) As LongPtr
  104. Private Declare PtrSafe Function utc_fread Lib "/usr/lib/libc.dylib" Alias "fread" _
  105.     (ByVal utc_Buffer As String, ByVal utc_Size As LongPtr, ByVal utc_Number As LongPtr, ByVal utc_File As LongPtr) As LongPtr
  106. Private Declare PtrSafe Function utc_feof Lib "/usr/lib/libc.dylib" Alias "feof" _
  107.     (ByVal utc_File As LongPtr) As LongPtr
  108.  
  109. #Else
  110.  
  111. ' 32-bit Mac
  112. Private Declare Function utc_popen Lib "libc.dylib" Alias "popen" _
  113.     (ByVal utc_Command As String, ByVal utc_Mode As String) As Long
  114. Private Declare Function utc_pclose Lib "libc.dylib" Alias "pclose" _
  115.     (ByVal utc_File As Long) As Long
  116. Private Declare Function utc_fread Lib "libc.dylib" Alias "fread" _
  117.     (ByVal utc_Buffer As String, ByVal utc_Size As Long, ByVal utc_Number As Long, ByVal utc_File As Long) As Long
  118. Private Declare Function utc_feof Lib "libc.dylib" Alias "feof" _
  119.     (ByVal utc_File As Long) As Long
  120.  
  121. #End If
  122.  
  123. #ElseIf VBA7 Then
  124.  
  125. ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724421.aspx
  126. ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms724949.aspx
  127. ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms725485.aspx
  128. Private Declare PtrSafe Function utc_GetTimeZoneInformation Lib "kernel32" Alias "GetTimeZoneInformation" _
  129.     (utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long
  130. Private Declare PtrSafe Function utc_SystemTimeToTzSpecificLocalTime Lib "kernel32" Alias "SystemTimeToTzSpecificLocalTime" _
  131.     (utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long
  132. Private Declare PtrSafe Function utc_TzSpecificLocalTimeToSystemTime Lib "kernel32" Alias "TzSpecificLocalTimeToSystemTime" _
  133.     (utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long
  134.  
  135. #Else
  136.  
  137. Private Declare Function utc_GetTimeZoneInformation Lib "kernel32" Alias "GetTimeZoneInformation" _
  138.     (utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION) As Long
  139. Private Declare Function utc_SystemTimeToTzSpecificLocalTime Lib "kernel32" Alias "SystemTimeToTzSpecificLocalTime" _
  140.     (utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpUniversalTime As utc_SYSTEMTIME, utc_lpLocalTime As utc_SYSTEMTIME) As Long
  141. Private Declare Function utc_TzSpecificLocalTimeToSystemTime Lib "kernel32" Alias "TzSpecificLocalTimeToSystemTime" _
  142.     (utc_lpTimeZoneInformation As utc_TIME_ZONE_INFORMATION, utc_lpLocalTime As utc_SYSTEMTIME, utc_lpUniversalTime As utc_SYSTEMTIME) As Long
  143.  
  144. #End If
  145.  
  146. #If Mac Then
  147.  
  148. #If VBA7 Then
  149. Private Type utc_ShellResult
  150.     utc_Output As String
  151.     utc_ExitCode As LongPtr
  152. End Type
  153.  
  154. #Else
  155.  
  156. Private Type utc_ShellResult
  157.     utc_Output As String
  158.     utc_ExitCode As Long
  159. End Type
  160.  
  161. #End If
  162.  
  163. #Else
  164.  
  165. Private Type utc_SYSTEMTIME
  166.     utc_wYear As Integer
  167.     utc_wMonth As Integer
  168.     utc_wDayOfWeek As Integer
  169.     utc_wDay As Integer
  170.     utc_wHour As Integer
  171.     utc_wMinute As Integer
  172.     utc_wSecond As Integer
  173.     utc_wMilliseconds As Integer
  174. End Type
  175.  
  176. Private Type utc_TIME_ZONE_INFORMATION
  177.     utc_Bias As Long
  178.     utc_StandardName(0 To 31) As Integer
  179.     utc_StandardDate As utc_SYSTEMTIME
  180.     utc_StandardBias As Long
  181.     utc_DaylightName(0 To 31) As Integer
  182.     utc_DaylightDate As utc_SYSTEMTIME
  183.     utc_DaylightBias As Long
  184. End Type
  185.  
  186. #End If
  187. ' === End VBA-UTC
  188.  
  189. Private Type json_Options
  190.     ' VBA only stores 15 significant digits, so any numbers larger than that are truncated
  191.    ' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits
  192.    ' See: http://support.microsoft.com/kb/269370
  193.    '
  194.    ' By default, VBA-JSON will use String for numbers longer than 15 characters that contain only digits
  195.    ' to override set `JsonConverter.JsonOptions.UseDoubleForLargeNumbers = True`
  196.    UseDoubleForLargeNumbers As Boolean
  197.  
  198.     ' The JSON standard requires object keys to be quoted (" or '), use this option to allow unquoted keys
  199.    AllowUnquotedKeys As Boolean
  200.  
  201.     ' The solidus (/) is not required to be escaped, use this option to escape them as \/ in ConvertToJson
  202.    EscapeSolidus As Boolean
  203. End Type
  204. Public JsonOptions As json_Options
  205.  
  206. ' ============================================= '
  207. ' Public Methods
  208. ' ============================================= '
  209.  
  210. ''
  211. ' Convert JSON string to object (Dictionary/Collection)
  212. '
  213. ' @method ParseJson
  214. ' @param {String} json_String
  215. ' @return {Object} (Dictionary or Collection)
  216. ' @throws 10001 - JSON parse error
  217. ''
  218. Public Function ParseJson(ByVal JsonString As String) As Object
  219.     Dim json_Index As Long
  220.     json_Index = 1
  221.  
  222.     ' Remove vbCr, vbLf, and vbTab from json_String
  223.    JsonString = VBA.Replace(VBA.Replace(VBA.Replace(JsonString, VBA.vbCr, ""), VBA.vbLf, ""), VBA.vbTab, "")
  224.  
  225.     json_SkipSpaces JsonString, json_Index
  226.     Select Case VBA.Mid$(JsonString, json_Index, 1)
  227.     Case "{"
  228.         Set ParseJson = json_ParseObject(JsonString, json_Index)
  229.     Case "["
  230.         Set ParseJson = json_ParseArray(JsonString, json_Index)
  231.     Case Else
  232.         ' Error: Invalid JSON string
  233.        Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(JsonString, json_Index, "Expecting '{' or '['")
  234.     End Select
  235. End Function
  236.  
  237. ''
  238. ' Convert object (Dictionary/Collection/Array) to JSON
  239. '
  240. ' @method ConvertToJson
  241. ' @param {Variant} JsonValue (Dictionary, Collection, or Array)
  242. ' @param {Integer|String} Whitespace "Pretty" print json with given number of spaces per indentation (Integer) or given string
  243. ' @return {String}
  244. ''
  245. Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitespace As Variant, Optional ByVal json_CurrentIndentation As Long = 0) As String
  246.     Dim json_Buffer As String
  247.     Dim json_BufferPosition As Long
  248.     Dim json_BufferLength As Long
  249.     Dim json_Index As Long
  250.     Dim json_LBound As Long
  251.     Dim json_UBound As Long
  252.     Dim json_IsFirstItem As Boolean
  253.     Dim json_Index2D As Long
  254.     Dim json_LBound2D As Long
  255.     Dim json_UBound2D As Long
  256.     Dim json_IsFirstItem2D As Boolean
  257.     Dim json_Key As Variant
  258.     Dim json_Value As Variant
  259.     Dim json_DateStr As String
  260.     Dim json_Converted As String
  261.     Dim json_SkipItem As Boolean
  262.     Dim json_PrettyPrint As Boolean
  263.     Dim json_Indentation As String
  264.     Dim json_InnerIndentation As String
  265.  
  266.     json_LBound = -1
  267.     json_UBound = -1
  268.     json_IsFirstItem = True
  269.     json_LBound2D = -1
  270.     json_UBound2D = -1
  271.     json_IsFirstItem2D = True
  272.     json_PrettyPrint = Not IsMissing(Whitespace)
  273.  
  274.     Select Case VBA.VarType(JsonValue)
  275.     Case VBA.vbNull
  276.         ConvertToJson = "null"
  277.     Case VBA.vbDate
  278.         ' Date
  279.        json_DateStr = ConvertToIso(VBA.CDate(JsonValue))
  280.  
  281.         ConvertToJson = """" & json_DateStr & """"
  282.     Case VBA.vbString
  283.         ' String (or large number encoded as string)
  284.        If Not JsonOptions.UseDoubleForLargeNumbers And json_StringIsLargeNumber(JsonValue) Then
  285.             ConvertToJson = JsonValue
  286.         Else
  287.             ConvertToJson = """" & json_Encode(JsonValue) & """"
  288.         End If
  289.     Case VBA.vbBoolean
  290.         If JsonValue Then
  291.             ConvertToJson = "true"
  292.         Else
  293.             ConvertToJson = "false"
  294.         End If
  295.     Case VBA.vbArray To VBA.vbArray + VBA.vbByte
  296.         If json_PrettyPrint Then
  297.             If VBA.VarType(Whitespace) = VBA.vbString Then
  298.                 json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)
  299.                 json_InnerIndentation = VBA.String$(json_CurrentIndentation + 2, Whitespace)
  300.             Else
  301.                 json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)
  302.                 json_InnerIndentation = VBA.Space$((json_CurrentIndentation + 2) * Whitespace)
  303.             End If
  304.         End If
  305.  
  306.         ' Array
  307.        json_BufferAppend json_Buffer, "[", json_BufferPosition, json_BufferLength
  308.  
  309.         On Error Resume Next
  310.  
  311.         json_LBound = LBound(JsonValue, 1)
  312.         json_UBound = UBound(JsonValue, 1)
  313.         json_LBound2D = LBound(JsonValue, 2)
  314.         json_UBound2D = UBound(JsonValue, 2)
  315.  
  316.         If json_LBound >= 0 And json_UBound >= 0 Then
  317.             For json_Index = json_LBound To json_UBound
  318.                 If json_IsFirstItem Then
  319.                     json_IsFirstItem = False
  320.                 Else
  321.                     ' Append comma to previous line
  322.                    json_BufferAppend json_Buffer, ",", json_BufferPosition, json_BufferLength
  323.                 End If
  324.  
  325.                 If json_LBound2D >= 0 And json_UBound2D >= 0 Then
  326.                     ' 2D Array
  327.                    If json_PrettyPrint Then
  328.                         json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength
  329.                     End If
  330.                     json_BufferAppend json_Buffer, json_Indentation & "[", json_BufferPosition, json_BufferLength
  331.  
  332.                     For json_Index2D = json_LBound2D To json_UBound2D
  333.                         If json_IsFirstItem2D Then
  334.                             json_IsFirstItem2D = False
  335.                         Else
  336.                             json_BufferAppend json_Buffer, ",", json_BufferPosition, json_BufferLength
  337.                         End If
  338.  
  339.                         json_Converted = ConvertToJson(JsonValue(json_Index, json_Index2D), Whitespace, json_CurrentIndentation + 2)
  340.  
  341.                         ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null
  342.                        If json_Converted = "" Then
  343.                             ' (nest to only check if converted = "")
  344.                            If json_IsUndefined(JsonValue(json_Index, json_Index2D)) Then
  345.                                 json_Converted = "null"
  346.                             End If
  347.                         End If
  348.  
  349.                         If json_PrettyPrint Then
  350.                             json_Converted = vbNewLine & json_InnerIndentation & json_Converted
  351.                         End If
  352.  
  353.                         json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength
  354.                     Next json_Index2D
  355.  
  356.                     If json_PrettyPrint Then
  357.                         json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength
  358.                     End If
  359.  
  360.                     json_BufferAppend json_Buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
  361.                     json_IsFirstItem2D = True
  362.                 Else
  363.                     ' 1D Array
  364.                    json_Converted = ConvertToJson(JsonValue(json_Index), Whitespace, json_CurrentIndentation + 1)
  365.  
  366.                     ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null
  367.                    If json_Converted = "" Then
  368.                         ' (nest to only check if converted = "")
  369.                        If json_IsUndefined(JsonValue(json_Index)) Then
  370.                             json_Converted = "null"
  371.                         End If
  372.                     End If
  373.  
  374.                     If json_PrettyPrint Then
  375.                         json_Converted = vbNewLine & json_Indentation & json_Converted
  376.                     End If
  377.  
  378.                     json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength
  379.                 End If
  380.             Next json_Index
  381.         End If
  382.  
  383.         On Error GoTo 0
  384.  
  385.         If json_PrettyPrint Then
  386.             json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength
  387.  
  388.             If VBA.VarType(Whitespace) = VBA.vbString Then
  389.                 json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
  390.             Else
  391.                 json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)
  392.             End If
  393.         End If
  394.  
  395.         json_BufferAppend json_Buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
  396.  
  397.         ConvertToJson = json_BufferToString(json_Buffer, json_BufferPosition)
  398.  
  399.     ' Dictionary or Collection
  400.    Case VBA.vbObject
  401.         If json_PrettyPrint Then
  402.             If VBA.VarType(Whitespace) = VBA.vbString Then
  403.                 json_Indentation = VBA.String$(json_CurrentIndentation + 1, Whitespace)
  404.             Else
  405.                 json_Indentation = VBA.Space$((json_CurrentIndentation + 1) * Whitespace)
  406.             End If
  407.         End If
  408.  
  409.         ' Dictionary
  410.        If VBA.TypeName(JsonValue) = "Dictionary" Then
  411.             json_BufferAppend json_Buffer, "{", json_BufferPosition, json_BufferLength
  412.             For Each json_Key In JsonValue.Keys
  413.                 ' For Objects, undefined (Empty/Nothing) is not added to object
  414.                json_Converted = ConvertToJson(JsonValue(json_Key), Whitespace, json_CurrentIndentation + 1)
  415.                 If json_Converted = "" Then
  416.                     json_SkipItem = json_IsUndefined(JsonValue(json_Key))
  417.                 Else
  418.                     json_SkipItem = False
  419.                 End If
  420.  
  421.                 If Not json_SkipItem Then
  422.                     If json_IsFirstItem Then
  423.                         json_IsFirstItem = False
  424.                     Else
  425.                         json_BufferAppend json_Buffer, ",", json_BufferPosition, json_BufferLength
  426.                     End If
  427.  
  428.                     If json_PrettyPrint Then
  429.                         json_Converted = vbNewLine & json_Indentation & """" & json_Key & """: " & json_Converted
  430.                     Else
  431.                         json_Converted = """" & json_Key & """:" & json_Converted
  432.                     End If
  433.  
  434.                     json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength
  435.                 End If
  436.             Next json_Key
  437.  
  438.             If json_PrettyPrint Then
  439.                 json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength
  440.  
  441.                 If VBA.VarType(Whitespace) = VBA.vbString Then
  442.                     json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
  443.                 Else
  444.                     json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)
  445.                 End If
  446.             End If
  447.  
  448.             json_BufferAppend json_Buffer, json_Indentation & "}", json_BufferPosition, json_BufferLength
  449.  
  450.         ' Collection
  451.        ElseIf VBA.TypeName(JsonValue) = "Collection" Then
  452.             json_BufferAppend json_Buffer, "[", json_BufferPosition, json_BufferLength
  453.             For Each json_Value In JsonValue
  454.                 If json_IsFirstItem Then
  455.                     json_IsFirstItem = False
  456.                 Else
  457.                     json_BufferAppend json_Buffer, ",", json_BufferPosition, json_BufferLength
  458.                 End If
  459.  
  460.                 json_Converted = ConvertToJson(json_Value, Whitespace, json_CurrentIndentation + 1)
  461.  
  462.                 ' For Arrays/Collections, undefined (Empty/Nothing) is treated as null
  463.                If json_Converted = "" Then
  464.                     ' (nest to only check if converted = "")
  465.                    If json_IsUndefined(json_Value) Then
  466.                         json_Converted = "null"
  467.                     End If
  468.                 End If
  469.  
  470.                 If json_PrettyPrint Then
  471.                     json_Converted = vbNewLine & json_Indentation & json_Converted
  472.                 End If
  473.  
  474.                 json_BufferAppend json_Buffer, json_Converted, json_BufferPosition, json_BufferLength
  475.             Next json_Value
  476.  
  477.             If json_PrettyPrint Then
  478.                 json_BufferAppend json_Buffer, vbNewLine, json_BufferPosition, json_BufferLength
  479.  
  480.                 If VBA.VarType(Whitespace) = VBA.vbString Then
  481.                     json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
  482.                 Else
  483.                     json_Indentation = VBA.Space$(json_CurrentIndentation * Whitespace)
  484.                 End If
  485.             End If
  486.  
  487.             json_BufferAppend json_Buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
  488.         End If
  489.  
  490.         ConvertToJson = json_BufferToString(json_Buffer, json_BufferPosition)
  491.     Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal
  492.         ' Number (use decimals for numbers)
  493.        ConvertToJson = VBA.Replace(JsonValue, ",", ".")
  494.     Case Else
  495.         ' vbEmpty, vbError, vbDataObject, vbByte, vbUserDefinedType
  496.        ' Use VBA's built-in to-string
  497.        On Error Resume Next
  498.         ConvertToJson = JsonValue
  499.         On Error GoTo 0
  500.     End Select
  501. End Function
  502.  
  503. ' ============================================= '
  504. ' Private Functions
  505. ' ============================================= '
  506.  
  507. Private Function json_ParseObject(json_String As String, ByRef json_Index As Long) As Dictionary
  508.     Dim json_Key As String
  509.     Dim json_NextChar As String
  510.  
  511.     Set json_ParseObject = New Dictionary
  512.     json_SkipSpaces json_String, json_Index
  513.     If VBA.Mid$(json_String, json_Index, 1) <> "{" Then
  514.         Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '{'")
  515.     Else
  516.         json_Index = json_Index + 1
  517.  
  518.         Do
  519.             json_SkipSpaces json_String, json_Index
  520.             If VBA.Mid$(json_String, json_Index, 1) = "}" Then
  521.                 json_Index = json_Index + 1
  522.                 Exit Function
  523.             ElseIf VBA.Mid$(json_String, json_Index, 1) = "," Then
  524.                 json_Index = json_Index + 1
  525.                 json_SkipSpaces json_String, json_Index
  526.             End If
  527.  
  528.             json_Key = json_ParseKey(json_String, json_Index)
  529.             json_NextChar = json_Peek(json_String, json_Index)
  530.             If json_NextChar = "[" Or json_NextChar = "{" Then
  531.                 Set json_ParseObject.item(json_Key) = json_ParseValue(json_String, json_Index)
  532.             Else
  533.                 json_ParseObject.item(json_Key) = json_ParseValue(json_String, json_Index)
  534.             End If
  535.         Loop
  536.     End If
  537. End Function
  538.  
  539. Private Function json_ParseArray(json_String As String, ByRef json_Index As Long) As Collection
  540.     Set json_ParseArray = New Collection
  541.  
  542.     json_SkipSpaces json_String, json_Index
  543.     If VBA.Mid$(json_String, json_Index, 1) <> "[" Then
  544.         Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '['")
  545.     Else
  546.         json_Index = json_Index + 1
  547.  
  548.         Do
  549.             json_SkipSpaces json_String, json_Index
  550.             If VBA.Mid$(json_String, json_Index, 1) = "]" Then
  551.                 json_Index = json_Index + 1
  552.                 Exit Function
  553.             ElseIf VBA.Mid$(json_String, json_Index, 1) = "," Then
  554.                 json_Index = json_Index + 1
  555.                 json_SkipSpaces json_String, json_Index
  556.             End If
  557.  
  558.             json_ParseArray.Add json_ParseValue(json_String, json_Index)
  559.         Loop
  560.     End If
  561. End Function
  562.  
  563. Private Function json_ParseValue(json_String As String, ByRef json_Index As Long) As Variant
  564.     json_SkipSpaces json_String, json_Index
  565.     Select Case VBA.Mid$(json_String, json_Index, 1)
  566.     Case "{"
  567.         Set json_ParseValue = json_ParseObject(json_String, json_Index)
  568.     Case "["
  569.         Set json_ParseValue = json_ParseArray(json_String, json_Index)
  570.     Case """", "'"
  571.         json_ParseValue = json_ParseString(json_String, json_Index)
  572.     Case Else
  573.         If VBA.Mid$(json_String, json_Index, 4) = "true" Then
  574.             json_ParseValue = True
  575.             json_Index = json_Index + 4
  576.         ElseIf VBA.Mid$(json_String, json_Index, 5) = "false" Then
  577.             json_ParseValue = False
  578.             json_Index = json_Index + 5
  579.         ElseIf VBA.Mid$(json_String, json_Index, 4) = "null" Then
  580.             json_ParseValue = Null
  581.             json_Index = json_Index + 4
  582.         ElseIf VBA.InStr("+-0123456789", VBA.Mid$(json_String, json_Index, 1)) Then
  583.             json_ParseValue = json_ParseNumber(json_String, json_Index)
  584.         Else
  585.             Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['")
  586.         End If
  587.     End Select
  588. End Function
  589.  
  590. Private Function json_ParseString(json_String As String, ByRef json_Index As Long) As String
  591.     Dim json_Quote As String
  592.     Dim json_Char As String
  593.     Dim json_Code As String
  594.     Dim json_Buffer As String
  595.     Dim json_BufferPosition As Long
  596.     Dim json_BufferLength As Long
  597.  
  598.     json_SkipSpaces json_String, json_Index
  599.  
  600.     ' Store opening quote to look for matching closing quote
  601.    json_Quote = VBA.Mid$(json_String, json_Index, 1)
  602.     json_Index = json_Index + 1
  603.  
  604.     Do While json_Index > 0 And json_Index <= Len(json_String)
  605.         json_Char = VBA.Mid$(json_String, json_Index, 1)
  606.  
  607.         Select Case json_Char
  608.         Case "\"
  609.             ' Escaped string, \\, or \/
  610.            json_Index = json_Index + 1
  611.             json_Char = VBA.Mid$(json_String, json_Index, 1)
  612.  
  613.             Select Case json_Char
  614.             Case """", "\", "/", "'"
  615.                 json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength
  616.                 json_Index = json_Index + 1
  617.             Case "b"
  618.                 json_BufferAppend json_Buffer, vbBack, json_BufferPosition, json_BufferLength
  619.                 json_Index = json_Index + 1
  620.             Case "f"
  621.                 json_BufferAppend json_Buffer, vbFormFeed, json_BufferPosition, json_BufferLength
  622.                 json_Index = json_Index + 1
  623.             Case "n"
  624.                 json_BufferAppend json_Buffer, vbCrLf, json_BufferPosition, json_BufferLength
  625.                 json_Index = json_Index + 1
  626.             Case "r"
  627.                 json_BufferAppend json_Buffer, vbCr, json_BufferPosition, json_BufferLength
  628.                 json_Index = json_Index + 1
  629.             Case "t"
  630.                 json_BufferAppend json_Buffer, vbTab, json_BufferPosition, json_BufferLength
  631.                 json_Index = json_Index + 1
  632.             Case "u"
  633.                 ' Unicode character escape (e.g. \u00a9 = Copyright)
  634.                json_Index = json_Index + 1
  635.                 json_Code = VBA.Mid$(json_String, json_Index, 4)
  636.                 json_BufferAppend json_Buffer, VBA.ChrW(VBA.Val("&h" + json_Code)), json_BufferPosition, json_BufferLength
  637.                 json_Index = json_Index + 4
  638.             End Select
  639.         Case json_Quote
  640.             json_ParseString = json_BufferToString(json_Buffer, json_BufferPosition)
  641.             json_Index = json_Index + 1
  642.             Exit Function
  643.         Case Else
  644.             json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength
  645.             json_Index = json_Index + 1
  646.         End Select
  647.     Loop
  648. End Function
  649.  
  650. Private Function json_ParseNumber(json_String As String, ByRef json_Index As Long) As Variant
  651.     Dim json_Char As String
  652.     Dim json_Value As String
  653.     Dim json_IsLargeNumber As Boolean
  654.  
  655.     json_SkipSpaces json_String, json_Index
  656.  
  657.     Do While json_Index > 0 And json_Index <= Len(json_String)
  658.         json_Char = VBA.Mid$(json_String, json_Index, 1)
  659.  
  660.         If VBA.InStr("+-0123456789.eE", json_Char) Then
  661.             ' Unlikely to have massive number, so use simple append rather than buffer here
  662.            json_Value = json_Value & json_Char
  663.             json_Index = json_Index + 1
  664.         Else
  665.             ' Excel only stores 15 significant digits, so any numbers larger than that are truncated
  666.            ' This can lead to issues when BIGINT's are used (e.g. for Ids or Credit Cards), as they will be invalid above 15 digits
  667.            ' See: http://support.microsoft.com/kb/269370
  668.            '
  669.            ' Fix: Parse -> String, Convert -> String longer than 15/16 characters containing only numbers and decimal points -> Number
  670.            ' (decimal doesn't factor into significant digit count, so if present check for 15 digits + decimal = 16)
  671.            json_IsLargeNumber = IIf(InStr(json_Value, "."), Len(json_Value) >= 17, Len(json_Value) >= 16)
  672.             If Not JsonOptions.UseDoubleForLargeNumbers And json_IsLargeNumber Then
  673.                 json_ParseNumber = json_Value
  674.             Else
  675.                 ' VBA.Val does not use regional settings, so guard for comma is not needed
  676.                json_ParseNumber = VBA.Val(json_Value)
  677.             End If
  678.             Exit Function
  679.         End If
  680.     Loop
  681. End Function
  682.  
  683. Private Function json_ParseKey(json_String As String, ByRef json_Index As Long) As String
  684.     ' Parse key with single or double quotes
  685.    If VBA.Mid$(json_String, json_Index, 1) = """" Or VBA.Mid$(json_String, json_Index, 1) = "'" Then
  686.         json_ParseKey = json_ParseString(json_String, json_Index)
  687.     ElseIf JsonOptions.AllowUnquotedKeys Then
  688.         Dim json_Char As String
  689.         Do While json_Index > 0 And json_Index <= Len(json_String)
  690.             json_Char = VBA.Mid$(json_String, json_Index, 1)
  691.             If (json_Char <> " ") And (json_Char <> ":") Then
  692.                 json_ParseKey = json_ParseKey & json_Char
  693.                 json_Index = json_Index + 1
  694.             Else
  695.                 Exit Do
  696.             End If
  697.         Loop
  698.     Else
  699.         Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting '""' or '''")
  700.     End If
  701.  
  702.     ' Check for colon and skip if present or throw if not present
  703.    json_SkipSpaces json_String, json_Index
  704.     If VBA.Mid$(json_String, json_Index, 1) <> ":" Then
  705.         Err.Raise 10001, "JSONConverter", json_ParseErrorMessage(json_String, json_Index, "Expecting ':'")
  706.     Else
  707.         json_Index = json_Index + 1
  708.     End If
  709. End Function
  710.  
  711. Private Function json_IsUndefined(ByVal json_Value As Variant) As Boolean
  712.     ' Empty / Nothing -> undefined
  713.    Select Case VBA.VarType(json_Value)
  714.     Case VBA.vbEmpty
  715.         json_IsUndefined = True
  716.     Case VBA.vbObject
  717.         Select Case VBA.TypeName(json_Value)
  718.         Case "Empty", "Nothing"
  719.             json_IsUndefined = True
  720.         End Select
  721.     End Select
  722. End Function
  723.  
  724. Private Function json_Encode(ByVal json_Text As Variant) As String
  725.     ' Reference: http://www.ietf.org/rfc/rfc4627.txt
  726.    ' Escape: ", \, /, backspace, form feed, line feed, carriage return, tab
  727.    Dim json_Index As Long
  728.     Dim json_Char As String
  729.     Dim json_AscCode As Long
  730.     Dim json_Buffer As String
  731.     Dim json_BufferPosition As Long
  732.     Dim json_BufferLength As Long
  733.  
  734.     For json_Index = 1 To VBA.Len(json_Text)
  735.         json_Char = VBA.Mid$(json_Text, json_Index, 1)
  736.         json_AscCode = VBA.AscW(json_Char)
  737.  
  738.         ' When AscW returns a negative number, it returns the twos complement form of that number.
  739.        ' To convert the twos complement notation into normal binary notation, add 0xFFF to the return result.
  740.        ' https://support.microsoft.com/en-us/kb/272138
  741.        If json_AscCode < 0 Then
  742.             json_AscCode = json_AscCode + 65536
  743.         End If
  744.  
  745.         ' From spec, ", \, and control characters must be escaped (solidus is optional)
  746.  
  747.         Select Case json_AscCode
  748.         Case 34
  749.             ' " -> 34 -> \"
  750.            json_Char = "\"""
  751.         Case 92
  752.             ' \ -> 92 -> \\
  753.            json_Char = "\\"
  754.         Case 47
  755.             ' / -> 47 -> \/ (optional)
  756.            If JsonOptions.EscapeSolidus Then
  757.                 json_Char = "\/"
  758.             End If
  759.         Case 8
  760.             ' backspace -> 8 -> \b
  761.            json_Char = "\b"
  762.         Case 12
  763.             ' form feed -> 12 -> \f
  764.            json_Char = "\f"
  765.         Case 10
  766.             ' line feed -> 10 -> \n
  767.            json_Char = "\n"
  768.         Case 13
  769.             ' carriage return -> 13 -> \r
  770.            json_Char = "\r"
  771.         Case 9
  772.             ' tab -> 9 -> \t
  773.            json_Char = "\t"
  774.         Case 0 To 31, 127 To 65535
  775.             ' Non-ascii characters -> convert to 4-digit hex
  776.            json_Char = "\u" & VBA.Right$("0000" & VBA.Hex$(json_AscCode), 4)
  777.         End Select
  778.  
  779.         json_BufferAppend json_Buffer, json_Char, json_BufferPosition, json_BufferLength
  780.     Next json_Index
  781.  
  782.     json_Encode = json_BufferToString(json_Buffer, json_BufferPosition)
  783. End Function
  784.  
  785. Private Function json_Peek(json_String As String, ByVal json_Index As Long, Optional json_NumberOfCharacters As Long = 1) As String
  786.     ' "Peek" at the next number of characters without incrementing json_Index (ByVal instead of ByRef)
  787.    json_SkipSpaces json_String, json_Index
  788.     json_Peek = VBA.Mid$(json_String, json_Index, json_NumberOfCharacters)
  789. End Function
  790.  
  791. Private Sub json_SkipSpaces(json_String As String, ByRef json_Index As Long)
  792.     ' Increment index to skip over spaces
  793.    Do While json_Index > 0 And json_Index <= VBA.Len(json_String) And VBA.Mid$(json_String, json_Index, 1) = " "
  794.         json_Index = json_Index + 1
  795.     Loop
  796. End Sub
  797.  
  798. Private Function json_StringIsLargeNumber(json_String As Variant) As Boolean
  799.     ' Check if the given string is considered a "large number"
  800.    ' (See json_ParseNumber)
  801.  
  802.     Dim json_Length As Long
  803.     Dim json_CharIndex As Long
  804.     json_Length = VBA.Len(json_String)
  805.  
  806.     ' Length with be at least 16 characters and assume will be less than 100 characters
  807.    If json_Length >= 16 And json_Length <= 100 Then
  808.         Dim json_CharCode As String
  809.  
  810.         json_StringIsLargeNumber = True
  811.  
  812.         For json_CharIndex = 1 To json_Length
  813.             json_CharCode = VBA.Asc(VBA.Mid$(json_String, json_CharIndex, 1))
  814.             Select Case json_CharCode
  815.             ' Look for .|0-9|E|e
  816.            Case 46, 48 To 57, 69, 101
  817.                 ' Continue through characters
  818.            Case Else
  819.                 json_StringIsLargeNumber = False
  820.                 Exit Function
  821.             End Select
  822.         Next json_CharIndex
  823.     End If
  824. End Function
  825.  
  826. Private Function json_ParseErrorMessage(json_String As String, ByRef json_Index As Long, ErrorMessage As String)
  827.     ' Provide detailed parse error message, including details of where and what occurred
  828.    '
  829.    ' Example:
  830.    ' Error parsing JSON:
  831.    ' {"abcde":True}
  832.    '          ^
  833.    ' Expecting 'STRING', 'NUMBER', null, true, false, '{', or '['
  834.  
  835.     Dim json_StartIndex As Long
  836.     Dim json_StopIndex As Long
  837.  
  838.     ' Include 10 characters before and after error (if possible)
  839.    json_StartIndex = json_Index - 10
  840.     json_StopIndex = json_Index + 10
  841.     If json_StartIndex <= 0 Then
  842.         json_StartIndex = 1
  843.     End If
  844.     If json_StopIndex > VBA.Len(json_String) Then
  845.         json_StopIndex = VBA.Len(json_String)
  846.     End If
  847.  
  848.     json_ParseErrorMessage = "Error parsing JSON:" & VBA.vbNewLine & _
  849.                              VBA.Mid$(json_String, json_StartIndex, json_StopIndex - json_StartIndex + 1) & VBA.vbNewLine & _
  850.                              VBA.Space$(json_Index - json_StartIndex) & "^" & VBA.vbNewLine & _
  851.                              ErrorMessage
  852. End Function
  853.  
  854. Private Sub json_BufferAppend(ByRef json_Buffer As String, _
  855.                               ByRef json_Append As Variant, _
  856.                               ByRef json_BufferPosition As Long, _
  857.                               ByRef json_BufferLength As Long)
  858.     ' VBA can be slow to append strings due to allocating a new string for each append
  859.    ' Instead of using the traditional append, allocate a large empty string and then copy string at append position
  860.    '
  861.    ' Example:
  862.    ' Buffer: "abc  "
  863.    ' Append: "def"
  864.    ' Buffer Position: 3
  865.    ' Buffer Length: 5
  866.    '
  867.    ' Buffer position + Append length > Buffer length -> Append chunk of blank space to buffer
  868.    ' Buffer: "abc       "
  869.    ' Buffer Length: 10
  870.    '
  871.    ' Put "def" into buffer at position 3 (0-based)
  872.    ' Buffer: "abcdef    "
  873.    '
  874.    ' Approach based on cStringBuilder from vbAccelerator
  875.    ' http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.asp
  876.    '
  877.    ' and clsStringAppend from Philip Swannell
  878.    ' https://github.com/VBA-tools/VBA-JSON/pull/82
  879.  
  880.     Dim json_AppendLength As Long
  881.     Dim json_LengthPlusPosition As Long
  882.  
  883.     json_AppendLength = VBA.Len(json_Append)
  884.     json_LengthPlusPosition = json_AppendLength + json_BufferPosition
  885.  
  886.     If json_LengthPlusPosition > json_BufferLength Then
  887.         ' Appending would overflow buffer, add chunk
  888.        ' (double buffer length or append length, whichever is bigger)
  889.        Dim json_AddedLength As Long
  890.         json_AddedLength = IIf(json_AppendLength > json_BufferLength, json_AppendLength, json_BufferLength)
  891.  
  892.         json_Buffer = json_Buffer & VBA.Space$(json_AddedLength)
  893.         json_BufferLength = json_BufferLength + json_AddedLength
  894.     End If
  895.  
  896.     ' Note: Namespacing with VBA.Mid$ doesn't work properly here, throwing compile error:
  897.    ' Function call on left-hand side of assignment must return Variant or Object
  898.    Mid$(json_Buffer, json_BufferPosition + 1, json_AppendLength) = CStr(json_Append)
  899.     json_BufferPosition = json_BufferPosition + json_AppendLength
  900. End Sub
  901.  
  902. Private Function json_BufferToString(ByRef json_Buffer As String, ByVal json_BufferPosition As Long) As String
  903.     If json_BufferPosition > 0 Then
  904.         json_BufferToString = VBA.Left$(json_Buffer, json_BufferPosition)
  905.     End If
  906. End Function
  907.  
  908. ''
  909. ' VBA-UTC v1.0.6
  910. ' (c) Tim Hall - https://github.com/VBA-tools/VBA-UtcConverter
  911. '
  912. ' UTC/ISO 8601 Converter for VBA
  913. '
  914. ' Errors:
  915. ' 10011 - UTC parsing error
  916. ' 10012 - UTC conversion error
  917. ' 10013 - ISO 8601 parsing error
  918. ' 10014 - ISO 8601 conversion error
  919. '
  920. ' @module UtcConverter
  921. ' @author tim.hall.engr@gmail.com
  922. ' @license MIT (http://www.opensource.org/licenses/mit-license.php)
  923. '' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
  924.  
  925. ' (Declarations moved to top)
  926.  
  927. ' ============================================= '
  928. ' Public Methods
  929. ' ============================================= '
  930.  
  931. ''
  932. ' Parse UTC date to local date
  933. '
  934. ' @method ParseUtc
  935. ' @param {Date} UtcDate
  936. ' @return {Date} Local date
  937. ' @throws 10011 - UTC parsing error
  938. ''
  939. Public Function ParseUtc(utc_UtcDate As Date) As Date
  940.     On Error GoTo utc_ErrorHandling
  941.  
  942. #If Mac Then
  943.     ParseUtc = utc_ConvertDate(utc_UtcDate)
  944. #Else
  945.     Dim utc_TimeZoneInfo As utc_TIME_ZONE_INFORMATION
  946.     Dim utc_LocalDate As utc_SYSTEMTIME
  947.  
  948.     utc_GetTimeZoneInformation utc_TimeZoneInfo
  949.     utc_SystemTimeToTzSpecificLocalTime utc_TimeZoneInfo, utc_DateToSystemTime(utc_UtcDate), utc_LocalDate
  950.  
  951.     ParseUtc = utc_SystemTimeToDate(utc_LocalDate)
  952. #End If
  953.  
  954.     Exit Function
  955.  
  956. utc_ErrorHandling:
  957.     Err.Raise 10011, "UtcConverter.ParseUtc", "UTC parsing error: " & Err.Number & " - " & Err.Description
  958. End Function
  959.  
  960. ''
  961. ' Convert local date to UTC date
  962. '
  963. ' @method ConvertToUrc
  964. ' @param {Date} utc_LocalDate
  965. ' @return {Date} UTC date
  966. ' @throws 10012 - UTC conversion error
  967. ''
  968. Public Function ConvertToUtc(utc_LocalDate As Date) As Date
  969.     On Error GoTo utc_ErrorHandling
  970.  
  971. #If Mac Then
  972.     ConvertToUtc = utc_ConvertDate(utc_LocalDate, utc_ConvertToUtc:=True)
  973. #Else
  974.     Dim utc_TimeZoneInfo As utc_TIME_ZONE_INFORMATION
  975.     Dim utc_UtcDate As utc_SYSTEMTIME
  976.  
  977.     utc_GetTimeZoneInformation utc_TimeZoneInfo
  978.     utc_TzSpecificLocalTimeToSystemTime utc_TimeZoneInfo, utc_DateToSystemTime(utc_LocalDate), utc_UtcDate
  979.  
  980.     ConvertToUtc = utc_SystemTimeToDate(utc_UtcDate)
  981. #End If
  982.  
  983.     Exit Function
  984.  
  985. utc_ErrorHandling:
  986.     Err.Raise 10012, "UtcConverter.ConvertToUtc", "UTC conversion error: " & Err.Number & " - " & Err.Description
  987. End Function
  988.  
  989. ''
  990. ' Parse ISO 8601 date string to local date
  991. '
  992. ' @method ParseIso
  993. ' @param {Date} utc_IsoString
  994. ' @return {Date} Local date
  995. ' @throws 10013 - ISO 8601 parsing error
  996. ''
  997. Public Function ParseIso(utc_IsoString As String) As Date
  998.     On Error GoTo utc_ErrorHandling
  999.  
  1000.     Dim utc_Parts() As String
  1001.     Dim utc_DateParts() As String
  1002.     Dim utc_TimeParts() As String
  1003.     Dim utc_OffsetIndex As Long
  1004.     Dim utc_HasOffset As Boolean
  1005.     Dim utc_NegativeOffset As Boolean
  1006.     Dim utc_OffsetParts() As String
  1007.     Dim utc_Offset As Date
  1008.  
  1009.     utc_Parts = VBA.Split(utc_IsoString, "T")
  1010.     utc_DateParts = VBA.Split(utc_Parts(0), "-")
  1011.     ParseIso = VBA.DateSerial(VBA.CInt(utc_DateParts(0)), VBA.CInt(utc_DateParts(1)), VBA.CInt(utc_DateParts(2)))
  1012.  
  1013.     If UBound(utc_Parts) > 0 Then
  1014.         If VBA.InStr(utc_Parts(1), "Z") Then
  1015.             utc_TimeParts = VBA.Split(VBA.Replace(utc_Parts(1), "Z", ""), ":")
  1016.         Else
  1017.             utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), "+")
  1018.             If utc_OffsetIndex = 0 Then
  1019.                 utc_NegativeOffset = True
  1020.                 utc_OffsetIndex = VBA.InStr(1, utc_Parts(1), "-")
  1021.             End If
  1022.  
  1023.             If utc_OffsetIndex > 0 Then
  1024.                 utc_HasOffset = True
  1025.                 utc_TimeParts = VBA.Split(VBA.Left$(utc_Parts(1), utc_OffsetIndex - 1), ":")
  1026.                 utc_OffsetParts = VBA.Split(VBA.Right$(utc_Parts(1), Len(utc_Parts(1)) - utc_OffsetIndex), ":")
  1027.  
  1028.                 Select Case UBound(utc_OffsetParts)
  1029.                 Case 0
  1030.                     utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), 0, 0)
  1031.                 Case 1
  1032.                     utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), VBA.CInt(utc_OffsetParts(1)), 0)
  1033.                 Case 2
  1034.                     ' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues
  1035.                    utc_Offset = TimeSerial(VBA.CInt(utc_OffsetParts(0)), VBA.CInt(utc_OffsetParts(1)), Int(VBA.Val(utc_OffsetParts(2))))
  1036.                 End Select
  1037.  
  1038.                 If utc_NegativeOffset Then: utc_Offset = -utc_Offset
  1039.             Else
  1040.                 utc_TimeParts = VBA.Split(utc_Parts(1), ":")
  1041.             End If
  1042.         End If
  1043.  
  1044.         Select Case UBound(utc_TimeParts)
  1045.         Case 0
  1046.             ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), 0, 0)
  1047.         Case 1
  1048.             ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), 0)
  1049.         Case 2
  1050.             ' VBA.Val does not use regional settings, use for seconds to avoid decimal/comma issues
  1051.            ParseIso = ParseIso + VBA.TimeSerial(VBA.CInt(utc_TimeParts(0)), VBA.CInt(utc_TimeParts(1)), Int(VBA.Val(utc_TimeParts(2))))
  1052.         End Select
  1053.  
  1054.         ParseIso = ParseUtc(ParseIso)
  1055.  
  1056.         If utc_HasOffset Then
  1057.             ParseIso = ParseIso - utc_Offset
  1058.         End If
  1059.     End If
  1060.  
  1061.     Exit Function
  1062.  
  1063. utc_ErrorHandling:
  1064.     Err.Raise 10013, "UtcConverter.ParseIso", "ISO 8601 parsing error for " & utc_IsoString & ": " & Err.Number & " - " & Err.Description
  1065. End Function
  1066.  
  1067. ''
  1068. ' Convert local date to ISO 8601 string
  1069. '
  1070. ' @method ConvertToIso
  1071. ' @param {Date} utc_LocalDate
  1072. ' @return {Date} ISO 8601 string
  1073. ' @throws 10014 - ISO 8601 conversion error
  1074. ''
  1075. Public Function ConvertToIso(utc_LocalDate As Date) As String
  1076.     On Error GoTo utc_ErrorHandling
  1077.  
  1078.     ConvertToIso = VBA.Format$(ConvertToUtc(utc_LocalDate), "yyyy-mm-ddTHH:mm:ss.000Z")
  1079.  
  1080.     Exit Function
  1081.  
  1082. utc_ErrorHandling:
  1083.     Err.Raise 10014, "UtcConverter.ConvertToIso", "ISO 8601 conversion error: " & Err.Number & " - " & Err.Description
  1084. End Function
  1085.  
  1086. ' ============================================= '
  1087. ' Private Functions
  1088. ' ============================================= '
  1089.  
  1090. #If Mac Then
  1091.  
  1092. Private Function utc_ConvertDate(utc_Value As Date, Optional utc_ConvertToUtc As Boolean = False) As Date
  1093.     Dim utc_ShellCommand As String
  1094.     Dim utc_Result As utc_ShellResult
  1095.     Dim utc_Parts() As String
  1096.     Dim utc_DateParts() As String
  1097.     Dim utc_TimeParts() As String
  1098.  
  1099.     If utc_ConvertToUtc Then
  1100.         utc_ShellCommand = "date -ur `date -jf '%Y-%m-%d %H:%M:%S' " & _
  1101.             "'" & VBA.Format$(utc_Value, "yyyy-mm-dd HH:mm:ss") & "' " & _
  1102.             " +'%s'` +'%Y-%m-%d %H:%M:%S'"
  1103.     Else
  1104.         utc_ShellCommand = "date -jf '%Y-%m-%d %H:%M:%S %z' " & _
  1105.             "'" & VBA.Format$(utc_Value, "yyyy-mm-dd HH:mm:ss") & " +0000' " & _
  1106.             "+'%Y-%m-%d %H:%M:%S'"
  1107.     End If
  1108.  
  1109.     utc_Result = utc_ExecuteInShell(utc_ShellCommand)
  1110.  
  1111.     If utc_Result.utc_Output = "" Then
  1112.         Err.Raise 10015, "UtcConverter.utc_ConvertDate", "'date' command failed"
  1113.     Else
  1114.         utc_Parts = Split(utc_Result.utc_Output, " ")
  1115.         utc_DateParts = Split(utc_Parts(0), "-")
  1116.         utc_TimeParts = Split(utc_Parts(1), ":")
  1117.  
  1118.         utc_ConvertDate = DateSerial(utc_DateParts(0), utc_DateParts(1), utc_DateParts(2)) + _
  1119.             TimeSerial(utc_TimeParts(0), utc_TimeParts(1), utc_TimeParts(2))
  1120.     End If
  1121. End Function
  1122.  
  1123. Private Function utc_ExecuteInShell(utc_ShellCommand As String) As utc_ShellResult
  1124. #If VBA7 Then
  1125.     Dim utc_File As LongPtr
  1126.     Dim utc_Read As LongPtr
  1127. #Else
  1128.     Dim utc_File As Long
  1129.     Dim utc_Read As Long
  1130. #End If
  1131.  
  1132.     Dim utc_Chunk As String
  1133.  
  1134.     On Error GoTo utc_ErrorHandling
  1135.     utc_File = utc_popen(utc_ShellCommand, "r")
  1136.  
  1137.     If utc_File = 0 Then: Exit Function
  1138.  
  1139.     Do While utc_feof(utc_File) = 0
  1140.         utc_Chunk = VBA.Space$(50)
  1141.         utc_Read = CLng(utc_fread(utc_Chunk, 1, Len(utc_Chunk) - 1, utc_File))
  1142.         If utc_Read > 0 Then
  1143.             utc_Chunk = VBA.Left$(utc_Chunk, CLng(utc_Read))
  1144.             utc_ExecuteInShell.utc_Output = utc_ExecuteInShell.utc_Output & utc_Chunk
  1145.         End If
  1146.     Loop
  1147.  
  1148. utc_ErrorHandling:
  1149.     utc_ExecuteInShell.utc_ExitCode = CLng(utc_pclose(utc_File))
  1150. End Function
  1151.  
  1152. #Else
  1153.  
  1154. Private Function utc_DateToSystemTime(utc_Value As Date) As utc_SYSTEMTIME
  1155.     utc_DateToSystemTime.utc_wYear = VBA.Year(utc_Value)
  1156.     utc_DateToSystemTime.utc_wMonth = VBA.Month(utc_Value)
  1157.     utc_DateToSystemTime.utc_wDay = VBA.Day(utc_Value)
  1158.     utc_DateToSystemTime.utc_wHour = VBA.Hour(utc_Value)
  1159.     utc_DateToSystemTime.utc_wMinute = VBA.Minute(utc_Value)
  1160.     utc_DateToSystemTime.utc_wSecond = VBA.Second(utc_Value)
  1161.     utc_DateToSystemTime.utc_wMilliseconds = 0
  1162. End Function
  1163.  
  1164. Private Function utc_SystemTimeToDate(utc_Value As utc_SYSTEMTIME) As Date
  1165.     utc_SystemTimeToDate = DateSerial(utc_Value.utc_wYear, utc_Value.utc_wMonth, utc_Value.utc_wDay) + _
  1166.         TimeSerial(utc_Value.utc_wHour, utc_Value.utc_wMinute, utc_Value.utc_wSecond)
  1167. End Function
  1168.  
  1169. #End If
  1170.  
Advertisement
Comments
Add Comment
Please, Sign In to add comment
Advertisement