Updated LibreOffice Macro to include timestamp

This commit is contained in:
Rufus King 2026-07-27 20:32:25 -04:00
parent 9d0d4271bd
commit bf0f376cdb

View file

@ -2,17 +2,18 @@
' StockFunctions.bas ' StockFunctions.bas
' Collabora / LibreOffice Basic port of the ONLYOFFICE custom functions. ' Collabora / LibreOffice Basic port of the ONLYOFFICE custom functions.
' '
' UPDATED 2026-07-23: STOCKPRICE / STOCKTIME (Finnhub) removed. ' UPDATED 2026-07-27: FUNDTIME now returns Yahoo's last market update
' Finnhub was dropped because it has no historical price coverage, which is ' date and time as a combined Calc date/time value.
' why stockproxy (NAS08) was built in the first place. FUNDPRICE already
' works for any ticker stockproxy knows about, fund or stock, so it now
' doubles as the equity price function. FUNDTIME replaces STOCKTIME and
' returns stockproxy's "resolved_date" field.
' '
' NOTE: resolved_date is a DATE (YYYY-MM-DD), not a time-of-day timestamp. ' FUNDPRICE returns the current price from stockproxy.
' Finnhub's old STOCKTIME gave true time-of-day resolution; this does not. ' FUNDPRICE_HIST returns a historical price from stockproxy.
' If intraday precision is ever needed, stockproxy's /current endpoint ' FUNDTIME returns Yahoo's regularMarketTime as a date/time value.
' would need a real timestamp field added on the backend side. '
' Example FUNDTIME result:
' 2026-07-27 19:58:00
'
' The returned value is a real Calc date/time value, not text, so it can
' be sorted, compared, formatted, or used in date/time calculations.
' '
' HOW TO INSTALL IN COLLABORA: ' HOW TO INSTALL IN COLLABORA:
' 1. Open the sheet in Collabora (collabora.kingdezigns.com). ' 1. Open the sheet in Collabora (collabora.kingdezigns.com).
@ -30,7 +31,7 @@
' =FUNDPRICE(D4) ' =FUNDPRICE(D4)
' =FUNDPRICE_HIST(D4, TEXT($B$3,"yyyy-mm-dd")) ' =FUNDPRICE_HIST(D4, TEXT($B$3,"yyyy-mm-dd"))
' =FUNDPRICE(A5) <- replaces old =STOCKPRICE(A5) ' =FUNDPRICE(A5) <- replaces old =STOCKPRICE(A5)
' =FUNDTIME(A5) <- replaces old =STOCKTIME(A5) ' =FUNDTIME(A5) <- returns Yahoo date/time of latest market update
' ========================================================================= ' =========================================================================
Option Explicit Option Explicit
@ -49,53 +50,105 @@ End Function
Function FUNDPRICE(ticker As String) As Variant Function FUNDPRICE(ticker As String) As Variant
Dim sUrl As String, sJson As String, vPrice As Variant Dim sUrl As String, sJson As String, vPrice As Variant
sUrl = PROXY_BASE() & "/current?ticker=" & EncodeUrl(ticker) & "&key=" & PROXY_SECRET()
```
sUrl = PROXY_BASE() & "/current?ticker=" & EncodeUrl(ticker) & _
"&key=" & PROXY_SECRET()
sJson = HttpGetText(sUrl) sJson = HttpGetText(sUrl)
If sJson = "" Then If sJson = "" Then
FUNDPRICE = "#ERROR" FUNDPRICE = "#ERROR"
Exit Function Exit Function
End If End If
vPrice = JsonNumber(sJson, "price") vPrice = JsonNumber(sJson, "price")
If IsNull(vPrice) Then If IsNull(vPrice) Then
FUNDPRICE = "#N/A" FUNDPRICE = "#N/A"
Else Else
FUNDPRICE = vPrice FUNDPRICE = vPrice
End If End If
```
End Function End Function
Function FUNDPRICE_HIST(ticker As String, dateStr As String) As Variant Function FUNDPRICE_HIST(ticker As String, dateStr As String) As Variant
Dim sUrl As String, sJson As String, vPrice As Variant Dim sUrl As String, sJson As String, vPrice As Variant
```
sUrl = PROXY_BASE() & "/historical?ticker=" & EncodeUrl(ticker) & _ sUrl = PROXY_BASE() & "/historical?ticker=" & EncodeUrl(ticker) & _
"&date=" & EncodeUrl(dateStr) & "&key=" & PROXY_SECRET() "&date=" & EncodeUrl(dateStr) & "&key=" & PROXY_SECRET()
sJson = HttpGetText(sUrl) sJson = HttpGetText(sUrl)
If sJson = "" Then If sJson = "" Then
FUNDPRICE_HIST = "#ERROR" FUNDPRICE_HIST = "#ERROR"
Exit Function Exit Function
End If End If
vPrice = JsonNumber(sJson, "price") vPrice = JsonNumber(sJson, "price")
If IsNull(vPrice) Then If IsNull(vPrice) Then
FUNDPRICE_HIST = "#N/A" FUNDPRICE_HIST = "#N/A"
Else Else
FUNDPRICE_HIST = vPrice FUNDPRICE_HIST = vPrice
End If End If
```
End Function End Function
' Replaces old STOCKTIME(ticker). Returns stockproxy's resolved_date ' Returns Yahoo's latest market update date and time as a real Calc
' (YYYY-MM-DD) — a date, not a time-of-day timestamp. See note at top. ' date/time value.
'
' Example:
' Yahoo JSON:
' "date":"2026-07-27","time":"19:58:00"
'
' FUNDTIME returns:
' 2026-07-27 19:58:00
'
Function FUNDTIME(ticker As String) As Variant Function FUNDTIME(ticker As String) As Variant
Dim sUrl As String, sJson As String, vDate As Variant Dim sUrl As String
sUrl = PROXY_BASE() & "/current?ticker=" & EncodeUrl(ticker) & "&key=" & PROXY_SECRET() Dim sJson As String
Dim vDate As Variant
Dim vTime As Variant
Dim sDateTime As String
Dim oDateTime As Date
```
sUrl = PROXY_BASE() & "/current?ticker=" & EncodeUrl(ticker) & _
"&key=" & PROXY_SECRET()
sJson = HttpGetText(sUrl) sJson = HttpGetText(sUrl)
If sJson = "" Then If sJson = "" Then
FUNDTIME = "#ERROR" FUNDTIME = "#ERROR"
Exit Function Exit Function
End If End If
vDate = JsonString(sJson, "resolved_date")
If IsNull(vDate) Then ' Get the separate date and time values from stockproxy.
vDate = JsonString(sJson, "date")
vTime = JsonString(sJson, "time")
If IsNull(vDate) Or IsNull(vTime) Then
FUNDTIME = "#N/A" FUNDTIME = "#N/A"
Else Exit Function
FUNDTIME = vDate
End If End If
' Combine into a single date/time string.
sDateTime = CStr(vDate) & " " & CStr(vTime)
' Convert to a real Calc date/time value.
On Error GoTo DateError
oDateTime = CDate(sDateTime)
FUNDTIME = oDateTime
Exit Function
```
DateError:
FUNDTIME = "#N/A"
End Function End Function
' ---- Internal helpers ---------------------------------------------------- ' ---- Internal helpers ----------------------------------------------------
@ -105,20 +158,27 @@ Function HttpGetText(sUrl As String) As String
Dim oSFA As Object, oStream As Object, oTextStream As Object Dim oSFA As Object, oStream As Object, oTextStream As Object
Dim sResult As String Dim sResult As String
```
On Error GoTo ErrHandler On Error GoTo ErrHandler
oSFA = createUnoService("com.sun.star.ucb.SimpleFileAccess") oSFA = createUnoService("com.sun.star.ucb.SimpleFileAccess")
oStream = oSFA.openFileRead(sUrl) oStream = oSFA.openFileRead(sUrl)
oTextStream = createUnoService("com.sun.star.io.TextInputStream") oTextStream = createUnoService("com.sun.star.io.TextInputStream")
oTextStream.setInputStream(oStream) oTextStream.setInputStream(oStream)
oTextStream.setEncoding("UTF-8") oTextStream.setEncoding("UTF-8")
sResult = "" sResult = ""
Do While Not oTextStream.isEOF() Do While Not oTextStream.isEOF()
sResult = sResult & oTextStream.readLine() & Chr(10) sResult = sResult & oTextStream.readLine() & Chr(10)
Loop Loop
oTextStream.closeInput() oTextStream.closeInput()
HttpGetText = sResult HttpGetText = sResult
Exit Function Exit Function
```
ErrHandler: ErrHandler:
HttpGetText = "" HttpGetText = ""
@ -131,27 +191,34 @@ Function JsonNumber(sJson As String, sKey As String) As Variant
Dim iPos As Integer, iStart As Integer, iEnd As Integer Dim iPos As Integer, iStart As Integer, iEnd As Integer
Dim sNum As String, cChar As String Dim sNum As String, cChar As String
```
iPos = InStr(sJson, Chr(34) & sKey & Chr(34)) iPos = InStr(sJson, Chr(34) & sKey & Chr(34))
If iPos = 0 Then If iPos = 0 Then
JsonNumber = Null JsonNumber = Null
Exit Function Exit Function
End If End If
iPos = InStr(iPos, sJson, ":") iPos = InStr(iPos, sJson, ":")
If iPos = 0 Then If iPos = 0 Then
JsonNumber = Null JsonNumber = Null
Exit Function Exit Function
End If End If
iStart = iPos + 1 iStart = iPos + 1
Do While iStart <= Len(sJson) And Mid(sJson, iStart, 1) = " " Do While iStart <= Len(sJson) And Mid(sJson, iStart, 1) = " "
iStart = iStart + 1 iStart = iStart + 1
Loop Loop
iEnd = iStart iEnd = iStart
Do While iEnd <= Len(sJson) Do While iEnd <= Len(sJson)
cChar = Mid(sJson, iEnd, 1) cChar = Mid(sJson, iEnd, 1)
If (cChar >= "0" And cChar <= "9") Or cChar = "." Or cChar = "-" Then
If (cChar >= "0" And cChar <= "9") Or _
cChar = "." Or cChar = "-" Then
iEnd = iEnd + 1 iEnd = iEnd + 1
Else Else
Exit Do Exit Do
@ -159,61 +226,89 @@ Function JsonNumber(sJson As String, sKey As String) As Variant
Loop Loop
sNum = Mid(sJson, iStart, iEnd - iStart) sNum = Mid(sJson, iStart, iEnd - iStart)
If Len(sNum) = 0 Then If Len(sNum) = 0 Then
JsonNumber = Null JsonNumber = Null
Else Else
JsonNumber = CDbl(sNum) JsonNumber = CDbl(sNum)
End If End If
```
End Function End Function
' Pulls a quoted STRING value out of a flat JSON string for a given key. ' Pulls a quoted STRING value out of a flat JSON string for a given key.
' e.g. JsonString("{""resolved_date"":""2026-07-23""}", "resolved_date") ' e.g. JsonString("{""resolved_date"":""2026-07-23""}", "resolved_date")
' -> "2026-07-23" ' -> "2026-07-23"
'
' Returns Null if the key isn't found or has no quoted value. ' Returns Null if the key isn't found or has no quoted value.
Function JsonString(sJson As String, sKey As String) As Variant Function JsonString(sJson As String, sKey As String) As Variant
Dim iPos As Integer, iColon As Integer, iQuoteStart As Integer, iQuoteEnd As Integer Dim iPos As Integer
Dim iColon As Integer
Dim iQuoteStart As Integer
Dim iQuoteEnd As Integer
```
iPos = InStr(sJson, Chr(34) & sKey & Chr(34)) iPos = InStr(sJson, Chr(34) & sKey & Chr(34))
If iPos = 0 Then If iPos = 0 Then
JsonString = Null JsonString = Null
Exit Function Exit Function
End If End If
iColon = InStr(iPos, sJson, ":") iColon = InStr(iPos, sJson, ":")
If iColon = 0 Then If iColon = 0 Then
JsonString = Null JsonString = Null
Exit Function Exit Function
End If End If
iQuoteStart = InStr(iColon, sJson, Chr(34)) iQuoteStart = InStr(iColon, sJson, Chr(34))
If iQuoteStart = 0 Then If iQuoteStart = 0 Then
JsonString = Null JsonString = Null
Exit Function Exit Function
End If End If
iQuoteEnd = InStr(iQuoteStart + 1, sJson, Chr(34)) iQuoteEnd = InStr(iQuoteStart + 1, sJson, Chr(34))
If iQuoteEnd = 0 Then If iQuoteEnd = 0 Then
JsonString = Null JsonString = Null
Exit Function Exit Function
End If End If
JsonString = Mid(sJson, iQuoteStart + 1, iQuoteEnd - iQuoteStart - 1) JsonString = Mid(sJson, iQuoteStart + 1, _
iQuoteEnd - iQuoteStart - 1)
```
End Function End Function
' Minimal percent-encoder - sufficient for tickers and yyyy-mm-dd strings. ' Minimal percent-encoder - sufficient for tickers and yyyy-mm-dd strings.
Function EncodeUrl(s As String) As String Function EncodeUrl(s As String) As String
Dim i As Integer, c As String, sOut As String Dim i As Integer, c As String, sOut As String
```
sOut = "" sOut = ""
For i = 1 To Len(s) For i = 1 To Len(s)
c = Mid(s, i, 1) c = Mid(s, i, 1)
If (c >= "A" And c <= "Z") Or (c >= "a" And c <= "z") Or _
(c >= "0" And c <= "9") Or c = "-" Or c = "_" Or c = "." Or c = "~" Then If (c >= "A" And c <= "Z") Or _
(c >= "a" And c <= "z") Or _
(c >= "0" And c <= "9") Or _
c = "-" Or c = "_" Or c = "." Or c = "~" Then
sOut = sOut & c sOut = sOut & c
Else Else
sOut = sOut & "%" & Right("0" & Hex(Asc(c)), 2) sOut = sOut & "%" & Right("0" & Hex(Asc(c)), 2)
End If End If
Next i Next i
EncodeUrl = sOut EncodeUrl = sOut
```
End Function End Function
Sub RefreshPriceSnapshot Sub RefreshPriceSnapshot
@ -222,14 +317,25 @@ Sub RefreshPriceSnapshot
Dim oCell As Object Dim oCell As Object
Dim i As Integer Dim i As Integer
```
oDoc = ThisComponent oDoc = ThisComponent
oSheet = oDoc.Sheets.getByIndex(0) ' adjust index/name if needed oSheet = oDoc.Sheets.getByIndex(0) ' adjust index/name if needed
' Copy P3:Q53 -> R3:S53 as values only ' Copy P3:Q53 -> R3:S53 as values only
For i = 2 To 4 ' rows 3 to 53 (0-indexed: row 3 = index 2) For i = 2 To 4 ' rows 3 to 53 (0-indexed: row 3 = index 2)
oSheet.getCellByPosition(17, i).setValue(oSheet.getCellByPosition(15, i).getValue()) ' P->R (col P=15, R=17)
oSheet.getCellByPosition(18, i).setValue(oSheet.getCellByPosition(16, i).getValue()) ' Q->S (col Q=16, S=18) oSheet.getCellByPosition(17, i).setValue( _
oSheet.getCellByPosition(15, i).getValue()) _
' P->R (col P=15, R=17)
oSheet.getCellByPosition(18, i).setValue( _
oSheet.getCellByPosition(16, i).getValue()) _
' Q->S (col Q=16, S=18)
Next i Next i
MsgBox "Price snapshot refreshed." MsgBox "Price snapshot refreshed."
```
End Sub End Sub