So, basically I'm building a rental form where you have three basic dropdowns for Month, Day, Year for a "rent from" and then another three for "rent till."
That's easy enough and I've even got decent leap year support and such. The issue comes with generating the "rent till" based on the "rent from." I'd like the "rent till" to show only the next five days in a dropdown afterwords and I'm having serious difficulty working out logic to do it efficiently.
Also, the range is variable, so I'm trying to work a function in that takes a 'length of rental' parameter to effect the changes. The code also uses reference points. So that the pick up dropdowns are "pick_up_day", "pick_up_month" etc. This is to try to make the code portable to multiple projects with consistently ID'd HTML elements.
Here's my basic logic and what I've got so far:
This is the code that sets up the from-dates and such.
function populate_dates(identifier) {
"use strict";
var i, currentday, node, textbox, maxdays;
while (document.getElementById(identifier + "days").hasChildNodes()) {
document.getElementById(identifier + "days").removeChild(document.getElementById(identifier + "days").firstChild);
}
currentday = new Date();
switch (parseInt(document.getElementById(identifier + "month").value, 10)) {
case 2:
if (new Date(currentday.getFullYear(), 1, 29).getDate() === 29) {
maxdays = 29;
} else {
maxdays = 28;
}
break;
case 9:
case 4:
case 6:
case 11:
maxdays = 30;
break;
default:
maxdays = 31;
break;
}
for (i = 1; i <= maxdays; i = i + 1) {
node = document.createElement("option");
textbox = document.createTextNode(i);
node.value = i;
node.appendChild(textbox);
document.getElementById(identifier + "days").appendChild(node);
}
}
window.onload = function() {
"use strict";
var currentday, i, node, textbox;
currentday = new Date();
for (i = 0; i <= 1; i = i + 1) {
node = document.createElement("option");
textbox = document.createTextNode(currentday.getFullYear() + i);
node.value = currentday.getFullYear() + i;
node.appendChild(textbox);
document.getElementById("pick_up_year").appendChild(node);
}
document.getElementById("pick_up_month").onchange = function () {
populate_dates("pick_up_");
};
populate_dates("pick_up_");
};
Now for the logic regarding the 'till' date I have worked out some pseudocode like this, but it seems overly complex for such a simple task:
function a(referencer, ref1, ref2, rentlength) {
//Make datetime object out of ref1's current date;
datetime.setDays(datetime.getDate() + rentlength)
for (i = 0; i <= rentlength; i = i + 1) {
datetime.setDays(datetime.getDate() + i);
outmatrix[outmatrix.length] = [getDates, getMonth, getYear];
}
outmatrix.removeduplicates;
date_generator(ref2, outmatrix)
}
function date_generator(referencer, inputmatrix) {
Clean up referencer
Loop through, create nodes based on array[y+1][x];
}
Is there a better way to do this that I'm just not seeing? What is a good way to turn a Datetime object into three dropdowns while removing duplicates? Is there a better way to handle this than the way I am currently?
In terms of the logic, you'll need to set the date to the sum of the start date and the rental time, in DATE format.
Related
I'm trying to create a stored proc in snowflake which checks if the 1st of the month is a bank holiday or a weekend, and updates it to the next working day. it seems to be getting stuck in a loop. I have the bank holidays stored in a table so I want to check if the date matches any values in that table and if it does, to take the next working day.
create or replace procedure check_dt(run_dt DATE)
returns string
language javascript
execute as caller
as
$$
function batch_date(ts){
var a = new Date(ts);
var dt = new Date(a.setDate(1));
var flag = false;
while(flag==false){
var sql_stment = `select 1 from calender where bh_date = date'` + dt.toLocaleDateString() + `'`;
var result = snowflake.execute({sqlText: sql_stment});
while(result.next()){
if(result.getColumnValue(1) == 1){
dt.setDate(dt.getDate() + 1);
}
else if (dt.getDay() == 0){
dt.setDate(dt.getDate() + 1);
}
else if(dt.getDay() == 6){
dt.setDate(dt.getDate() + 2);
}
else{
flag = true;
break;
}
}
}
var batch_dt = batch_date(RUN_DT);
return batch_dt;
$$;
You're never exiting the outer loop. You can see that by removing the code from the loops to simplify:
while(flag==false){
while(result.next()){
...
break;
}
}
The break statement will break out of the inner loop only. There's no break statement for the outer loop. There's also no line changing the value of the flag variable.
I've realised by using the result.next() if the date doesn't exist in the table it never reaches the clause to update the flag, so ends in an infinite loop. the below has fixed my problem. By using row count instead, if no values are present it will continue through the if-else statement
create or replace procedure check_dt(run_dt DATE)
returns string
language javascript
execute as caller
as
$$
function batch_date(ts){
var a = new Date(ts);
var dt = new Date(a.setDate(1));
var flag = false;
while(flag==false){
var sql_stment = `select 1 from calender where bh_date = date'` + dt.toLocaleDateString() + `'`;
var result = snowflake.execute({sqlText: sql_stment});
if(result.getRowCount() > 0){
dt.setDate(dt.getDate() + 1);
}
else if (dt.getDay() == 0){
dt.setDate(dt.getDate() + 1);
}
else if(dt.getDay() == 6){
dt.setDate(dt.getDate() + 2);
}
else{
flag = true;
}
}
var batch_dt = batch_date(RUN_DT);
return batch_dt;
$$;
I am creating a calendar in Angular that has a previous & next month button that currently displays the correct data of that month when I press either the next or previous button only once. The question that I have is how can I increment the months when clicking on either the previous or next button. I have looked at Temporal's Documentation a good bit but I still am confused on how to simply increment the months. Here is some of the code I have so far. Any help would be greatly appreciated!
currentMonth = Temporal.Now.plainDateISO();
nextMonth = this.currentMonth.add({ months: 1 });
previousMonth = this.currentMonth.subtract({ months: 1 });
daysInCurrentMonth = this.currentMonth.daysInMonth;
daysInNextMonth = this.nextMonth.daysInMonth;
daysInPreviousMonth = this.previousMonth.daysInMonth;
onStartMonth() {
for (let i = 1; i <= this.daysInCurrentMonth; i++) {
let div = this.calendarData.nativeElement;
let calendarDates = this.renderer.createElement('div');
this.add(calendarDates, 'dates');
this.set(calendarDates, 'innerText', i);
this.append(div, calendarDates);
}
}
onNextMonth() {
for (let i = 1; i <= this.daysInNextMonth; i++) {
let div = this.calendarData.nativeElement;
let calendarDates = this.renderer.createElement('div');
this.add(calendarDates, 'dates');
this.set(calendarDates, 'innerText', i);
this.append(div, calendarDates);
}
}
onPreviousMonth() {
for (let i = 1; i <= this.daysInPreviousMonth; i++) {
let div = this.calendarData.nativeElement;
let calendarDates = this.renderer.createElement('div');
this.add(calendarDates, 'dates');
this.set(calendarDates, 'innerText', i);
this.append(div, calendarDates);
}
}
Your comment: "Since the current month is August; when I press the next month button, I always get September, likewise when I press the previous month I always get July"
From that comment, your issue is not with the Temporal API. The problem is that you've essentially hard-coded the current, next and previous months when you should probably be updating these dynamically (as well as the respective days in month. When you press the next button you should update current month to be the next month and then recalcuate daysInCurrentMonth.
currentMonth = Temporal.Now.plainDateISO();
// Reset to this month
onStartMonth() {
this.currentMonth = Temporal.Now.plainDateISO();
renderCalendar();
}
// move to next month
onNextMonth() {
this.currentMonth = this.currentMonth.add({ months: 1 });
renderCalendar();
}
// move previous month
onPreviousMonth() {
this.currentMonth = this.currentMonth.subtract({ months: 1 });
renderCalendar();
}
// Render the calendar
renderCalendar(){
const daysInCurrentMonth = this.currentMonth.daysInMonth;
const div = this.calendarData.nativeElement;
for (let i = 1; i <= daysInCurrentMonth; i++) {
let calendarDates = this.renderer.createElement('div');
this.add(calendarDates, 'dates');
this.set(calendarDates, 'innerText', i);
this.append(div, calendarDates);
}
}
You now only care about the current tracked month and can easily navigate forward or backward any number of times. Your rendering got much simpler too as any changes you need to make can be made in that 1 function without having to be replicated multiple times.
If you still need next and previous month info (incl days in months) then you should probably recalculate these as well when current month changes.
If you have an array of appointments with start and end dates how do you calculate the unique time for all of the appointments?
Example:
var appointments = {
0:{"start":"2015-01-20 09:00:00","end":"2015-01-20 09:30:00"},
1:{"start":"2015-01-20 09:15:00","end":"2015-01-20 09:42:22"},
2:{"start":"2015-01-20 10:00:00","end":"2015-01-20 10:25:00"},
3:{"start":"2015-01-20 10:10:00","end":"2015-01-20 10:53:00"}
}
So in this example I would want to get a unique time (activity) value of 1H 35M 22S.
Anyone know any formulas for this?
So far I have this, seems to work but I think dates have to be sorted by start time. Is this the most efficient way to calculate this?:
var totalElapsedAppointmentSeconds = 0;
var lastActiveTimestamp;
for (i in appointments) {
if (totalElapsedAppointmentSeconds == 0) {
totalElapsedAppointmentSeconds = new Date(appointments[i].end) - new Date(appointments[i].start);
lastActiveTimestamp = new Date(appointments[i].end);
} else {
if (new Date(appointments[i].start) < lastActiveTimestamp) {
if (new Date(appointments[i].end) > lastActiveTimestamp) {
totalElapsedAppointmentSeconds += new Date(appointments[i].end) - lastActiveTimestamp;
lastActiveTimestamp = new Date(appointments[i].end);
} else {
//nothing, already completely accounted for
}
} else {
totalElapsedAppointmentSeconds += new Date(appointments[i].end) - new Date(appointments[i].start);
lastActiveTimestamp = new Date(appointments[i].end);
}
}
}
totalElapsedAppointmentSeconds = totalElapsedAppointmentSeconds/1000;
var totalElapsedTime = Math.floor(totalElapsedAppointmentSeconds / 3600) + "H " + Math.floor((totalElapsedAppointmentSeconds % 3600)/60) + "M " + (totalElapsedAppointmentSeconds % 3600) % 60 + "S";
console.log("totalElapsedTime",totalElapsedTime);
unclear what you are asking but this demonstrates calculating a time difference
EDIT whoops javascript says these are invalid dates, where did they come from?
moment.js is a good option to parse them if you must use these as inputs
var data = {
"appointments": {
0:{"start":"2015-01-20 09:00:00","end":"2015-01-20 09:30:00"},
1:{"start":"20-01-2015 09:15:00","end":"20-01-2015 09:42:22"},
2:{"start":"20-01-2015 10:00:00","end":"20-01-2015 10:25:00"},
3:{"start":"20-01-2015 10:10:00","end":"20-01-2015 10:53:00"},
}
}
function secondsDifference(ts1, ts2){
startMs = new Date(ts1).valueOf();
endMs = new Date(ts2).valueOf();
deltaMs = endMs - startMs;
deltaS = deltaMs /1000;
deltaS = Math.floor(deltaS);
return deltaS;
}
var a = data.appointments[0];
var result = secondsDifference(a.start, a.end);
console.log('first appointment length seconds:', result)
I'm trying to sort a JS array, but in a different fashion. Here's my scenario:
Say we have the following dates in the array (these will be each item's expiryDate)
1. 2015/09/23
2. 2015/10/10
3. 2015/07/05
4. 2015/07/24
And the current date is 2015/07/04
And we select 2015/09 (which is vm.sortModel in the example) as the sorting date
The display order must be as follows -
3.
1.
4.
2.
Here's what I have so far, but it's obviously not working...
for (var i = 0; i < vm.tableData.length; i++) {
var entity = vm.tableData[i];
entity.items.sort(function(a, b) {
if (a.expiresThisWeek() < b.expiresThisWeek()) {
return 1;
}
//Now check for stuff in months AFTER the selected year/month combo
if (vm.sortModel) {
var tmp = vm.sortModel.split('/');
var currentDateSelection = new Date(tmp[0], tmp[1]);
if (a.getExpiryDateObject() >= currentDateSelection) {
return 1;
} else {
return -1;
}
}
if (a.getExpiryDateObject() < b.getExpiryDateObject()) {
return -1;
} else if (a.getExpiryDateObject() > b.getExpiryDateObject()) {
return 1;
}
return 0;
});
}
Basically, if an item expires in this week, it must be at the top of the table, then we should list all the items in the "selected month", then all the months AFTER that month, and then only the months before that.
Very strange, I know, but business gets what business wants.
Thanks for any help!
EDIT
Here's the expiresThisWeek method
model.expiresThisWeek = function() {
var todayDate = new Date();
//We want to check if the item expires within 7 days from today
var endDate = new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate() + 7);
var date = model.getExpiryDateObject();
return todayDate < date && date < endDate;
}
Dygraphs options provide 'rollPeriod' to support rolling averages and 'stepPlot' to support step plots. When set together when some data is missing in between, they give very unexpected results. For example, attached image link shows graph for original data (rollPeriod=1) and rollPeriod=5. (http://imgur.com/a/9ajh8)
At 40,000 for example, the rolling average must be zero. But, dygraphs takes average of last 5 datapoints instead of last 5 seconds.
Is it possible to get rolling average that maintains notion of time rather than data points. Thanks in advance !
PS- Sorry for image link. SO won't allow me to directly post images due to lack of reputation. :(
As you noticed, dygraphs averages the last five data points, not the last five seconds. This is all it can do, since it doesn't know the cadence of your data. Fortunately, you can fix this by adding explicit missing values:
Datetime,Value
2014-08-01 12:34:55,0
2014-08-01 12:34:56,
2014-08-01 12:34:57,0
2014-08-01 12:34:58,
2014-08-01 12:34:59,0
The zeros are values, the blanks are missing values.
See http://dygraphs.com/data.html for more information, or one of these two demos for examples.
Due to lack of this functionality, I implemented it by myself. I am putting the code here for someone in similar situation. Code uses internal function extractSeries_ in dygraph library and Queue.js. Use with extreme caution !
function calcAvg_(minDate, maxDate, dispData){
var windowSize = Math.round((maxDate-minDate)/100);
if(windowSize <= 1){
return dispData;
}
var energy = 0;
var lastS = new Queue();
var series = dispData;
var lastAvg = 0;
// Initially lastS elements are all 0
// lastS shall always be maintained of windowSize.
// Every enqueue after initialization in lastS shall be matched by a dequeue
for(j=0; j<windowSize; j++){
lastS.enqueue(0);
}
var avg_series = [];
var prevTime = minDate - windowSize;
var prevVal = 0;
avg_series.push([prevTime, prevVal]);
//console.log( "calcAvg_ min: " + minDate + " max: " + maxDate + " win: " + windowSize );
for(j=0; j<series.length; j++){
var time = series[j][0];
var value = series[j].slice(1);
if(time > minDate){
var k = 0;
while(k < windowSize && prevTime + k < time){
var tail = lastS.dequeue();
lastS.enqueue(prevVal);
lastAvg = lastAvg + (prevVal - tail)/windowSize;
avg_series.push([prevTime+k, lastAvg]);
k++;
}
}
prevTime = time;
prevVal = value;
if(time > maxDate){
break;
}
}
if(j == series.length){
//console.log("Fix last value");
var k = 0;
while(k < windowSize && prevTime + k < maxDate){
var tail = lastS.dequeue();
lastS.enqueue(prevVal);
lastAvg = lastAvg + (prevVal - tail)/windowSize;
avg_series.push([prevTime+k, lastAvg]);
k++;
}
}
//console.log(avg_series);
avg_series.push([maxDate, 0]);
return avg_series;
}
var blockRedraw = false;
myDrawCallback_ = function(gs, initial) {
if (blockRedraw) return;
blockRedraw = true;
var range = gs.xAxisRange();
var yrange = gs.yAxisRange();
var series = calcAvg_(range[0], range[1],
gs.extractSeries_(gs.rawData_, 0, false));
gs.updateOptions( {
dateWindow: range,
valueRange: yrange,
file: series } );
blockRedraw = false;
}