fix(webcal): Fix the issue that some system are not able to load the output ics format.
Closes #16
This commit is contained in:
@@ -17,8 +17,10 @@
|
||||
|
||||
package cn.org.codecrafters.webcal;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@code WebCalendar} class represents a web calendar in iCalendar format.
|
||||
@@ -189,18 +191,22 @@ public final class WebCalendar {
|
||||
* @return the resolved iCalendar string
|
||||
*/
|
||||
public String resolve() {
|
||||
var events = new StringBuilder();
|
||||
var eventBuilder = new StringBuilder();
|
||||
if (!nodes.isEmpty()) {
|
||||
nodes.forEach(item ->
|
||||
events.append(item.setDomainName(domainName)
|
||||
.resolve()));
|
||||
for (var node : nodes) {
|
||||
if (Objects.isNull(node.getDomainName()) || node.getDomainName().isBlank()) {
|
||||
node.setDomainName(this.domainName);
|
||||
}
|
||||
|
||||
eventBuilder.append(node.resolve());
|
||||
}
|
||||
}
|
||||
|
||||
return "BEGIN:" + TAG + "\n" +
|
||||
"PRODID:-//" + companyName + "//" + productName + "//EN\n" +
|
||||
"VERSION:" + version + "\n" +
|
||||
"X-WR-CALNAME:" + name + "\n" +
|
||||
events + "\n" +
|
||||
eventBuilder + "\n" +
|
||||
"END:" + TAG;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
package cn.org.codecrafters.webcal;
|
||||
|
||||
import cn.org.codecrafters.webcal.config.Classification;
|
||||
import cn.org.codecrafters.webcal.config.Formatter;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
@@ -257,30 +259,34 @@ public final class WebCalendarEvent extends WebCalendarNode {
|
||||
*/
|
||||
@Override
|
||||
public String resolve() {
|
||||
return "\nBEGIN:" + TAG + "\n" +
|
||||
"UID:" + Optional.ofNullable(uid).orElse(UUID.randomUUID().toString()) + "@" + domainName + "\n" +
|
||||
Optional.ofNullable(summary).map((item) -> "SUMMARY:" + item + "\n").orElse("") +
|
||||
"DTSTART" + Optional.ofNullable(timezone).map(item -> ";TZID=" + item).orElse("") + ":" + start.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "\n" +
|
||||
var now = LocalDateTime.now().atZone(ZoneId.systemDefault());
|
||||
return MessageFormat.format("""
|
||||
BEGIN:{0}
|
||||
UID:{1}
|
||||
DTSTAMP:{2}
|
||||
DTSTART:{3}
|
||||
DURATION:PT{6}S
|
||||
{4}{5}{7}{8}{9}{10}{11}{12}
|
||||
END:{0}""",
|
||||
TAG, // 0 - tag
|
||||
Optional.ofNullable(uid).orElse(UUID.randomUUID().toString()) + "@" + domainName, // 1 - uid
|
||||
now.format(Formatter.getUtcDatetimeFormatter()), // 2 - dtstamp
|
||||
start.atZone(ZoneId.systemDefault()).format(Formatter.getUtcDatetimeFormatter()), // 3 - start time
|
||||
Optional.ofNullable(summary).map((item) -> "\nSUMMARY:" + item).orElse(""), // 4 - summary
|
||||
Optional.ofNullable(categories)
|
||||
.map((item) -> {
|
||||
if (!item.isEmpty()) {
|
||||
return "CATEGORIES:" + resolveCategories() + "\n";
|
||||
}
|
||||
return null;
|
||||
}).orElse("") +
|
||||
.map((item) -> !item.isEmpty() ? "\nCATEGORIES:" + resolveCategories() : null).orElse(""), // 5 - categories
|
||||
Optional.ofNullable(duration)
|
||||
.map((item) -> "DURATION:PT" + item.getSeconds() + "S\n").orElse("") +
|
||||
Optional.ofNullable(end)
|
||||
.map((item) -> "DTEND" + Optional.ofNullable(timezone).map(tz -> ";TZID=" + tz).orElse("") + ":" +
|
||||
end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "\n").orElse("") +
|
||||
Optional.ofNullable(classification)
|
||||
.map((item) -> "CLASS:" + item.name() + "\n").orElse("") +
|
||||
Optional.ofNullable(comment).map((item) -> "COMMENT:" + item + "\n").orElse("") +
|
||||
Optional.ofNullable(description).map((item) -> "DESCRIPTION:" + item + "\n").orElse("") +
|
||||
Optional.ofNullable(location).map((item) -> "LOCATION:" + item + "\n").orElse("") +
|
||||
Optional.ofNullable(percentComplete).map((item) -> "PERCENT-COMPLETE:" + item + "\n").orElse("") +
|
||||
Optional.ofNullable(priority).map((item) -> "PRIORITY:" + item + "\n").orElse("") +
|
||||
"END:" + TAG + "\n";
|
||||
.map((_duration) -> String.valueOf(_duration.getSeconds()))
|
||||
.orElse(Optional.ofNullable(end)
|
||||
.map((_end) -> String.valueOf(Duration.between(_end, start).getSeconds()))
|
||||
.orElse("0")), // 6 - duration
|
||||
Optional.ofNullable(classification).map((_classification) -> "\nCLASS:" + _classification + "\n").orElse(""), /* 7 - classification */
|
||||
Optional.ofNullable(comment).map((_comment) -> "\nCOMMENT:" + _comment + "\n").orElse(""), /* 8 - comment */
|
||||
Optional.ofNullable(location).map((_location) -> "\nLOCATION:" + _location).orElse("") /* 9 - location */,
|
||||
Optional.ofNullable(percentComplete).map((_percentComplete) -> "\nPERCENT-COMPLETE:" + _percentComplete).orElse("") /* 10 = percentComplete */,
|
||||
Optional.ofNullable(description).map((_description) -> "\nDESCRIPTION:" + _description).orElse("") /* 11 - description */,
|
||||
Optional.ofNullable(priority).map((_priority) -> "\nPRIORITY:" + _priority).orElse("") /* 12 - priority */
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -90,6 +90,10 @@ public abstract sealed class WebCalendarNode
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return this.domainName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the list of categories into a comma-separated string.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 CodeCraftersCN.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.org.codecrafters.webcal.config;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* DatetimeFormatters
|
||||
*
|
||||
* @author Zihlu Wang
|
||||
* @since 21 Sept, 2023
|
||||
*/
|
||||
public final class Formatter {
|
||||
|
||||
public static DateTimeFormatter getUtcDatetimeFormatter() {
|
||||
if (Objects.isNull(utcDateTimeFormatter)) {
|
||||
utcDateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").withZone(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
return utcDateTimeFormatter;
|
||||
}
|
||||
|
||||
// public static DateTimeFormatter getLocalDatetimeFormatter() {
|
||||
// if (Objects.isNull(localDatetimeFormatter)) {
|
||||
// localDatetimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss");
|
||||
// }
|
||||
//
|
||||
// return localDatetimeFormatter;
|
||||
// }
|
||||
|
||||
private static DateTimeFormatter utcDateTimeFormatter;
|
||||
|
||||
// private static DateTimeFormatter localDatetimeFormatter;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user