Is EXDATE not included in rrule for Full Calendar - fullcalendar

I have a recurring calendar event that happens on the 4th Friday of every month and I want to exclude 1 Friday event. I've tried EXDATE but I'm getting an error
Failure passing JSON
Without EXDATE the rrule works fine.
Here are the details that I'm using in Full Calendar to produce the event
{
start: "2019-07-06T09:00:00+10:00",
end: "2019-07-06T15:00:00+10:00",
rrule: "FREQ=WEEKLY;DTSTART=20190607T090000;EXDATE=20190705T090000;INTERVAL=4;BYDAY=FR",
title: "Weed Spraying",
description: "June, Harry, Pat, George, Valda, Helen, Karen, Ken",
color: "red",
url: "./?action=detail_view&id=22",
duration: "06:00"
}

OK I worked it out, #Arnaud is right, RRULE, EXDATE and DTSTART are properties not parameters of rrule, BUT they do go in the rrule property for FullCalendar with a \n newline, they also require a : NOT =. Example
rrule: 'DTSTART:20190308T120000Z\nRRULE:FREQ=WEEKLY;UNTIL=20220330\nEXDATE:20190614T120000Z\nEXDATE:20190628T120000Z'
Notice how there are 2 EXDATE properties, for each date you want to exclude, you need to put an EXDATE.
I spent 3 days trying to get this to work, hopefully this will help save someone else time.

It is possible to add exceptions. You just need to format the RRule string correct:
DTSTART:20190610T103000\nRRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20190801\nEXDATE:20190618T103000Z\nEXDATE:20190619T103000
Watch this code sandbox for a demo

This format can also be used for multiple EXDATE:
rrule: "DTSTART:20201114T000000Z\nRRULE:FREQ=WEEKLY\nEXDATE:20201121T000000Z,20201128T000000Z"
This string was formatted using rruleSet.exdate(new Date(Date.UTC(2012, 5, 1, 10, 30))) from rrule.js library to add multiple EXDATE in the rrule object then using the method .toString()
Also, note that adding the 'Z' char for RRule datetimes now works in v5.4.0

Do not know much about this particular JSON format but the EXDATE is a property, not a parameter of RRULE.
Please try
rrule: "FREQ=WEEKLY;DTSTART=20190607T090000;INTERVAL=4;BYDAY=FR\nEXDATE=20190705T090000"

In the latest version 5.4.0 the following code will work:
DTSTART:20201101T040000Z
RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU
EXDATE:20201110T040000Z,20201124T040000Z
FullCalendar now supports multiple exdate
I used the RRule and RRuleSet to produce the rrule string:
*NOTE: You will have to be careful with daylight savings. That's why I had to adjust a couple of times. Do not focus on my specific date object. This is just an example of the solution to support multiple exdates using RRuleSet and RRule.
const rruleSet = new RRuleSet();
rruleSet.rrule(new RRule({
freq: Frequency.WEEKLY,
interval: 1,
byweekday: [RRule.TU],
dtstart: new Date(2020, 10, 1, 0, 0, 0, 0)
}));
rruleSet.exdate(new Date(2020, 10, 9, 23, 0, 0, 0));
rruleSet.exdate(new Date(2020, 10, 23, 23, 0, 0, 0));
console.log(rruleSet.toString());
Here is the issue number:
https://github.com/fullcalendar/fullcalendar/issues/5726

Related

get custom value from point series data

I am adding data to scatter series like below , how do we receive the custom parameter value I add here
series.add({
x: 0,
y: 22,
color: ColorRGBA(255, 0, 0),
size: 10,
rotation: 45,
value: "custom message",
});
From the above series how do I extract the value "custom message" .. basically is there anyway to get it ? so I can to use it in table formatter like below .
series.setCursorResultTableFormatter((builder, series, xValue, yValue,value) => {
return builder.addRow(value).addRow(series.getValue());
});
Also another doubt is can we change shape of series dynamically like we change color and size ?
With v.3.1 this kind of logic is not supplied out of the box.
To implement it you'd need to add some kind of custom logic which finds your custom data based on the X and Y information.
In next release, v.3.2 we'll add an extra parameter to cursor result table formatters, so you can use it like follows:
series.setCursorResultTableFormatter((builder, series, x, y, dataPoint) => {
// `dataPoint` has all same information as user supplied, size, rotation, value or anything.
return builder.addRow(x).addRow(y).addRow(dataPoint.value);
});
Please note that these custom properties (size, rotation, value, etc.) will only be included when cursor interpolation is disabled.
At this time v.3.2 is scheduled for late September, but this could change.
2nd question about changing shape of point series, we currently don't have active plans to change this, but when there is enough motivation it will be improved on.

C# asp.net core 2.2 - Get current date time - Not Accurate

I tried everything to get the current year/month/date/hour but it doesn't work. I always get data 1 hour behind. So if it's 17:00 in the Netherlands, I get 16:00. I have tried so many things, but I can't get this fixed.. is it a bug?
I Tried this:
string dateTimeNowPlusForecaseHour = DateTimeOffset.Now.ToLocalTime().AddHours(hour).ToString("yyyyMMddhh");
And this:
string dateTimeNowPlusForecaseHour = DateTimeOffset.UtcNow.ToLocalTime().AddHours(hour).ToString("yyyyMMddhh");
And this:
string dateTimeNowPlusForecaseHour = DateTimeOffset.UtcNow.ToLocalTime().AddHours(hour).ToString("yyyyMMddhh");
And this:
string dateTimeNowPlusForecaseHour = DateTimeOffset.Now.AddHours(hour).ToString("yyyyMMddhh");
And this:
public string GetForecastTime(int hour)
{
// Take amount of seconds elapsed since 1970
var amountOfSecondsElapsedSince1970 = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
// Make a Datetime variable (1970 is the start)
System.DateTime dateTimeNowString = new DateTime(1970, 1, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
// Add amountOfSecondsElapsedSince1970 to the date time starting at 1970, + the amount of hours (for each forecast so 0, 1, and 2)
dateTimeNowString = dateTimeNowString.AddSeconds(amountOfSecondsElapsedSince1970).AddHours(hour).ToLocalTime();
// This is the dateTime
string dateRealWeatherForecastTimee = dateTimeNowString.ToString("yyyyMMddhh");
return dateRealWeatherForecastTimee;
}
Nothing seems to work.
Update
I tried the following line on a new asp.net core console project (rather than the project I'm working on):
string dateTimeNowPlusForecaseHour = DateTime.Now.AddHours(hour).ToString("yyyyMMddhh");
That works! But why doesn't it work for the project I'm working on?
Update2.0:
When I view my timezone of the project, I get GMT which is wrong. When I create a new asp.net core console project and view the timezone, I get w. europe, which gives me the correct time... Why is the timezone wrong? Shouldn't that be correct automatically?
DateTime.Now returns the current time and day. The struct it returns
can be stored as a field or property in a class. We look into this
property and its implementation—where it accesses the operating system
for the current time.
string dateTimeNowPlusForecaseHour = DateTime.Now.ToString("yyyyMMddHH");
This screenshot is taken from here. And I've used almost all of these, which means these are tested and should definitely work. If you still get incorrect hour, please check the time-zone of the server where you're hosting your application.

Generate ics with dynamic VTIMEZONE using moment js

Trying to create a .ics file which has a VTIMEZONE component, which based on the supplied timezone sets the Standard time and Daylight Savings time dynamically.
Just a sample:
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
BEGIN:STANDARD
DTSTART:20071104T020000
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20070311T020000
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
END:DAYLIGHT
END:VTIMEZONE
In my attempt to solve this I created a moment.tz.zone(timezone)Object which based on the documentation of moment https://momentjs.com/timezone/docs/#/zone-object/ I assume holds the necessary data untils(should be TZOFFSETFROM, TZOFFSETTO) and offsets(DTSTART).
Yet I can't find a clear documentation on how to extract these data.
Was wondering if there's anyway that one can extract the DTSTART, TZOFFSETFROM and TZOFFSETTO for Standard time and Daylight in moment-timezone.js
You can download pre-made VTIMEZONE components here:
http://tzurl.org/
As you already mentioned in the question, you can use the moment.tz.zone(name) method. This will give you a Zone object that contains a list of timestamps in the untils property, then you can apply your logic to get the timestamps you want in the VTIMEZONE (I've used the first timestamps of the untils array in my code sample).
You can use moment.tz and format() on a timestamp to get DTSTART. You can pass ZZ token to format() to get offset for TZOFFSETFROM and TZOFFSETTO.
You can use abbrs values to get TZNAME.
Here a live sample:
const MAX_OCCUR = 2;
const getVtimezoneFromMomentZone = (tzName) => {
const zone = moment.tz.zone(tzName);
const header = `BEGIN:VTIMEZONE\nTZID:${tzName}`;
const footer = 'END:VTIMEZONE';
let zTZitems = '';
for(let i=0; i<MAX_OCCUR && i+1<zone.untils.length; i++){
const type = i%2 == 0 ? 'STANDARD' : 'DAYLIGHT';
const momDtStart = moment.tz(zone.untils[i], tzName);
const momNext = moment.tz(zone.untils[i+1], tzName);
const item =
`BEGIN:${type}
DTSTART:${momDtStart.format('YYYYMMDDTHHmmss')}
TZOFFSETFROM:${momDtStart.format('ZZ')}
TZOFFSETTO:${momNext.format('ZZ')}
TZNAME:${zone.abbrs[i]}
END:${type}\n`;
zTZitems += item;
}
const result = `${header}\n${zTZitems}${footer}\n`;
return result;
};
console.log(getVtimezoneFromMomentZone('America/New_York'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment-with-locales.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data-2012-2022.min.js"></script>
This is a bit challenging to do in a robust way.
Summary
Use RRULE to avoid bloating your ics and support long-running or open-ended recurring events.
moment-timezone doesn't expose underlying zoneinfo data any way which would make it easy to build RRULE for a given zone (as far as I can tell).
For one-off events with fixed date, you can just pick the correct interval(s) to include in the ics from moment.tz.zone('America/New_York').untils based on the event date.
Details
As an example: moment.tz.zone('America/New_York').untils includes 235 intervals (DAYLIGHT or STANDARD over the years) from 1918 to 2037.
You don't want to include them all in your ics.
If you only include the first two in your VTIMEZONE, it won't be valid except for some events in 1918/1919.
var timezoneName = 'America/New_York',
{untils, abbrs, offsets} = moment.tz.zone(timezone);
console.log(untils.length);
// 236
console.log(moment.tz(untils[0], timezoneName).format('YYYY-MM-DD HH:mm:ss'));
// 1918-03-31 03:00:00
console.log(moment.tz(untils[untils.length-2], timezoneName).format('YYYY-MM-DD HH:mm:ss'));
// 2037-11-01 01:00:00
console.log(untils[untils.length-1]);
// Infinity
You could put all 235 of these intervals into an ICS but it would be really bloated.
The RFC section on VTIMEZONE includes some examples...
This is an example showing time zone information for New York City
using only the "DTSTART" property. Note that this is only
suitable for a recurring event that starts on or later than March
11, 2007 at 03:00:00 EDT (i.e., the earliest effective transition
date and time) and ends no later than March 9, 2008 at 01:59:59 EST (i.e., latest valid date and time for EST in this scenario).
For example, this can be used for a recurring event that occurs
every Friday, 8:00 A.M.-9:00 A.M., starting June 1, 2007, ending
December 31, 2007,
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
BEGIN:STANDARD
DTSTART:20071104T020000
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20070311T020000
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
END:DAYLIGHT
END:VTIMEZONE
The point is that the VTIMEZONE in the example is using only the "DTSTART" property...and in this case the VTIMEZONE is only valid for event dates covered by the STANDARD and DAYLIGHT intervals explicitly listed in the VTIMEZONE.
Another example from the RFC...
This is a simple example showing the current time zone rules for
New York City using a "RRULE" recurrence pattern. Note that there
is no effective end date to either of the Standard Time or
Daylight Time rules. This information would be valid for a
recurring event starting today and continuing indefinitely.
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
TZURL:http://zones.example.com/tz/America-New_York.ics
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
END:DAYLIGHT
END:VTIMEZONE
Note that in this case the presence of an RRULE which explains when these STANDARD and DAYLIGHT intervals reoccur means that we don't have to explicitly add all the specific intervals over the years. You would just need the most recent (before your event) interval where the RRULE changed. If your event is recurring and spans across rule changes, then you have to include a couple more intervals with corresponding rules to cover the events BEFORE the rule change as well as the events AFTER the rule change.
Indeed, inspecting an ICS generated by Apple's macOS calendar app for an event on August 19, 2021 in timezone Europe/Berlin includes the following VTIMEZONE (indented for readability)...
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
DTSTART:19810329T020000
TZNAME:GMT+2
TZOFFSETTO:+0200
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
DTSTART:19961027T030000
TZNAME:GMT+1
TZOFFSETTO:+0100
END:STANDARD
END:VTIMEZONE
Note that STANDARD has a DTSTART in 1996 and DAYLIGHT has a DTSTART in 1981 despite the event being in 2021. The presence of the RRULE allows them to avoid including many more STANDARD/DAYLIGHT intervals.
Best solution
...is probably to generate RRULE. This allows you to minimize the size of your ics file while supporting recurring events far into the future.
Downside: I can't find any easy way to generate RRULE with moment-timezone... but there appears to be some other libs around that might help (haven't played with them yet).
If anyone has some tips/experience generating RRULEs, it would be great to hear your experience.
Option 2: Work-around for specific use-cases
If you are dynamically generating an ICS file for a single or recurring event where you know the event date (or date range for recurring event), then you can just filter the moment.tz.zone('America/New_York').untils to make sure that you have ALL the STANDARD and DAYLIGHT intervals you need to cover your event date/range.
Downside: for long-running or open-ended recurring events this may not be a good option because too many intervals will have to be included in the ics file (bloat).
However for single, fixed-date events this is probably a fine option.
Quick example for option 2...
I only did a cursory scan of the RFC and to be safe I included the transition FOLLOWING the end date so you will always have at least 2 transitions even when you have an event at a single timestamp. One transition that occurs before the event date and one that occurs after. This may not be necessary.
function generateVTimezone (timezoneName, tsRangeStart, tsRangeEnd) {
var zone = moment.tz.zone(timezoneName),
{untils, abbrs, offsets} = zone,
i, dtStart, utcOffsetBefore, utcOffsetDuring, periodType,
vtz = [
`BEGIN:VTIMEZONE`,
`TZID:${timezoneName}`,
];
tsRangeStart = tsRangeStart || 0;
tsRangeEnd = tsRangeEnd || Math.pow(2,31)-1;
// https://momentjs.com/timezone/docs/#/data-formats/unpacked-format/
// > between `untils[n-1]` and `untils[n]`, the `abbr` should be
// > `abbrs[n]` and the `offset` should be `offsets[n]`
for (i=0; i<untils.length - 1; i++) {
// filter to intervals that include our start/end range timestamps
if (untils[i+1] < tsRangeStart) continue; // interval ends before our start, skip
if (i>0 && untils[i-1] > tsRangeEnd) break; // interval starts after interval we end in, break
utcOffsetBefore = formatUtcOffset(offsets[i]); // offset BEFORE dtStart
dtStart = moment.tz(untils[i], timezoneName).format('YYYYMMDDTHHmmss');
utcOffsetDuring = formatUtcOffset(offsets[i+1]); // offset AFTER dtStart
periodType = offsets[i+1] < offsets[i] ? 'DAYLIGHT' : 'STANDARD'; // spring-forward, DAYLIGHT, fall-back: STANDARD.
vtz.push(`BEGIN:${periodType}`);
vtz.push(`DTSTART:${dtStart}`); // local date-time when change
vtz.push(`TZOFFSETFROM:${utcOffsetBefore}`); // utc offset BEFORE DTSTART
vtz.push(`TZOFFSETTO:${utcOffsetDuring}`); // utc offset AFTER DTSTART
vtz.push(`TZNAME:${abbrs[i+1]}`);
vtz.push(`END:${periodType}`);
}
vtz.push(`END:VTIMEZONE`);
return vtz.join('\r\n'); // rfc5545 says CRLF
}
function formatUtcOffset(minutes) {
var hours = Math.floor(Math.abs(minutes) / 60).toString(),
mins = (Math.abs(minutes) % 60).toString(),
sign = minutes > 0 ? '-' : '+', // sign inverted, see https://momentjs.com/timezone/docs/#/zone-object/offset/
output = [sign];
// zero-padding
if (hours.length < 2) output.push('0');
output.push(hours);
if (mins.length < 2) output.push('0');
output.push(mins);
return output.join('');
}
function test() {
var timezone = 'America/New_York',
startTS = moment.tz('2013-11-18 11:55', timezone).unix()*1000,
endTS = moment.tz('2013-11-18 11:55', timezone).unix()*1000;
console.log(generateVTimezone(timezone, startTS, endTS));
}
test();
produces output...
BEGIN:VTIMEZONE
TZID:America/New_York
BEGIN:STANDARD
DTSTART:20131103T010000
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20140309T030000
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
END:DAYLIGHT
END:VTIMEZONE

How to pass date to breeze

I am passing date to query in breeze as below
Predicate.create('approvedOn', op.GreaterThanOrEqual, "01.05.1985");
But i get invalid date error. How to pass date to breeze query?
Update1
I also tried passing date by converting it to ISO 8601 format but still get error
You need to use a javascript date object in your query.
var aDate = new Date(1985, 5, 1, 0, 0, 0)
Predicate.create('approvedOn', op.GreaterThanOrEqual, aDate);
Do you try to convert it to other formats?
I worked with some projects that use query with date like this:
var time = new Date($.now());
var pre = new breeze.Predicate.create("DatePost", ">=", time);
It works fine. You should try with other date format, if get errors, post them here.

Using VFreebusy iCalendar component

I making a scheduling application and I am using iCalendar format. I am aware that I can use this code to get the free time slots from current calendar:
DateTime start = new DateTime();
DateTime end = new DateTime(start.getTime() + 1000 * 60 * 60 * 24 * 7);
VFreeBusy request = new VFreeBusy(start, end, new Dur(0, 2, 0, 0));
VFreeBusy response = new VFreeBusy(request, myCalendar.getComponents());
And I get the following output from using this code on a couple of events in the calendar.
DTSTAMP:20140323T204423Z
DTSTART:20020202T040023Z
DTEND:20020203T040023Z
DURATION:PT45M
FREEBUSY;FBTYPE=FREE:20020202T040023Z/PT2H,20020202T070023Z/PT4H,20020202T120023Z/PT16H
END:VFREEBUSY
What I don't know is how to use that VFreeBusy object with those free time slots and actually get them out, so I can compare them and use them as dates and times.
I used response.getProperties().getProperty(Property.FREEBUSY) to get the part that I need, but I don't know how to parse all that String. If you have any other ways for me to get those time slots please advise.
Assuming that you are using ical4j. Once you get the property, you can cast it to a FreeBusy property which has a getPeriods() method.

Resources