Why is the end date for one more day? - fullcalendar

select: function(start, end) {
var formatStart = start.format();
var formatEnd = end.format();
alert(formatEnd);
showProjModal(formatStart,formatEnd);
},
i choice 2016-08-11 to 2016-08-13,but formatEnd is 2016-08-14,i do not know why?

From the docs at fullcalendar.io.
end is a Moment indicating the end of the selection. It is an exclusive value, so if the selection is all-day, and the last day is a Thursday, end will be Friday.
I guess your selection is all day? So it is working as intended.
You can check this by calling hasTime
Example
if (end.hasTime()) {
// Specific endpoint. For example 2016-07-13 10:00:00'
}
else {
// All day. For example 2016-07-13
// If you want to output the end day you selected here subtract 1 of the day
end.subtract(1, 'days');
}

Related

SwiftUI or Combine Clock/Timer events

Using SwiftUI (or Combine) how might I set up a series of one or more events that are triggered by the (system) clock. Examples might include:
Every night at midnight,
On the hour,
Every fifteen minutes on the quarter hour,
Finally, on a slightly different note: On the 29th of February 2020 at 12:15.
An approximation is easily achieved by setting up a timer event that fires every second and then checking the hours/minutes/seconds, etc. but this seems very inefficient for events that may be many hours or days apart.
I'm looking for something that is closely synchronised to the actual system clock and fires off a single event at the required time rather than firing loads of events and having each one ask "Are we there yet?".
I would suggest the following:
DispatchQueue.global(qos: .background).async {
let isoDate = "2020-01-13T16:58:30+0000"
let dateFormatter = ISO8601DateFormatter()
let date = dateFormatter.date(from:isoDate)!
let t = Timer(fire: date, interval: 2, repeats: true) { timer in
print("fired")
}
let runLoop = RunLoop.current
runLoop.add(t, forMode: .default)
runLoop.run()
}
string to date conversion I used this answer to format the time correctly.
The example is in GMT.
documentation apple you can look up timer tolerance which can be adjusted if you need the timer to be very accurate.
interval is in seconds so this solution won't get more accurate than seconds
You might want to enable the Background Modes capability to go for the very long running timers. Never done that so I can't help here.
All your examples should work. I hope this helps!
I had to implement this feature too using Combine / SwiftUI : a Timer that would execute at start then every day, hour or minutes (for testing), here is my solution if it can be useful or improved :)
class PeriodicPublisher {
var periodicFormat: PeriodicFormat = .daily
init(_ format: PeriodicFormat = .daily) {
self.periodicFormat = format
}
// Must have an equatable for removeDuplicate
struct OutputDate: Equatable {
let compared: String
let original: String
init(_ comparedDatePart: String, _ originalDate: String) {
self.compared = comparedDatePart
self.original = originalDate
}
static func ==(lhs: OutputDate, rhs: OutputDate) -> Bool {
return lhs.compared == rhs.compared
}
}
enum PeriodicFormat {
case daily
case hourly
case minutely
func toComparableDate() -> String {
switch self {
case .daily:
return "yyyy-MM-dd"
case .hourly:
return "HH"
case .minutely:
return "mm"
}
}
}
func getPublisher() -> AnyPublisher<OutputDate, Never> {
let compareDateFormatter = DateFormatter()
compareDateFormatter.dateFormat = self.periodicFormat.toComparableDate()
let originalTimerDateFormatter = DateFormatter()
originalTimerDateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
var nowDate: Just<OutputDate> {
let comparedDate = compareDateFormatter.string(from: Date())
let originalDate = originalTimerDateFormatter.string(from: Date())
return Just(OutputDate(comparedDate, originalDate))
}
let timerDate = Timer.publish(every: 2.0, tolerance: 1.0, on: .main, in: .default, options: nil)
.autoconnect()
.map { dateString -> OutputDate in
return OutputDate(compareDateFormatter.string(from: dateString), originalTimerDateFormatter.string(from: dateString))
}
.eraseToAnyPublisher()
return Publishers.Merge(nowDate, timerDate)
.map { $0 }
.removeDuplicates()
.eraseToAnyPublisher()
}
}
How does it work ?
Every 2 seconds the scheduler issue current date (with Timer.publish()), this date is used to create a "OutputDate" holding two properties : one "comparable" part used to compare if something has changed and one "original" part so it can be useful for the consumer.
Comparable property is Timer's date formatted with toComparableDate given the provided configuration (.daily, .hourly, .minutely). Using "removeDuplicates" on this property allow to publish "OutputDate" only when this value changes. Every day or hour or minute.
Publishers.Merge is used to publish a value immediately after instantiation, otherwise nothing happens before the first Timer.publish(every). Here 2 seconds.
How to use it ?
You would use it with Combine like this :
PeriodicPublisher(.daily).getPublisher().sink { date in
print("Day has changed \(date.original)")
}

Get the next upcoming Friday at 5 PM

I need to display how far away a deadline is that is every Friday at 5PM. Moment will give me that date for a day of the week, but it's always the current time, so the deadline always displays 'x days, 23 hours, 59 minutes'. How do I get the day, AND a specific time from moment? In my example, I need deadline to be 'next Friday, at 17:00' instead of 'next Friday, at the current time'
console.log(timeLeft());
function timeLeft() {
var dayINeed = 5; // for Friday
var deadline;
// if we haven't yet passed the day of the week that I need:
if (moment().isoWeekday() <= dayINeed) {
// then just give me this week's instance of that day
deadline = moment().isoWeekday(dayINeed);
} else {
// otherwise, give me next week's instance of that day
deadline = moment().add(1, 'weeks').isoWeekday(dayINeed);
}
console.log(deadline);
const now = moment();
const days = deadline.diff(now, 'days');
const hours = deadline.subtract(days, 'days').diff(now, 'hours');
const minutes = deadline.subtract(hours, 'hours').diff(now, 'minutes');
return `${days} days, ${hours} hours, and ${minutes} minutes`;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.js"></script>
You have to set to deadline the time (17:00), instead of getting current time, you can use moment startOf() and set(), simply add the following to your code:
deadline.startOf('day').set({h: 17});
This way you are setting 17:00:00 to deadline and you will get the desired output.
Here a full example:
console.log(timeLeft());
function timeLeft() {
var dayINeed = 5; // for Friday
var deadline;
// if we haven't yet passed the day of the week that I need:
if (moment().isoWeekday() <= dayINeed) {
// then just give me this week's instance of that day
deadline = moment().isoWeekday(dayINeed);
} else {
// otherwise, give me next week's instance of that day
deadline = moment().add(1, 'weeks').isoWeekday(dayINeed);
}
deadline.startOf('day').set({h: 17});
console.log(deadline.format());
const now = moment();
const days = deadline.diff(now, 'days');
const hours = deadline.subtract(days, 'days').diff(now, 'hours');
const minutes = deadline.subtract(hours, 'hours').diff(now, 'minutes');
return `${days} days, ${hours} hours, and ${minutes} minutes`;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.js"></script>
If you want, you can use setter like hour(), minute() etc to set 17:00:00 to deadline.

Meteor : Countdown timer

This might be a big ask, but I'm completely stuck so any help is appreciated.
I'm trying to create a countdown timer that runs from Sunday to Sunday and just restarts at the end of the week. I've tried using countdown packages in atmosphere but the documentation is limited and never seems to work. I've also tried to download and run 3rd party jquery packages however they always seem to crash meteor.
Could someone point me in the right direction or show me how to do this in meteor?
Specific details:
Countdown timer used to run an auction.
Auction runs for 7 days, Starts Sunday at 12:00am finishes 7 days
later.
Auction resets and starts again after 7 days.
Countdown timer will be visible by users on multiple pages.
Countdown timer units to be displayed - Days, Hours, Minutes, Seconds (eg.
6 days, 3 hours, 55 minutes, 22 seconds until the next auction
begins.)
The question is too large. But i can suggest the small step to work with this. Your auction scheme will need to have a endDateTime to store the value (even it will start/end in Sunday). On the template you need to display the timer, set one ReactiveVar as number (to count down), one ReactiveVar as string (to display to result)
Template['countDownTemplate'].created = function() {
var due, dueDate, duration, now, num, self;
self = this;
dueDate = Template.instance().data['auction']['endDateTime'];
now = moment.utc();
due = moment.utc(dueDate, 'YYYY-MM-DD hh:mm:ss');
duration = moment.duration(due.diff(now));
num = Math.floor(duration.asSeconds());
if (num >= 0) {
self['remaining'] = new ReactiveVar<number>(num);
self['timeRemaining'] = new ReactiveVar<string>(convertPeriod(num));
self['interval'] = Meteor.setInterval((function() {
var remaining;
remaining = self['remaining'].get();
self['remaining'].set(remaining - 1);
self['timeRemaining'].set(convertPeriod(self['remaining'].get()));
if (remaining === 0) {
Meteor.clearInterval(self['interval']);
} else {
remaining = Math.floor(moment.duration(due.diff(now)).asSeconds());
}
}), 1000);
}
};
(the convertPeriod will be based on the remaining number to convert into your correct format)
The rest is just about showing timeRemaining in the correct format with the convertPeriod

How can we specify custom date range with fullcalendar?

I want to use fullcalendar with custom date range for ex. it should display view for particular date range like from 15th April to 4th May(Spans between two months).
Any suggestions?.
you can call this function to gt events in date range. but this will bring you only 30 days evnt. if you pass dates like '01-may-2013' to 15-June-2013' then it will show data from 01-may2013 to 30st may 2013. Lt me know if you can find any clue for this issue.
function GetAgendaEvents(datefrom, dateTo) {
var fromDate = new Date($("#from").val());
var toDate = new Date($("#to").val());
if (fromDate.getTime() <= toDate.getTime()) {
$('#fullcal').fullCalendar('removeEvents').fullCalendar('addEventSource', events);
$('#fullcal').fullCalendar('refetchEvents');
var filteredEvent = $('#fullcal').fullCalendar('clientEvents', function (event) {
return event.start >= fromDate && event.start <= toDate;
});
$('#fullcal').fullCalendar('gotoDate', fromDate.getFullYear(), fromDate.getMonth(), fromDate.getDate());
$('#fullcal').fullCalendar('changeView', 'agenda'/* or 'basicDay' */);
$('#fullcal').fullCalendar('removeEvents').fullCalendar('addEventSource', filteredEvent);
$('#fullcal').fullCalendar('refetchEvents');
}
}

Fullcalendar - limit selectable to a single day

By default if you enable the 'selectable' attribute it will allow you to click and drag and select several days. I would like to only allow the user to select a single day, not drag over multiple. Is there a way to have 'selectable' enabled, but disable the dragging feature that comes along with it?
If you want to limit highlight to a single day in agenda week view you can use following:
selectConstraint:{
start: '00:01',
end: '23:59',
},
if you want to limit the event you can use
eventConstraint:{
start: '00:00',
end: '24:00',
},
in the select callback, adding the following does the trick:
(fullcalendar 2 using moment.js)
if (start.add('days', 1).date() != end.date() )
$scope.eventCal.fullCalendar('unselect');
resources:
http://arshaw.com/fullcalendar/docs/selection/select_callback/
http://arshaw.com/fullcalendar/docs/selection/unselect_method/
You can select a single date or time by passing fullcalendar's 'select' method to the dayClick event listener:
$('#myCalendar').fullcalendar({
dayClick: function(date,jsEvent,view) {
$('#myCalendar').fullcalendar('select', date);
}
});
Note you will also need to fire the 'unselect' method on your next callback (or dayClick).
Why not use selectAllow?
Start by converting the start and end times to seconds. Compare that to the number of seconds in a day.
Working Solution Without Using Moment.js:
selectAllow: function (e) {
if (e.end.getTime() / 1000 - e.start.getTime() / 1000 <= 86400) {
return true;
}
}
This configuration setting worked for me on FullCalendar v5:
selectAllow: function(selectionInfo) {
let startDate = selectionInfo.start;
let endDate = selectionInfo.end;
endDate.setSeconds(endDate.getSeconds() - 1); // allow full day selection
if (startDate.getDate() === endDate.getDate()) {
return true;
} else {
return false;
}
}
simply :
selectAllow: function (selectInfo) {
return selectInfo.end.diff(selectInfo.start, 'days') == 1;
}
For me using the selectAllow option like this worked
selectAllow: function(selectionInfo) {
// Don't allow creation of events over more than 1 day
return moment(selectionInfo.start).utcOffset(false).isSame(moment(selectionInfo.end).subtract(1, 'second').utcOffset(false), 'day');
},
I used utcOffset(false) because for whatever reason it doesn't work reliably without it and I used subtract(1, 'second') because the end date is inclusive, so without it you can't select the end of the day
This will be executed only when the user selects a day
// ...
select: function(start, end){
if(moment(start._d).add(1, 'days').format('YYYY-MM-DD')==moment(end._d).format('YYYY-MM-DD')){
// just select one day
}
},
// ...
I could do this using validRange:
https://fullcalendar.io/docs/validRange
Not at this time: the range of selectable days can not be customized without modifying the source.

Resources