I needed to figure out how to work with dates and modifying records for my project so I decided to tackle this as a learning exercise. So here I'll share my experience. For the record, time + computers is HARD.
With that in mind, I tried to use built in Date functions where I could, but because of the way input is formatted and the objects are constructed for the database, I did end up making some specific routines for this problem.
Let's first look at these Date functions. I've added them as an extension of the Date Object type here:
Date.prototype.dateAdd = function (interval, units) {
switch(interval.toLowerCase()) {
case 'year' : this.setFullYear(this.getFullYear() + units); break;
case 'quarter': this.setMonth(this.getMonth() + 3*units); break;
case 'month' : this.setMonth(this.getMonth() + units); break;
case 'week' : this.setDate(this.getDate() + 7*units); break;
case 'day' : this.setDate(this.getDate() + units); break;
case 'hour' : this.setTime(this.getTime() + units*3600000); break;
case 'minute' : this.setTime(this.getTime() + units*60000); break;
case 'second' : this.setTime(this.getTime() + units*1000); break;
default : return false;
}
return this;
};
Date.prototype.knackDate = function () {
knDate = new Object();
knDate = {am_pm: (this.getHours() > 12 ? “PM” : “AM”),
date: (this.getMonth() < 9 ? “0” : “”) + (this.getMonth()+1) + “/” +
(this.getDate() < 10 ? “0” : “”) + this.getDate() + “/” +
this.getFullYear(),
date_formatted: (this.getMonth() < 9 ? “0” : “”) + (this.getMonth()+1) + “/” +
(this.getDate() < 10 ? “0” : “”) + this.getDate() + “/” +
this.getFullYear(),
hours: (this.getHours() < 10 ? “0” : “”) + this.getHours(),
minutes: (this.getMinutes() < 10 ? “0” : “”) + this.getMinutes(),
timestamp: (this.getMonth() < 9 ? “0” : “”) + (this.getMonth()+1) + “/” +
(this.getDate() < 10 ? “0” : “”) + this.getDate() + “/” +
this.getFullYear().toString().substr(-2, 2) + "Y " +
(this.getHours() < 10 ? “0” : “”) + this.getHours() + “:” +
(this.getMinutes() < 10 ? “0” : “”) + this.getMinutes() + " " +
(this.getHours() > 12 ? “pm” : “am”),
unix_timestamp: this.valueOf()
}
return knDate;
};
The .dateAdd(interval, units) function is used on a JavaScript Date object. From the switch statement you can see the types of intervals. We will use this to find when 30 minutes from the start time is. I did slight overkill on this function so that others could use it to add (or subtract with a negative value in the unit parameter) time from their start as needed.
The .knackDate() function is used to return an object in the format that is used by the Knack code to populate a Date field in a record. I did not experiment with the additional flags for recurrence so I’m not certain how that Object would need to be constructed, but to make a simple Start - End object, a .to attribute is added to a “knackDate” with another “knackDate” inside it.
As for the database design, we will need to have two Date/Time fields for this process. As an example, I named mine “Appointment Start” to get the input from the user and “Appointment” to store the complex start-end style date. For the “Appointment Start” field, I set it as Required: No, Date Format: mm/dd/yyyy, Time Format: HH:MM am, Options: simple, Defalut Date: Current Date (Optional), Default Time: Specific 7:00 (Optional). You can configure the Defaults as you’d like on this field. For the “Appointment” field, I set it as Required: No, Date Format: mm/dd/yyyy, Time Format: HH:MM am, Options: Yes add options, Defalut Date: None, Default Time: None. It is important that the default is None so that the field hides properly on the Add form later. You can then have the rest of the fields as normal, no separate Database object is needed.
In the Interface, both fields will need to be added to the form. Under Form Rules - Display Rules we need to hide the date input that we don’t want the user to see. Set an “If Appointment is blank, Then Hide Appointment” rule for this purpose.
With these new tools in hand we can go about updating a record when it is submitted with this code. You will need to know your scene, view and field IDs, here is Knack’s Guide.
// Update your view number to match your project
$(document).on(‘knack-record-create.view_395’, function(event, view, record) {
var start, end, data = new Object();
// Update your view number, and field number is your “Start” field
var enteredDate = ("#view_395-field_379").val(),
time = ("#view_395-field_379-time").val().match(/(\d+)(?::(\d\d))?\s*(p?)/);
Knack.showSpinner();
start = new Date( enteredDate ); // Gets Date portion of input
start.setHours( parseInt(time[1], 10) + ( ( parseInt(time[1], 10) < 12 && time[3] ) ? 12 : 0) ); Gets Hour portion of input
start.setMinutes( parseInt(time[2]) || 0 ); // Gets minute portion of input
end = new Date(start); // Creates an end Date object based on the start
end.dateAdd(“minute”, 30); // Adds 30 minutes (customize as necessary)
// create data to update record, this is the complex “Appointment” field number
data.field_381 = start.knackDate();
data.field_381.to = end.knackDate();
// This is the call that actually adds the complex date to the database
// You will need to update with your own <Application ID> and <API Key>
// Also update with the correct scene and view numbers.
$.ajax({
url: “https://api.knackhq.com/v1/scenes/scene_<204>/views/view_<395>/records/” + record.id ,
type: “PUT”,
headers: {“X-Knack-Application-Id”: “<Application ID>”, “X-Knack-REST-API-Key”:"<API Key>"},
data: data,
success: function(response) {
console.log(“Record Updated with this data:”);
console.dir(data);
},
error: function(response) {
alert(‘Update Failed!’);
console.log( response.error );
},
complete: function () {
Knack.hideSpinner();
}
});
});
Granted there is only minimal error checking here, but the input is pretty sanitized due to the Knack engine cleaning up the Date entry prior to it being submitted.
This should get you on your way though. If you make any refinements, please share!