ken_nogi/XVBA/MSS/■MSSツール/ExportICS.bas
Kenichiro NOGI 88a402ce0f up
2026-07-10 18:13:30 +09:00

331 lines
10 KiB
QBasic
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Attribute VB_Name = "ExportICS"
Option Explicit
'=====================================================================
' 予定一覧テーブルからICSを作成し保存する
'=====================================================================
Public Sub ExportScheduleTableToICS()
On Error Goto ErrHandler
Dim lo As ListObject
Set lo = FindListObjectByName("予定一覧")
If lo Is Nothing Then
Set lo = FindListObjectByName("登録予定一覧")
End If
If lo Is Nothing Then
MsgBox "テーブル「予定一覧」が見つかりません。", vbExclamation
Exit Sub
End If
If lo.DataBodyRange Is Nothing Then
MsgBox "テーブル「予定一覧」にデータがありません。", vbInformation
Exit Sub
End If
Dim idxStartDate As Long
Dim idxStartTime As Long
Dim idxEndDate As Long
Dim idxEndTime As Long
Dim idxAllDay As Long
Dim idxTitle As Long
Dim idxDesc As Long
Dim idxCheck As Long
Dim idxOutput As Long
idxStartDate = GetListColumnIndex(lo, "開始日")
idxStartTime = GetListColumnIndex(lo, "開始時刻")
idxEndDate = GetListColumnIndex(lo, "終了日")
idxEndTime = GetListColumnIndex(lo, "終了時刻")
idxAllDay = GetListColumnIndex(lo, "終日")
idxTitle = GetListColumnIndex(lo, "件名")
idxDesc = GetListColumnIndex(lo, "内容")
idxCheck = GetListColumnIndex(lo, "check")
idxOutput = GetListColumnIndex(lo, "出力")
If idxStartDate = 0 Or idxStartTime = 0 Or idxEndDate = 0 Or idxEndTime = 0 Or _
idxAllDay = 0 Or idxTitle = 0 Or idxDesc = 0 Or idxCheck = 0 Or idxOutput = 0 Then
MsgBox "必要な列が不足しています。列名を確認してください。", vbExclamation
Exit Sub
End If
Dim icsBody As String
Dim rowItem As ListRow
Dim exportedCount As Long
exportedCount = 0
For Each rowItem In lo.ListRows
Dim checkValue As String
Dim outputValue As String
checkValue = CStr(rowItem.Range.Cells(1, idxCheck).Value)
outputValue = CStr(rowItem.Range.Cells(1, idxOutput).Value)
If IsCheckOk(checkValue) And IsOutputBlank(outputValue) Then
Dim ev As String
ev = BuildEventBlock(rowItem, idxStartDate, idxStartTime, idxEndDate, idxEndTime, idxAllDay, idxTitle, idxDesc)
If ev <> "" Then
icsBody = icsBody & ev
rowItem.Range.Cells(1, idxOutput).Value = "済"
exportedCount = exportedCount + 1
End If
End If
Next rowItem
If exportedCount = 0 Then
MsgBox "出力対象check=OK かつ 出力が空欄)のデータがありません。", vbInformation
Exit Sub
End If
Dim icsText As String
icsText = BuildCalendarText(icsBody)
Dim fileName As String
fileName = "予定一括登録_" & Format$(Now, "yyyymmdd_hhnnss") & ".ics"
Dim savePath As String
savePath = ThisWorkbook.Path & Application.PathSeparator & fileName
WriteTextUtf8 savePath, icsText
MsgBox "ICSファイルを出力しました。" & vbCrLf & _
"ファイル: " & fileName & vbCrLf & _
"件数: " & CStr(exportedCount) & "件", vbInformation
Exit Sub
ErrHandler:
MsgBox "ICS出力中にエラーが発生しました。" & vbCrLf & _
"番号: " & Err.Number & vbCrLf & _
"内容: " & Err.Description, vbCritical
End Sub
Private Function BuildCalendarText(Byval eventBlocks As String) As String
Dim txt As String
txt = "BEGIN:VCALENDAR" & vbCrLf & _
"PRODID:-//MyLINEWORKS_CAL_IMPORT//JP" & vbCrLf & _
"VERSION:2.0" & vbCrLf & _
"CALSCALE:GREGORIAN" & vbCrLf & _
"METHOD:PUBLISH" & vbCrLf & _
vbCrLf & _
eventBlocks & _
"END:VCALENDAR" & vbCrLf
BuildCalendarText = txt
End Function
Private Function BuildEventBlock( _
Byval rowItem As ListRow, _
Byval idxStartDate As Long, _
Byval idxStartTime As Long, _
Byval idxEndDate As Long, _
Byval idxEndTime As Long, _
Byval idxAllDay As Long, _
Byval idxTitle As Long, _
Byval idxDesc As Long) As String
On Error Goto BuildErr
Dim startDateValue As Variant
Dim startTimeValue As Variant
Dim endDateValue As Variant
Dim endTimeValue As Variant
Dim allDayValue As Variant
startDateValue = rowItem.Range.Cells(1, idxStartDate).Value
startTimeValue = rowItem.Range.Cells(1, idxStartTime).Value
endDateValue = rowItem.Range.Cells(1, idxEndDate).Value
endTimeValue = rowItem.Range.Cells(1, idxEndTime).Value
allDayValue = rowItem.Range.Cells(1, idxAllDay).Value
If Not IsDate(startDateValue) Or Not IsDate(endDateValue) Then
BuildEventBlock = ""
Exit Function
End If
Dim startDate As Date
Dim endDate As Date
startDate = DateValue(CDate(startDateValue))
endDate = DateValue(CDate(endDateValue))
Dim isAllDay As Boolean
isAllDay = (Val(CStr(allDayValue)) = 1)
Dim lines As String
lines = "BEGIN:VEVENT" & vbCrLf
If isAllDay Then
lines = lines & "DTSTART;VALUE=DATE:" & Format$(startDate, "yyyymmdd") & vbCrLf
lines = lines & "DTEND;VALUE=DATE:" & Format$(DateAdd("d", 1, endDate), "yyyymmdd") & vbCrLf
Else
If Not CanParseTime(startTimeValue) Or Not CanParseTime(endTimeValue) Then
BuildEventBlock = ""
Exit Function
End If
Dim localStart As Date
Dim localEnd As Date
localStart = DateValue(startDate) + TimeValue(CDate(startTimeValue))
localEnd = DateValue(endDate) + TimeValue(CDate(endTimeValue))
Dim utcStart As Date
Dim utcEnd As Date
utcStart = LocalToUtc(localStart)
utcEnd = LocalToUtc(localEnd)
lines = lines & "DTSTART:" & Format$(utcStart, "yyyymmdd") & "T" & Format$(utcStart, "hhnnss") & "Z" & vbCrLf
lines = lines & "DTEND:" & Format$(utcEnd, "yyyymmdd") & "T" & Format$(utcEnd, "hhnnss") & "Z" & vbCrLf
End If
Dim titleText As String
titleText = Trim$(CStr(rowItem.Range.Cells(1, idxTitle).Value))
If titleText <> "" Then
lines = lines & "SUMMARY:" & EscapeIcsText(titleText) & vbCrLf
End If
Dim descText As String
descText = CStr(rowItem.Range.Cells(1, idxDesc).Value)
If Trim$(descText) <> "" Then
lines = lines & "DESCRIPTION:" & EscapeIcsDescriptionText(descText) & vbCrLf
End If
lines = lines & "END:VEVENT" & vbCrLf
BuildEventBlock = lines
Exit Function
BuildErr:
BuildEventBlock = ""
End Function
Private Function EscapeIcsText(Byval txt As String) As String
Dim s As String
s = txt
s = Replace(s, "\", "\\")
s = Replace(s, ";", "\;")
s = Replace(s, ",", "\,")
EscapeIcsText = s
End Function
Private Function EscapeIcsDescriptionText(Byval txt As String) As String
Const NL_TOKEN As String = "__ICS_NEWLINE_TOKEN__"
Dim s As String
s = NormalizeLineBreak(txt)
s = Replace(s, vbCrLf, NL_TOKEN)
s = Replace(s, vbLf, NL_TOKEN)
s = Replace(s, vbCr, NL_TOKEN)
s = EscapeIcsText(s)
s = Replace(s, NL_TOKEN, "\n")
EscapeIcsDescriptionText = s
End Function
Private Function NormalizeLineBreak(Byval txt As String) As String
Dim s As String
s = Replace(txt, vbCrLf, vbLf)
s = Replace(s, vbCr, vbLf)
NormalizeLineBreak = Replace(s, vbLf, vbCrLf)
End Function
Private Function IsCheckOk(Byval rawValue As String) As Boolean
Dim s As String
s = NormalizeText(rawValue)
IsCheckOk = (s = "OK")
End Function
Private Function IsOutputBlank(Byval rawValue As String) As Boolean
Dim s As String
s = NormalizeText(rawValue)
IsOutputBlank = (s = "")
End Function
Private Function NormalizeText(Byval txt As String) As String
Dim s As String
s = txt
s = Replace(s, Chr$(160), " ")
s = Replace(s, ChrW$(12288), " ")
s = Replace(s, vbTab, " ")
s = Trim$(s)
On Error Resume Next
s = StrConv(s, vbNarrow)
On Error Goto 0
NormalizeText = UCase$(s)
End Function
Private Function CanParseTime(Byval v As Variant) As Boolean
On Error Goto ParseErr
Dim t As Date
t = TimeValue(CDate(v))
CanParseTime = True
Exit Function
ParseErr:
CanParseTime = False
End Function
Private Function FindListObjectByName(Byval tableName As String) As ListObject
Dim ws As Worksheet
Dim lo As ListObject
For Each ws In ThisWorkbook.Worksheets
For Each lo In ws.ListObjects
If StrComp(lo.Name, tableName, vbTextCompare) = 0 Then
Set FindListObjectByName = lo
Exit Function
End If
Next lo
Next ws
End Function
Private Function GetListColumnIndex(Byval lo As ListObject, Byval colName As String) As Long
On Error Goto ColErr
GetListColumnIndex = lo.ListColumns(colName).Index
Exit Function
ColErr:
GetListColumnIndex = 0
End Function
Private Sub WriteTextUtf8(Byval filePath As String, Byval textContent As String)
Dim stm As Object
Set stm = CreateObject("ADODB.Stream")
With stm
.Type = 2
.Charset = "utf-8"
.Open
.WriteText textContent
.Position = 0
.SaveToFile filePath, 2
.Close
End With
Set stm = Nothing
End Sub
Private Function LocalToUtc(Byval localDateTime As Date) As Date
Dim utcBiasMinutes As Long
utcBiasMinutes = GetCurrentUtcBiasMinutes()
LocalToUtc = DateAdd("n", -utcBiasMinutes, localDateTime)
End Function
Private Function GetCurrentUtcBiasMinutes() As Long
'Win32_TimeZone.Biasは「UTC = Local + Bias(分)」の定義
On Error Goto Fallback
Dim wmi As Object
Dim tzSet As Object
Dim tz As Object
Set wmi = GetObject("winmgmts:\\.\root\cimv2")
Set tzSet = wmi.ExecQuery("Select Bias FROM Win32_TimeZone")
For Each tz In tzSet
GetCurrentUtcBiasMinutes = CLng(tz.Bias)
Exit Function
Next tz
Fallback:
'取得できない場合はJST(UTC+9)相当を既定値とする
GetCurrentUtcBiasMinutes = -540
End Function