mangstadt / biweekly Goto Github PK
View Code? Open in Web Editor NEWbiweekly is an iCalendar library written in Java.
License: BSD 2-Clause "Simplified" License
biweekly is an iCalendar library written in Java.
License: BSD 2-Clause "Simplified" License
See #20
I'm curious if it is possible to combine a biweekly.property.RecurrenceRule
, a biweekly.property.RecurrenceDates
, and an biweekly.property.ExceptionDate
and then obtain a DateIterator
(or some variant of it) of their union? The application I am working on will need to account for RDates and ExDates when computing occurrences of an event from a recurrence rule. The getDateIterator
method on biweekly.property.RecurrenceRule
does not appear to handle RecurrenceDates
and ExceptionDates.
The static join
method in com.google.ical.iter.RecurrenceIteratorFactory
would appear to do what I need. Just curious if there is some analogous functionality in the biweekly
package.
Hi @mangstadt, I am new in your library and I'd like to sorry in advance if the question is trivial. I want to select android events and convert them into VEvent
for future processing. I am stuck with converting from string and setting following fields:
CalendarContract.Events.RRULE (String),
CalendarContract.Events.RDATE (String),
CalendarContract.Events.EXDATE (String),
CalendarContract.Events.EXRULE (String)
I need probably these methods from your library
setRecurrenceRule();
addRecurrenceDates();
addExceptionRule();
addExceptionDates();
but I haven't found how to parse their parameters from string. How can I do that with your library?
Thanks for your library and I highly appreciate your help.
The FoldedLineReader removes all whitespace that prepends a folded quoted-printable line instead of just the first whitespace character.
See also: mangstadt/ez-vcard#30
Reported by: mangstadt
Original Ticket: biweekly/tickets/12
Hello,
I'm starting to use the biweekly library, and I want to use the parsed iCalendar data to create an intent that will prompt the user to add the event to its calendar app.
Is there any easy way to do that?
I'm having to get each value and fill the intent specific parameter.
I'm stuck on the RRULE. I think I need to pass a single RAW String to the RRULE android field, like: "FREQ=WEEKLY;COUNT=9;BYDAY=WE,FR"
When I parse the event I can't find a way to get this raw parameter string that was included in the icalendar data. How can I do that without the need to reconstruct the string using each separated parameter value?
Hi,
I am using biweekly to add events on Outlook, it works fine but i can't find any boolean "all day event". Without this boolean, i can't set the related checkbox in outlook even if my dateStart and dateEnd are ok.
Waiting to hear from you,
Yours sincerely
This is a basic question. Unfortunately i couldn't get the required details any where. Does this API provide me the capability to monitor the mailbox for acceptance status of attendees pertaining to calendar invites?
Mike,
I would like to get your comments on a problem I am seeing regarding the Date
returned from a DateIterator
when the event occurs during the Daylight Savings Time (DST) "gap" hour (at least for the Pacific Time Zone) of 02:00 to 02:59. I have written a test shown below that illustrates the problem.
Specifically, for the occurrence on the day DST begins, the Date returned has the time as 01:30 when I believe it should be 03:30. Is my understanding of how the "gap" hour is handled incorrect or is my use of the library incorrect? Or Both? Also listed below is a test that uses Java 8's ZonedDate time for the same set of dates and in it the occurrence that would fall on the "gap" hour is advanced to 03:30.
Thank you in advance for any feedback.
/**
* Output:
* Sun Mar 06 02:30:00 PST 2016
* Sun Mar 13 01:30:00 PST 2016 <-- Should be 03:30?
* Sun Mar 20 02:30:00 PDT 2016
* @throws ParseException
*/
@Test
public void daylightSavingsGapHour() throws ParseException {
final TimeZone pacificTimeZone = TimeZone.getTimeZone("America/Los_Angeles");
// Start date: March 6, 2016 at 02:30 - one week before DST begins
RecurrenceIterator recurrenceIterator = RecurrenceIteratorFactory.createRecurrenceIterator(
"RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=3", new DateTimeValueImpl(2016, 3, 6, 2, 30, 0), pacificTimeZone, true);
DateIterator dateIterator = DateIteratorFactory.createDateIterator(recurrenceIterator);
while (dateIterator.hasNext()) {
Date next = dateIterator.next();
System.out.println(next);
}
}
Here is an example of date-time stamps I think should be given using Java 8's ZonedDateTime:
/**
* Output:
* 2016-03-06T02:30-08:00[America/Los_Angeles]
* 2016-03-13T03:30-07:00[America/Los_Angeles] <--- Time is at 03:30 and in DST (e.g. -07:00 offset)
* 2016-03-20T02:30-07:00[America/Los_Angeles]
*/
@Test
public void gapHourUsingJavaTimePackage() {
// Time is March 6, 2016 at 02:30 (One week before DST begins)
final ZonedDateTime start = ZonedDateTime.of(2016, 3, 6, 2, 30, 0, 0, ZoneId.of("America/Los_Angeles"));
System.out.println(start);
// Add One week
ZonedDateTime march13th2016 = start.plusWeeks(1);
System.out.println(march13th2016);
// Add two weeks
ZonedDateTime march20th2016 = start.plusWeeks(2);
System.out.println(march20th2016);
}
Currently, when biweekly parses an iCalendar object, the object's timezone information is stored separately from the parsed ICalendar
object. The idea behind this was that, because a date/time value can be formatted in any number of timezones, the timezone that a date is formatted in is more of a serialization matter, and should be separated from the "raw" iCalendar data.
For example, if a DTSTART property is formatted in the "America/New_York" timezone (as opposed to UTC time), then the property's date value is parsed according to that timezone and saved to the ICalendar
object as a Java Date
object (which does not retain the timezone that the date was parsed under). The fact that the property was originally formatted in "America/New_York" time is stored in a separate TimezoneInfo
object, obtained by calling ICalReader.getTimezoneInfo()
immediately after parsing the ICalendar
object.
However, this becomes a problem with recurrence rules. They require a timezone in order to properly compute the date/value values they cover. Therefore, it is vitally important that the timezone in the original iCalendar file be preserved and be treated as part of the "raw" iCalendar data.
A possible fix for this problem would be to store the TimezoneInfo
object inside the ICalendar
object. This way, the TimezoneInfo
object is more securely "attached" to its ICalendar
object.
I'm trying to use ICalWriter
on a production host that has no access to internet. When I call icalWriter.getTimezoneInfo().setDefaultTimeZone(tz);
, it indirectly uses TzUrlDotOrgGenerator
, which need access to the internet and thus blocks my code.
One way I thought of fixing this is to obtain the VTimezone
instance on a host with internet access, serialize it, and then de-serialize it on the "real" app code. The main problem is that VTimezone is not serializable :(
I want to be able to parse ICalendar's that use global ID's for timezones rather than a VTIMEZONE component. Whenever I try to parse an ICalendar that is formatted this way, I get this warning: (37): No VTIMEZONE component exists. ID will be treated as an Olsen timezone ID instead. Which is fine. Except that the ParseWarnings object just stores a list of error messages as Strings with no mapping to the error code, so I have to do something like this to ignore the warning:
reader.getWarnings().forEach(warning -> {
if (!warning.startsWith("(37)")) {
errorResponse.addMessage(warning);
}
});
I don't think this is a good way to do this at all, especially because I have seen parse warnings that start with the line number rather than the error code.
Could you make the Import-Package statement for Jackson optional in the bundle? That way Jackson doesn't need to be deployed and the biweekly bundle can still be started. You mention in https://sourceforge.net/p/biweekly/wiki/Maven/ that Jackson is optional. I tried the bundle in version 0.3.4 and that still requires Jackson.
Reported by: kwin
Original Ticket: biweekly/tickets/6
When parsing an iCalendar object, a warning should be generated if a parameter value is non-standard. For example, if the RSVP
parameter contains a value other than "true" or "false".
Discussion: https://sourceforge.net/p/biweekly/discussion/help-and-support/thread/310100a6/
Reported by: mangstadt
Original Ticket: biweekly/tickets/2
When events are published with local timezone info, some clients (Outlook, Google Calendar) do not interpret "until" time of recurring events correctly. Turns out that rfc5545 specification of UNTIL requires: "If specified as a DATE-TIME value, then it MUST be specified in a UTC time format." (see http://tools.ietf.org/html/rfc5545#section-3.3.10 for all the complex details).
I fixed rendering of "until" in RecurrencePropertyScribe to always use UTC, see attached patch. Clients are interpreting my use cases properly now.
Please review and decide whether this fix is generally useful.
Thanks
Peter
Reported by: pmenhart
Original Ticket: biweekly/tickets/10
When I try to run the readme reader code but put in leading spacing on the string literal, rather than an error as I expected, I simply get an empty collection.
I'm not sure if parse is supposed to be able to handle arbitrary streams, but it seems desirable that it throws an error if it gets a malformed stream. Thoughts?
See: https://sourceforge.net/p/biweekly/discussion/help-and-support/thread/4d10e749/
Reported by: mangstadt
Original Ticket: biweekly/tickets/11
Parsing the above iCalendar file takes a very long time. Removing the VTIMEZONE component from the file speeds the parse time up significantly, so the issue appears to be with how biweekly is handling this component.
The bottleneck seems to be that, in order to convert the necessary date/time property values to the given timezone, it wants to calculate the daylight savings time switch-over dates for every year since 1600 (this is the start date defined in the VTIMEZONE component). And it repeats this process from scratch for every date/time property that's using the timezone.
The code in question is located in the ICalTimeZone.getObservanceBoundary(int, int, int, int, int, int)
method. In two places, a RecurrenceIterator
object is created and iterated over until the right date is found. Caching the values that this iterator generates may help with performance. It is unclear whether the RecurrenceIterator.advanceTo()
method will help, as this may cause the iterator to skip over the value that it's looking for (in at least one case).
Workaround:
A workaround is to use Olson timezone IDs. This forces biweekly to use Java's integrated timezone definitions, which greatly speeds up the parsing process. To do this, simply prepend every TZID parameter value with a "/", like so:
DTSTART;TZID="/Europe/Istanbul":20160628T110000
If you have no control over how the .ics files are created, this could be accomplished with some simple "find and replace" code on the .ics file contents.
I'm curious why EnumProperty classes like Method aren't Java enums.
It would seem to make it much easier to parse them.
Even not being enums, one can't even do:
switch(method.getValue()) {
case Method.PUBLISH:
}
because Method.PUBLISH is private.
The attached iCalendar file contains an all-day event (the DTSTART is a "date" as opposed to a "date/time"). When outputting its dates generated by a recurrence iterator, the dates have a time component. But they should not have time components because it is an all-day event.
Code:
ICalReader reader = new ICalReader(new File("[email protected]"));
ICalendar ical = reader.readNext();
VEvent event = ical.getEvents().get(1);
System.out.println("Start: " + event.getDateStart().getValue());
DateIterator iterator = event.getDateIterator(TimeZone.getDefault());
int count = 0;
while (iterator.hasNext()) {
if (++count == 10) break;
final Date date = iterator.next();
System.out.println(date);
}
Output:
Start: Mon Jun 06 00:00:00 EDT 2016
Sun Jun 05 20:00:00 EDT 2016
Sun Jun 19 20:00:00 EDT 2016
Sun Jul 03 20:00:00 EDT 2016
Sun Jul 17 20:00:00 EDT 2016
Sun Jul 31 20:00:00 EDT 2016
Sun Aug 14 20:00:00 EDT 2016
Sun Aug 28 20:00:00 EDT 2016
Sun Sep 11 20:00:00 EDT 2016
Sun Sep 25 20:00:00 EDT 2016
Test_obalqpctbh1c4f66r683opue5g@group.calendar.google.com.ics.txt
Not sure if this is the right channel. If not, please indicate which channel to use.
Any example on how to use biweekly with mybatis? Working on an application where biweekly VEvent need to be store in a database.
Thanks.
Reported by: mangstadt
Original Ticket: biweekly/tickets/9
I have multiple VTIMEZONE
objects specified in my calendar, all of which are being used by VEVENTS
. I do not want to specify a timezone for the calendar as a whole, but rather on individual events.
Perhaps I am mistaken, but it seems as though specifying a timezone property on a VEvent
object should cause it to be serialized without a Zulu time. An example, using Scala but should work same in Java:
val timezoneIds = List("America/Seattle")
// Create some timezones
val timezones: List[VTimezone] = buildTimezones(timezoneIds)
// Create a DateStart
val dtStart = new DateStart(startTime)
val dtEnd = new DateEnd(endTime)
// Create timezone params and add to start and end
val params = new ICalParameters()
params.setTimezoneId(timezoneIds(0)) // Example, use the first timezone
dtStart.setParameters(params)
dtEnd.setParameters(params)
// Add the start and end to an event
val event = new VEvent()
event.setProperty(dtStart)
event.setProperty(dtEnd)
val calendar = new iCalendar()
calendar.addEvent(event)
val writer = new ICalWriter(buffer, ICalVersion.V2_0)
// Add the timezone
writer.getTimezoneInfo.assign(timezones(0), TimeZone.getTimeZone(timezoneIds(0))
// Write to a string
val buffer = new ByteArrayOutputStream()
val written = f(writer)
written.flush()
written.close()
val calString = new String(buffer.toByteArray)
// calStrig now contains a VTimezone object, and VEvent object in the format
// DTSTART;TZID=America/Seattle:20160505T150000Z
There is mention in the "Working with Timezones" page about using icalWriter.getTimezoneInfo().setDefaultTimeZone(tz);
to set timezones for the calendar as a whole. I do not need to specify a global timezone as all events have a timezone specified, however if that is the best solution I could specify a bunk timezone to prevent the Zulu time specification.
Hope this makes sense, happy to provide more clarification
The method:
public static DateIterator createDateIterator(
String rdata, Date start, TimeZone tzid, boolean strict)
throws ParseException {
return new RecurrenceIteratorWrapper(
RecurrenceIteratorFactory.createRecurrenceIterator(
rdata, dateToDateValue(start, true),
tzid, strict));
}
claims it will use the timezone to interpret the start
parameter, but it does not actually do that. The GregorianCalendar that is created in dateToDateValue
is hard-coded to UTC, and thus the start
parameter is not properly handled if the tzid is not UTC.
The following unit test shows the issue:
@Test
public void testTimeZoneHandling() throws Exception {
TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
Calendar calendar = Calendar.getInstance(timeZone, Locale.US);
calendar.clear();
calendar.set(2015, Calendar.JANUARY, 1, 0, 0, 0);
Date start = calendar.getTime();
DateIterator dateIterator = DateIteratorFactory.createDateIterator("RRULE:FREQ=DAILY;INTERVAL=1", start, timeZone, true);
Date next = dateIterator.next();
assertEquals(start, next);
}
I believe that the RecurrenceIteratorWrapper and the RecurrenceIterableWrapper need to store the timezone, and use it when converting between internal date formats and java.util.Date
s. The only method I'm not sure about is public static DateIterator createDateIterator(RecurrenceIterator rit)
, which would then require a timezone parameter, although adding one does not work properly.
As a work-around, I'm explicitly using createDateIterator(createRecurrenceIterator(rrule, start, timeZone, true))
and manually building the DateValue using the correct time zone for the start date.
Note: This unit test can be used to verify that public static DateIterator createDateIterator(RecurrenceIterator rit)
continues to work correctly, with any changes that are made.
@Test
public void testTimeZones_viaRecurrenceIterator() throws Exception {
TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
RecurrenceIterator recurrenceIterator = RecurrenceIteratorFactory.createRecurrenceIterator("RRULE:FREQ=DAILY;INTERVAL=1", new DateTimeValueImpl(2015, 1, 1, 0, 0, 1), timeZone, true);
DateIterator dateIterator = DateIteratorFactory.createDateIterator(recurrenceIterator);
Date next = dateIterator.next();
Calendar calendar = Calendar.getInstance(timeZone, Locale.US);
calendar.clear();
calendar.set(2015, Calendar.JANUARY, 1, 0, 0, 1);
Date start = calendar.getTime();
assertEquals(start, next);
}
Outlook.com produces ics files with MAILTO URIs:
ATTENDEE;CUTYPE=INDIVIDUAL;CN=John Doe;ROLE=REQ-PARTICIPAN
T;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:MAILTO:[email protected]
ORGANIZER;CN=John Smith:MAILTO:[email protected]
The biweekly API returns null for the email on the attendee/organizer. If you lower-case the URI, it works as expected:
ATTENDEE;CUTYPE=INDIVIDUAL;CN=John Doe;ROLE=REQ-PARTICIPAN
T;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:[email protected]
ORGANIZER;CN=John Smith:mailto:[email protected]
Is case sensitivity required? The specs I've seen usually document them as upper case. In practice, most clients seem to use lower case (except outlook.com).
Test Case (groovy):
@Test
void testOutlookCalendar() {
def ical = Biweekly.parse(new FileInputStream("test-outlook.ics")).first()
assertEquals("[email protected]", ical.events.first().attendees.first().email)
assertEquals("[email protected]", ical.events.first().organizer.email)
}
See attached file: test-outlook.ics.zip
Add ability to output xCards using XML 1.1.
Reported by: mangstadt
Original Ticket: biweekly/tickets/14
It appears that some data loss can occur when parsing daylight savings start times. For example:
:::java
//daylight savings start time for Paris, France:
//March 31, 2013 at 02:00
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
DateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
Date date = df.parse("20130331T020000");
System.out.println(df.format(date));
//expected: "20130331T020000"
//actual: "20130331T030000"
```~~
In the example above, "20130331T020000" should be printed, but "20130331T030000" is printed instead.
It looks like the data loss occurs when the default timezone of the local computer is the same as the timezone being parsed.
iCalendar example:
```~~
:::text
BEGIN:VCALENDAR
BEGIN:VTIMEZONE
TZID:Europe/Paris
BEGIN:DAYLIGHT
DTSTART:20130331T020000
...
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR
```~~
If the local computer's timezone is "Europe/Paris", then it will appear as if the `DTSTART` property in the example above is set to "20130331T030000". However, its true value is "20130331T020000".
Discussion: https://sourceforge.net/p/biweekly/discussion/help-and-support/thread/cb55fc2a/
**Workaround:**
Use the attached marshaller and property classes to get the raw string value of all DTSTART properties:
```~~
:::java
String str =
"BEGIN:VCALENDAR\r\n" +
"BEGIN:VTIMEZONE\r\n" +
"BEGIN:DAYLIGHT\r\n" +
"DTSTART:20130331T020000\r\n" +
"END:DAYLIGHT\r\n" +
"END:VTIMEZONE\r\n" +
"END:VCALENDAR\r\n";
ICalendar ical = Biweekly.parse(str).register(new DateStartRawMarshaller()).first();
DateStartRaw prop = ical.getTimezones().get(0).getDaylightSavingsTime().get(0).getProperty(DateStartRaw.class);
System.out.println(prop.getValueRaw());
```~~
Reported by: mangstadt
Original Ticket: [biweekly/tickets/3](https://sourceforge.net/p/biweekly/tickets/3)
The following slightly-malformed REPLY:
BEGIN:VCALENDAR
METHOD:REPLY
BEGIN:VEVENT
DTSTAMP:20050317T164626Z
DTSTART:20050318T170000Z
UID:040000008200E00074C5B7101A82E00800000000A0DD17CECD2AC501000000000000000010000000DA4A35FDD8F70E4686F330A21558AF27
ATTENDEE;PARTSTAT=ACCEPTED;CN="Roland Schtroumpf":MAILTO:[email protected]
LOCATION:866-555-3378, conf ID = 650-555-0500, passcode = 255455
DTEND:20050318T180000Z
END:VEVENT
END:VCALENDAR
Causes the following NPE:
Caused by: java.lang.NullPointerException: null
at biweekly.io.scribe.property.AttendeeScribe._defaultDataType(AttendeeScribe.java:51) ~[biweekly-0.4.6.jar:na]
at biweekly.io.scribe.property.ICalPropertyScribe.defaultDataType(ICalPropertyScribe.java:171) ~[biweekly-0.4.6.jar:na]
at biweekly.io.text.ICalReader._readNext(ICalReader.java:301) ~[biweekly-0.4.6.jar:na]
at biweekly.io.StreamReader.readNext(StreamReader.java:150) ~[biweekly-0.4.6.jar:na]
Hi
I really like this lib and would like to use it in a project. However, when dealing with enterprise orgs and/or OSS redistribution, the license is a cruical factor.
From what I understand, you use a custom "this is my license" license. Did you consider switching this to a more standard way? I really don't care if its apache, gnu or whatever, but it would be helpful to argue "pro" biweekly if it would be a known one.
Concerns?
Jan
Hi, thanks for this library.
I am creating recurrence as following:
Recurrence recurrence = new Recurrence.Builder(Frequency.DAILY).build();
Question: how to get string representation of Recurrence
?
The spec defines DTSTART and DTEND as being able to be either a DATE-TIME or a DATE value type. biweekly always outputs whole day events with a date with a time of midnight (sans timezone). While this techincally the same timespan as an all day event, most calendar apps, including Google and Yahoo calendars treat this as a time-bounded event. And if there is ambiguity about the timezone of the calendar because it's ommitted, then all day events often end with their times up being offset by the timezone inferred by the calendar system that is subscribed to an ical feed.
To avoid this, biweekly should output dates from all date events using DATE values and not DATE-TIME values.
The spec says (http://tools.ietf.org/html/rfc5545#section-3.8.2.4):
Value Type: The default value type is DATE-TIME. The time value MUST be one of the forms defined for the DATE-TIME value type. The value type can be set to a DATE value type.
But biweekly is parsing DTSTART and DTEND dates that are just DATEs as DATE-TIMEs and setting the time to T000000. For example, reading in an ICS with
DTSTART;VALUE=DATE:20141005
results in an entry which biweekly will output as:
DTSTART:20141005T000000
The behavor ICalPropertyScribe$DateWriter.write() should be to prefix the value with "VALUE=DATE:" and omit the time and timezone portion when the hasTime is false.
Reported by: evaneaston
Original Ticket: biweekly/tickets/7
Consider the following code :
ICalendar ical = getCalendar();
File file = new File("target/test-simpleCalendarToOutputStream.ics");
FileOutputStream fileOutputStream = new FileOutputStream(file);
ical.write(fileOutputStream);
Nothing is written into the file.
When you dive into biweekly source code, you'll find :
public void go(OutputStream out) throws IOException {
go(new ICalWriter(out));
}
Writer needs to be flush at one point.
FYI, you don't have any problem with file directly (not through output stream), because the writer is properly close (and flush before that automatically) :
public void go(File file, boolean append) throws IOException {
ICalWriter icalWriter = new ICalWriter(file, append);
try {
go(icalWriter);
} finally {
IOUtils.closeQuietly(icalWriter);
}
}
The go(OutputStream out) should manage the writer with a proper closing.
Reported by: rgonord
Original Ticket: biweekly/tickets/4
Are you planning to provide a support of java.time (LocalDateTime, Datetime etc) for usage of Date, DateTime classes?
Not necessarely for internal working, but as parameters for methods?
From @ParthPadg:
I think I've found a small bug in the DateIterable created from DateIteratorFactory.createDateIterable(String, Date, TimeZone, boolean).
If I create a DateIterable
with a Date
(e.g., July 17, 2016 @ 14:00) and timezone=PDT, the DateIterable
doesn't entirely respect the timezone I input--it spits out a Date with UTC time (e.g., July 19, 2016 @ 21:00). The workaround is to initialize the DateIterable
with UTC time, but it isn't readily apparent that that's what a user should do.
I've written a small script that reproduces this issue (see below). I suspect that one of the issues is in DateIteratorFactory#dateToDateValue(Date, boolean)
, but it might be deeper in the iterator generator code. (I also realize that this is code ported from Google's RFC2445 lib, so feel free to tell me that this isn't your problem anymore )
TimeZone timezone_pdt = TimeZone.getTimeZone("America/Los_Angeles");
Date startDate = new Date(2016, 7, 17, 14, 0, 0);
DateIterable workaroundIterable = DateIteratorFactory.createDateIterable("RRULE:FREQ=WEEKLY;BYDAY=TU", startDate, DateTimeZone.UTC.toTimeZone(), true);
DateIterable PDTInitializedIterable = DateIteratorFactory.createDateIterable("RRULE:FREQ=WEEKLY;BYDAY=TU", startDate, timezone_pdt, true);
int count = 3;
SimpleDateFormat sdf_pdt = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz");
sdf_pdt.setTimeZone(timezone_pdt);
SimpleDateFormat sdf_default = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz");
for (DateIterator pdtIter = PDTInitializedIterable.iterator(), workaroundIter = workaroundIterable.iterator(); pdtIter.hasNext() && workaroundIter.hasNext();) {
Date pdtNext = pdtIter.next();
Date utcNext = workaroundIter.next();
System.out.println("Next PDT-initialized-iterable time: \t" + sdf_pdt.format(pdtNext)); // should be "14:00:00 PDT", but is "...21:00:00 PDT"
System.out.println("Next workaround-created time: \t\t" + sdf_default.format(utcNext));
System.out.println();
if (--count == 0)
break;
}
Output:
Next PDT-initialized-iterable time: Thu Aug 17 21:00:00 PDT
Next workaround-created time: Thu Aug 17 14:00:00 PDT
Next PDT-initialized-iterable time: Tue Aug 22 21:00:00 PDT
Next workaround-created time: Tue Aug 22 14:00:00 PDT
Next PDT-initialized-iterable time: Tue Aug 29 21:00:00 PDT
Next workaround-created time: Tue Aug 29 14:00:00 PDT
Original gitter conversation: https://gitter.im/mangstadt/biweekly?at=578eb63bac85f2507ad30fb1
I have been searching through the documentation and don't see any way to create an ics file that cancels an existing event. I have just started using this library and it is fantastic. Just wanted to add event cancellations to the wish list :).
If a parameter is value-less, then a NullPointerException
is thrown when attempting to insert the parameter into the multimap.
Example:
::text
ATTACH;FMTTYPE:application/postscript:ftp://xyzCorp.com/pub/
```~~
Stack trace:
```~~
:::text
Exception in thread "main" java.lang.NullPointerException
at biweekly.parameter.ICalParameters.sanitizeKey(ICalParameters.java:591)
at biweekly.parameter.ICalParameters.sanitizeKey(ICalParameters.java:42)
at biweekly.util.ListMultimap.get(ListMultimap.java:112)
at biweekly.util.ListMultimap.put(ListMultimap.java:81)
at biweekly.io.text.ICalRawReader.parseLine(ICalRawReader.java:125)
at biweekly.io.text.ICalRawReader.start(ICalRawReader.java:70)
at biweekly.io.text.ICalReader.readNext(ICalReader.java:172)
at biweekly.Biweekly$ParserChainText.first(Biweekly.java:447)
at biweekly.Biweekly$ParserChainTextString.first(Biweekly.java:570)
at Tests.TestBiWeeklyParsing.parse(TestBiWeeklyParsing.java:92)
at Tests.TestBiWeeklyParsing.main(TestBiWeeklyParsing.java:52)
```~~
See: https://sourceforge.net/p/biweekly/discussion/help-and-support/thread/e5efcfc4/
Reported by: mangstadt
Original Ticket: [biweekly/tickets/1](https://sourceforge.net/p/biweekly/tickets/1)
Hi mangstadt
I used the biweekly(version 0.6.0) to generate executed times. But if the date value equals
' Sun Nov 06 01:46:46 PST 2016' or ' Sun Nov 06 01:46:46 PDT 2016'
the result is same. I am not sure that is correct.
If there is no problem, pls ignore this issue.
The code:
` final TimeZone pacificTimeZone = TimeZone.getTimeZone("America/Los_Angeles");
Recurrence.Builder builder = new Recurrence.Builder(Frequency.DAILY);
builder.count(5);
builder.interval(1);
RecurrenceIterator recurrenceIterator = RecurrenceIteratorFactory.createRecurrenceIterator(builder.build(), date, pacificTimeZone);
DateIterator dateIterator = DateIteratorFactory.createDateIterator(recurrenceIterator);
while (dateIterator.hasNext()) {
System.out.println(dateIterator.next());
}`
date = 'Sun Nov 06 01:46:46 PST 2016'
Output
Sun Nov 06 01:46:46 PST 2016
Mon Nov 07 01:46:46 PST 2016
Tue Nov 08 01:46:46 PST 2016
Wed Nov 09 01:46:46 PST 2016
Thu Nov 10 01:46:46 PST 2016
It would be nice to have a reader or a visitor to stream components (VEVENT, etc.) as they're encountered instead of reading all of them into memory. This would allow for reading larger calendars without the memory concerns.
According to spec "more than one category can be specified as a COMMA-separated list of categories" (p. 81) which implies that it should appear only once within an event.
However the method "VEvent.addCategories(String... categories)" adds a new CATEGORIES property every time it is invoked. The same applies to other components where the CATEGORIES property is allowed: VTodo, VJournal.
In my point of view adding new categories should append them to the CATEGORIES property if one exists.
Reported by: bjgumble
Original Ticket: biweekly/tickets/5
Hello,
I think I'm having a problem with timezones in an iCalendar example I'm using.
I created a recurring event on outlook and exported to iCalendar format for testing. It uses my timezone. The resulting iCalendar data is in here http://pastebin.com/ABXCD18s
So, it contains two VTIMEZONE objects, and the VEVENT references them like:
DTSTART;TZID="Unnamed Time Zone 1":20160805T150000
DTEND;TZID="E. South America Standard Time":20160805T153000
This is supposed to be America/Sao_Paolo timezone which is -3:00 GMT. So I try to verify the final value I'm getting like (using joda time):
assertEquals(new DateTime(Instant.parse("20160805T150000", DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss"))).toDateTime().plusHours(3).getMillis(), event.getDateStart().getValue().getTime());
assertEquals(new DateTime(Instant.parse("20160805T153000", DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss"))).toDateTime().plusHours(3).getMillis(), event.getDateEnd().getValue().getTime());
If I use direct GMT values the test works. What I'm I missing to make sure the time is taking into consideration the vtimezone?
From thread: https://sourceforge.net/p/biweekly/discussion/help-and-support/thread/0540a0b1/?limit=25#e251
This constructor uses the Calendar class to remove the time components from the specified date. The Calendar object returned from Calendar.getInstance defaults to using the default timezone for the device (machine). This causes the resulting date to be offset by the default timezone’s offset from UTC. As an example:
I want to create an all day event with a start date of 9/24/2015 and end date of 9/25/2015. I pass in a date object for 9/24/2015 00:00:00, and 9/25/2015 00:00:00 (obviously UTC, since all Dates are UTC). My timezone is CDT (UTC-5:00). What happens is, the Calendar object has a time of 9/23/2015 19:00:00, because it’s in CDT time, and I end up with a date of 9/23/2015 with the time components removed.
This should be a simple matter of adding the line: c.setTimeZone(TimeZone.getTimeZone("UTC”));
Reported by: mangstadt
Original Ticket: biweekly/tickets/13
From @dankarp via gitter:
When a VALARM has a TRIGGER that goes off at the start of the event, biweekly's VAlarm.getTrigger() returns null. This seems like a really bad idea.
BEGIN:VALARM ACTION:DISPLAY DESCRIPTION:REMINDER TRIGGER;RELATED=START:+PT00H00M00S END:VALARM
VAlarm.getTrigger()
returns null because an error occurs when the property's duration value is parsed. The parser incorrectly thinks the "+" at the beginning of the duration string is invalid.
I can't build JAR-Package because the testrun fails:
Results :
Failed tests:
Google2445UtilsTest.getDateIterator:124->assertIteratorEquals:132 expected:<Sun Mar 27 20:00:00 CEST 2016> but was:<Sun Mar 27 15:00:00 CEST 2016>
Tests run: 1036, Failures: 1, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.862s
[INFO] Finished at: Tue Aug 23 10:33:13 CEST 2016
[INFO] Final Memory: 32M/457M
[INFO] ------------------------------------------------------------------------
There could be added LocalDateIteratorFactory and DateTimeIteratorFactory as java.time support. In google-rfc-2445 there are already implemented similar iterators for jodatime compatibility.
In the course of using the advanceTo
method on a DateIterator
I have come across an issue that exists in release 0.4.6 whereby the advanceTo
method on an DateIterator
fails to actually advance.
This unit test below illustrates the problem. In the unit test, a Recurrence
is created with a DAILY
frequency, a start time of 2016-07-01 00:01:00
, and a time zone of America/Los_Angeles
. After fetching the iterator it is advanced to 2016-07-01 06:59:00
then next
is called on the iterator. The first occurrence, which is the start time, should be skipped but it is not.
@Test
public void advanceIterator() {
// Use time zone with negative offset
TimeZone pacificTimeZone = TimeZone.getTimeZone("America/Los_Angeles");
Date startTime = date("2016-07-01 00:01:00", pacificTimeZone);
Date advanceTo = date("2016-07-01 06:59:00", pacificTimeZone); // advance iterator to the time that is 1-minute less than the time zone offset.
// Note: date-time used for advancement must first be converted to UTC first.
Recurrence recur = new Recurrence.Builder(Recurrence.Frequency.DAILY).count(4).build();
RecurrenceProperty recurrenceProperty = new RecurrenceProperty(recur);
DateIterator it = recurrenceProperty.getDateIterator(startTime, pacificTimeZone);
it.advanceTo(new Date(advanceTo.getTime())); //
//@formatter:off
// First occurrence is skipped. The last three occurrences should be returned.
List<Date> expected = Arrays.asList(
date("2016-07-02 00:01:00", pacificTimeZone),
date("2016-07-03 00:01:00", pacificTimeZone),
date("2016-07-04 00:01:00", pacificTimeZone)
);
//@formatter:on
List<Date> actual = new ArrayList<Date>();
while (it.hasNext()) {
actual.add(it.next());
}
assertEquals(expected, actual);
}
The root cause of the problem appears to be method convert
in TimeUtils.java
: https://github.com/mangstadt/biweekly/blob/master/src/main/java/biweekly/util/com/google/ical/util/TimeUtils.java#L88-89
When converting from UTC within method convert
, line 89 where method addSeconds
is called, is not necessary. The UTC time passed into the method has already been adjusted to the time zone passed into convert
by line 88. Because addSeconds
is called, the DateTimeValue
returned in line 88 is further adjusted by the offset of the time zone in use. For the unit test above, the DateTimeValue
returned by line 88 is 2016-07-01 06:59:00
. The call to addSeconds
causes -25,200 seconds (e.g. -7 hours) to be added which adjusts the value (back) further to 2016-06-30 23:59:00
. Note this is actually prior to the start time. Because the iterator is now behind the the start-time, the first call to next
causes the occurrence at the start-time to be returned.
It should be noted that this problem arose due to PR #39, which I submitted. That PR resolved an issue related to the DST gap hour but apparently created the issue reported here. The problem described by this issue does not exist in releases prior to 0.4.6, though the issue resolved by PR #39 would exist.
I am working on a fix for this issue and associated unit tests. A PR will be submitted for your review today or tomorrow.
The project cannot be compiled using JDK 8 because of some javadoc generation problems, as explained here:
We are currently using the 'original' google-rfc2445 jar and its jodatime compatibility package. As biweekly uses its own copy of google-rfc2445 without that package (presumably to avoid a dependency), we have to face a conflict.
A possible solution would be to rename biweekly's package to something like biweekly.com.google.ical.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.