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)")
}
What I need to do is use either collection-2 or another package to automatically create a new order number, incremented from the last order number used.
i.e. Starting off with PO123456, when I save this order, the next time I make a new PO, it automatically generates the number PO123457.
I've been looking for a good example or tutorial, but I'm not able to find one.
Using konecty:mongo-counter in conjuntion with aldeed:collection2 and aldeed:simple-schema should be pretty straightforward. In your schema definition try:
POnumber: { type: String, autoValue: function(){
if ( this.isInsert ){ // restrict to when inserting a document
var currentNumber = incrementCounter('purchase order'); // this will use mongo-counter
// WARNING: you can only ever get as rich as 10M POs!!
var zeroPad = "000000" + currentNumber; // pad with 6 zeros
zeroPad = zeroPad.substr(zeroPad.length-7); // restrict to 7 places
return 'PO' + zeroPad; // prefix with 'PO'
} else if ( this.isSet ){
this.unset(); // prevent attempts to change the number
}
}
I am trying to use momentjs to check if a given date is today or in the future.
This is what I have so far:
<script type="text/javascript" src="http://momentjs.com/downloads/moment.min.js"></script>
<script type="text/javascript">
var SpecialToDate = '31/01/2014'; // DD/MM/YYYY
var SpecialTo = moment(SpecialToDate, "DD/MM/YYYY");
if (moment().diff(SpecialTo) > 0) {
alert('date is today or in future');
} else {
alert('date is in the past');
}
</script>
The code is evaluating my date (31st of Jan 2014) as a date in past.
Any idea what I am doing wrong?
You can use the isSame function:
var iscurrentDate = startTime.isSame(new Date(), "day");
if(iscurrentDate) {
}
After reading the documentation: http://momentjs.com/docs/#/displaying/difference/, you have to consider the diff function like a minus operator.
// today < future (31/01/2014)
today.diff(future) // today - future < 0
future.diff(today) // future - today > 0
Therefore, you have to reverse your condition.
If you want to check that all is fine, you can add an extra parameter to the function:
moment().diff(SpecialTo, 'days') // -8 (days)
Since no one seems to have mentioned it yet, the simplest way to check if a Moment date object is in the past:
momentObj.isBefore()
Or in the future:
momentObj.isAfter()
Just leave the args blank -- that'll default to now.
There's also isSameOrAfter and isSameOrBefore.
N.B. this factors in time. If you only care about the day, see Dipendu's answer.
// Returns true if it is today or false if it's not
moment(SpecialToDate).isSame(moment(), 'day');
You can use the isAfter() query function of momentjs:
Check if a moment is after another moment.
moment('2010-10-20').isAfter('2010-10-19'); // true
If you want to limit the granularity to a unit other than milliseconds, pass the units as the second parameter.
moment('2010-10-20').isAfter('2010-01-01', 'year'); // false
moment('2010-10-20').isAfter('2009-12-31', 'year'); // true
http://momentjs.com/docs/#/query/is-after/
Update
moment().isSame('2010-02-01', 'day'); // Return true if we are the 2010-02-01
I have since found the isSame function, which in I believe is the correct function to use for figuring out if a date is today.
Original answer
Just in case someone else needs this, just do this:
const isToday = moment(0, "HH").diff(date, "days") == 0;
or if you want a function:
isToday = date => moment(0,"HH").diff(date, "days") == 0;
Where date is the date you want to check for.
Explanation
moment(0, "HH") returns today's day at midnight.
date1.diff(date2, "days") returns the number of days between the date1 and date2.
invert isBefore method of moment to check if a date is same as today or in future like this:
!moment(yourDate).isBefore(moment(), "day");
To check if it is today:
If we compare two dates which contain also the time information isSame will obviously fail. diff will fail in case that the two dates span over the new day:
var date1 = moment("01.01.2016 23:59:00", "DD.MM.YYYY HH.mm.ss");
var date2 = moment("02.01.2016 00:01:00", "DD.MM.YYYY HH.mm.ss");
var diff = date2.diff(date1); // 2seconds
I think the best way, even if it is not quick and short, is the following:
var isSame = date1.date() == date2.date() && date1.month() == date2.month() && date1.year() == date2.year()
To check if it is in the future:
As suggested also by other users, the diff method works.
var isFuture = now.diff(anotherDate) < 0
If you only need to know which one is bigger, you can also compare them directly:
var SpecialToDate = '31/01/2014'; // DD/MM/YYYY
var SpecialTo = moment(SpecialToDate, "DD/MM/YYYY");
if (moment() > SpecialTo) {
alert('date is today or in future');
} else {
alert('date is in the past');
}
Hope this helps!
Use the simplest one to check for future date
if(moment().diff(yourDate) >= 0)
alert ("Past or current date");
else
alert("It is a future date");
if firstDate is same or after(future) secondDate return true else return false. Toda is firstDate = new Date();
static isFirstDateSameOrAfterSecondDate(firstDate: Date, secondDate: Date): boolean {
var date1 = moment(firstDate);
var date2 = moment(secondDate);
if(date1 && date2){
return date1.isSameOrBefore(date2,'day');
}
return false;
}
There is isSame, isBefore and isAfter for day compare moment example;
static isFirstDateSameSecondDate(firstDate: Date, secondDate: Date): boolean {
var date1 = moment(firstDate);
var date2 = moment(secondDate);
if (date1 && date2) {
return date1.isSame(date2,'day');
}
return false;
}
static isFirstDateAfterSecondDate(firstDate: Date, secondDate: Date): boolean {
var date1 = moment(firstDate);
var date2 = moment(secondDate);
if(date1 && date2){
return date1.isAfter(date2,'day');
}
return false;
}
static isFirstDateBeforeSecondDate(firstDate: Date, secondDate: Date): boolean {
var date1 = moment(firstDate);
var date2 = moment(secondDate);
if(date1 && date2){
return date1.isBefore(date2,'day');
}
return false;
}
I wrote functions that check if a date of Moment type is a Day that Passed or not, as functional and self-descriptive functions.
Maybe it is could to help someone.
function isItBeforeToday(MomentDate: Moment) {
return MomentDate.diff(moment(0, 'HH')) < 0;
}
function isItAfterToday(MomentDate: Moment) {
return MomentDate.diff(moment(0, 'HH')) > 0;
}
Select yesterday to check past days or not with help of moment().subtract(1, "day");
Reference:- http://momentjs.com/docs/#/manipulating/subtract/
function myFunction() {
var yesterday = moment().subtract(1, "day").format("YYYY-MM-DD");
var SpecialToDate = document.getElementById("theDate").value;
if (moment(SpecialToDate, "YYYY-MM-DD", true).isAfter(yesterday)) {
alert("date is today or in future");
console.log("date is today or in future");
} else {
alert("date is in the past");
console.log("date is in the past");
}
}
<script src="http://momentjs.com/downloads/moment.js"></script>
<input type="date" id="theDate" onchange="myFunction()">
function isTodayOrFuture(date){
date = stripTime(date);
return date.diff(stripTime(moment.now())) >= 0;
}
function stripTime(date){
date = moment(date);
date.hours(0);
date.minutes(0);
date.seconds(0);
date.milliseconds(0);
return date;
}
And then just use it line this :
isTodayOrFuture(YOUR_TEST_DATE_HERE)
If we want difference without the time you can get the date different (only date without time) like below, using moment's format.
As, I was facing issue with the difference while doing ;
moment().diff([YOUR DATE])
So, came up with following;
const dateValidate = moment(moment().format('YYYY-MM-DD')).diff(moment([YOUR SELECTED DATE HERE]).format('YYYY-MM-DD'))
IF dateValidate > 0
//it's past day
else
//it's current or future
Please feel free to comment if there's anything to improve on.
Thanks,
i wanted it for something else but eventually found a trick which you can try
somedate.calendar(compareDate, { sameDay: '[Today]'})=='Today'
var d = moment();
var today = moment();
console.log("Usign today's date, is Date is Today? ",d.calendar(today, {
sameDay: '[Today]'})=='Today');
var someRondomDate = moment("2012/07/13","YYYY/MM/DD");
console.log("Usign Some Random Date, is Today ?",someRondomDate.calendar(today, {
sameDay: '[Today]'})=='Today');
var anotherRandomDate = moment("2012/07/13","YYYY/MM/DD");
console.log("Two Random Date are same date ? ",someRondomDate.calendar(anotherRandomDate, {
sameDay: '[Today]'})=='Today');
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
check with following:
let isContinue = moment().diff('2020-04-04T20:06:11+05:30')/1000
it is returning in seconds..
If will check as 2 mins condition then
if (isContinue < 120) {
..To check otp details or further logic
} else {
// otp is getting invalid
}
Simplest answer will be:
const firstDate = moment('2020/10/14'); // the date to be checked
const secondDate = moment('2020/10/15'); // the date to be checked
firstDate.startOf('day').diff(secondDate.startOf('day'), 'days'); // result = -1
secondDate.startOf('day').diff(firstDate.startOf('day'), 'days'); // result = 1
It will check with the midnight value and will return an accurate result. It will work also when time diff between two dates is less than 24 hours also.
With default view set to agendaWeek. If I load fullCalendar at 11:59PM the today day slot is not automatically updated after 12:00AM until I refresh the browser.
This is how I fixed it:
Call a function every 5 minutes (or any interval that suits your requirements)
$(function() {
var tInterval = 5*60*1000;
timelineInterval = window.setInterval(updateFcToday, tInterval); // Update timeline after every 5 minutes
});
Add a function like this to the page that contains your calendar:
function updateFcToday() {
var curTime = new Date();
if(curTime.getHours() == 0 && curTime.getMinutes() <= 5) // this five minutes is same interval that we are calling this function for. Both should be the same
{// the day has changed
var todayElem = $(".fc-today");
todayElem.removeClass("fc-today");
todayElem.removeClass("fc-state-highlight");
todayElem.next().addClass("fc-today");
todayElem.next().addClass("fc-state-highlight");
}
}
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.