2021-07-05 19:50:58 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2021-07-08 10:41:20 +00:00
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Linq;
|
2021-07-05 19:50:58 +00:00
|
|
|
using System.Text;
|
2021-07-08 10:41:20 +00:00
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
using NodaTime;
|
2021-07-05 19:50:58 +00:00
|
|
|
|
|
|
|
namespace BlazorApp.Data
|
|
|
|
{
|
|
|
|
public class Event
|
|
|
|
{
|
|
|
|
public string Uid { get; set; }
|
|
|
|
public string Summary { get; set; }
|
|
|
|
public DateTime? DtStart { get; set; }
|
|
|
|
public DateTime? DtEnd { get; set; }
|
|
|
|
public string Description { get; set; }
|
|
|
|
public TimeSpan? Duration { get; set; }
|
|
|
|
|
|
|
|
public List<(string, string, string)> RawProperties { get; } = new();
|
|
|
|
public string ETag { get; set; }
|
|
|
|
public string Href { get; set; }
|
|
|
|
|
2021-07-08 10:41:20 +00:00
|
|
|
public DateTime? CalculatedEnd
|
|
|
|
{
|
|
|
|
get => DtEnd ?? DtStart + Duration;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (Duration != null)
|
|
|
|
Duration = DtStart - value;
|
|
|
|
else
|
|
|
|
DtEnd = value;
|
|
|
|
}
|
|
|
|
}
|
2021-07-05 19:50:58 +00:00
|
|
|
|
|
|
|
private static string _escape(string s)
|
|
|
|
{
|
2021-07-08 10:41:20 +00:00
|
|
|
if (s == null)
|
|
|
|
return null;
|
|
|
|
|
2021-07-05 19:50:58 +00:00
|
|
|
return s
|
|
|
|
.Replace(@"\", @"\\")
|
|
|
|
.Replace(",", @"\,")
|
|
|
|
.Replace(";", @"\;")
|
|
|
|
.Replace("\n", @"\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
public string ToICal()
|
|
|
|
{
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
sb.Append("BEGIN:VCALENDAR\r\n");
|
|
|
|
sb.Append("VERSION:2.0\r\n");
|
|
|
|
sb.Append("PRODID:-//Sijmen//blazor-calendar//EN\r\n");
|
|
|
|
sb.Append("BEGIN:VEVENT\r\n");
|
|
|
|
|
2021-07-08 10:41:20 +00:00
|
|
|
var seenNames = new HashSet<string>();
|
|
|
|
|
2021-07-05 19:50:58 +00:00
|
|
|
foreach (var (name, parameters, oldValue) in RawProperties)
|
|
|
|
{
|
|
|
|
var value = name switch
|
|
|
|
{
|
|
|
|
"SUMMARY" => _escape(Summary),
|
2021-07-08 10:41:20 +00:00
|
|
|
"DESCRIPTION" => _escape(Description),
|
|
|
|
"DTSTART" => FormatDate(parameters, DtStart),
|
|
|
|
"DTEND" => FormatDate(parameters, DtEnd),
|
2021-07-05 19:50:58 +00:00
|
|
|
_ => oldValue
|
|
|
|
};
|
|
|
|
|
2021-07-08 10:41:20 +00:00
|
|
|
if (value != null)
|
|
|
|
{
|
|
|
|
sb.Append(name);
|
|
|
|
if (parameters != null)
|
|
|
|
sb.Append($";{parameters}");
|
|
|
|
sb.Append($":{value}\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
seenNames.Add(name);
|
2021-07-05 19:50:58 +00:00
|
|
|
}
|
|
|
|
|
2021-07-08 10:41:20 +00:00
|
|
|
// If one of the event's properties did not exist yet, add it to the end.
|
|
|
|
if (!seenNames.Contains("SUMMARY") && !string.IsNullOrWhiteSpace(Summary))
|
|
|
|
sb.Append($"SUMMARY:{_escape(Summary)}\r\n");
|
|
|
|
if (!seenNames.Contains("DESCRIPTION") && !string.IsNullOrWhiteSpace(Description))
|
|
|
|
sb.Append($"DESCRIPTION:{_escape(Description)}\r\n");
|
|
|
|
if (!seenNames.Contains("DTSTART") && DtStart != null)
|
|
|
|
sb.Append($"DTSTART:{DtStart?.ToUniversalTime().ToString(Calendar.DateTimeFormat)}\r\n");
|
|
|
|
if (!seenNames.Contains("DTEND") && !seenNames.Contains("DURATION") && CalculatedEnd != null)
|
|
|
|
sb.Append($"DTEND:{DtStart?.ToUniversalTime().ToString(Calendar.DateTimeFormat)}\r\n");
|
|
|
|
|
2021-07-05 19:50:58 +00:00
|
|
|
sb.Append("END:VEVENT\r\n");
|
|
|
|
sb.Append("END:VCALENDAR\r\n");
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
2021-07-08 10:41:20 +00:00
|
|
|
|
|
|
|
private static string FormatDate(string parameters, DateTime? dateTime)
|
|
|
|
{
|
|
|
|
if (dateTime == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
DateTimeZone timezone;
|
|
|
|
if (parameters != null)
|
|
|
|
{
|
|
|
|
// If there's parameters, see if we can find a timezone, or default to UTC.
|
|
|
|
var parameterList = parameters.Split('=');
|
|
|
|
var timezoneIana =
|
|
|
|
Array.Find(parameterList, s => s.StartsWith("TZID="))?[5..] ?? "UTC";
|
|
|
|
timezone = DateTimeZoneProviders.Tzdb[timezoneIana];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If there's no parameters, default to UTC.
|
|
|
|
timezone = DateTimeZone.Utc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the datetime to the used timezone, to avoid changing it.
|
|
|
|
return Instant
|
|
|
|
.FromDateTimeUtc(((DateTime) dateTime).ToUniversalTime())
|
|
|
|
.InZone(timezone)
|
|
|
|
.ToString(Calendar.DateTimeFormat, CultureInfo.InvariantCulture);
|
|
|
|
}
|
2021-07-05 19:50:58 +00:00
|
|
|
}
|
|
|
|
}
|