Complex date calculation - from & to times

Have a need to display records on a calendar view with a standard duration of 30 minutes, and allow users to select the start date & time only.

So a simple date for user entry, plus a calculated date field to populate the calendar view would work - except we can't work out how to create the formulas to add 30 minutes and have a date field filled with [date] [starttime] to [endtime] format.

We can create another field that contains the end time correctly, and then even a text field combo field with the correct formatting but forcing a complex date field to conditionally format to that value for every record doesn't work (it stays blank).

Any ideas?

Here is a workaround that does not require any javascript:

I used "mid({Date Field},20,100)" text formula to extract the "to" part and then used the resulting text field in a conditional rule for new "End Date" field - and that worked as expected (with mm/dd/YYYY and "military" HH:MM time format)

to deal with same-day intervals you need to extract "to" part then add a current date part: left({Interval Date/Time},10) {End Date Text}

if you need to deal with both- same-day and different-day intervals you will need to apply conditional rules "is blank" and "is not blank" to either use first or second method in the final result

Dustin, if I'm trying to do the opposite, basically, extract and parse a date field into 4 separate fields, and started from your code. Except mine doesn't work. Do I need all the date functions? I didn't think I did.


// Update your view number to match your project
$(document).on('knack-record-create.view_259', function(event, view, record) {

var data = new Object();
// Updated my view number, and field numbers field, trying to parse the data object.
var data = ("#view_259-field_299");
var getData = JSON.parse(data);

alert(getData.date); //output: 02/08/2013
alert(getData.hours); //output: 2
alert(getData.minutes); //output: 0
alert(getData.am_pm); //output: pm

for(i=0;i<getData.to.length;i++)
{
alert(getData[“to”][i].date); //output: 02/11/2013
alert(getData[“to”][i].hours); //output: 2
alert(getData[“to”][i].minutes); //output: 0
alert(getData[“to”][i].am_pm); //output: pm
}

Knack.showSpinner();
// This was originally a call that added the complex date to the database. But I need it to add the new simplified dates 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_135/views/view_259/records/” + record.id ,
type: “PUT”,
headers: {“X-Knack-Application-Id”: “<Knack-Application-ID>”, “X-Knack-REST-API-Key”:"<API-Key>"},
field_275 : Data.date,
field_276 : Data.hours":“Data.minutes” “Data.am_pm,
field_277 : Data.to.date,
field_278 : Data.to.hours”:“Data.to.minutes” "Data.to.am_pm,
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();
}
});
});

I’m new to JavaScript too. I recently went through the courses just at codeschool.com. You can try out a few intro courses free on JavaScript and jQuery then pick up a month to blast through the rest. I didn’t have time to sign up right away, and in waiting they dropped the intro month to $9. Can’t beat that!

Dustin, that's awesome and really appreciate you sharing - I think this community will grow with these types of efforts. I'll look at trying out aspects of this and I wasn't aware of object prototypes in JavaScript either being a newbie.

Thanks

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! :slight_smile: