How to set closest available date? - javascript

Main question: How to set closest available date?
Hi all, i'm using bootstrap-datetimepicker.
I've disabled some days from calendar and sometimes current date in range.
So it disables successfully but I want to preset default date into the input value, if I use defaultDate: 'moment' it sets current date (which is disabled).
How to set closest available date?
Does exist any lifehack to resolve it?
Or should I make it manually?
This is how it works now:
var $datesDisabled = [];
$.each($dates, function (key, value) {
$datesDisabled.push(moment(value))
});
// PARAMS TO DATETIMEPICKER
var $params = {
locale: 'en',
format: 'DD/MM/YYYY',
disabledDates: $datesDisabled,
};
// CHECK IF CURRENT DATE IS IN DISABLED ARRAY
if ($.inArray(moment().format('MM/DD/YYYY'), $dates) === -1) {
// IF NOT PRESET CURRENT DATE
$params.defaultDate = 'moment';
} else {
// DO NOT PRESET
$params.useCurrent = false;
}
$('.datetimepicker').datetimepicker($params);

According to the library documentation, you're not supposed to use defaultDate: 'moment'. They do mention the following:
Accepts: date, moment, string
But it means you can provide a Date object, a moment() object or a date string. The string "moment" is none of those, so it probably defaults to today.
However, you can configure the defaultDate like this:
// Using a date string
$('#my-date-picker').datetimepicker({
defaultDate: '2016-08-20'
});
// Using a Date object
$('#my-date-picker').datetimepicker({
defaultDate: new Date('2016-08-20')
});
// Using a moment.js object
$('#my-date-picker').datetimepicker({
defaultDate: moment('2016-08-20')
});
But if you want to have the defaultDate the closest date according to your disabled dates, you'll have to calculate it by yourself. For such a functionality you could create a recursive function that recalls itself with the given date minus or plus 1 until it finds one that is not disabled.
For example:
var disabled = [
new Date('2016-01-03'),
new Date('2016-01-04'),
new Date('2016-01-05'),
new Date('2016-01-08')
];
function getClosest(date, disabled, direction) {
if(!containsDate(disabled, date)) {
return date;
} else {
var prev = getClosest(date.clone().add(direction || -1, 'days'), disabled, direction || -1),
next = getClosest(date.clone().add(direction || 1, 'days'), disabled, direction || 1);
if (Math.abs(date.diff(prev, 'days')) > Math.abs(date.diff(next, 'days'))) {
return next;
} else {
return prev;
}
}
}
function containsDate(dates, given) {
return dates.some(function(date) {
return given.isSame(date, 'day');
});
}
console.log(getClosest(moment('2016-01-02'), disabled).toDate());
console.log(getClosest(moment('2016-01-03'), disabled).toDate());
console.log(getClosest(moment('2016-01-04'), disabled).toDate());
console.log(getClosest(moment('2016-01-05'), disabled).toDate());
console.log(getClosest(moment('2016-01-06'), disabled).toDate());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.14.1/moment.min.js"></script>

Related

Moment.Js calculate difference datetime and show result

Library moments.js included.
I have a variable "lastMessage.created_at"
If it today's date to display then time in the format "h:mm a" if it was yesterday, display "Yesterday" or before that "D.M.Y", how do I do this ?
You could create a function to format input dates, these can be strings (in supported formats), Dates or moment objects.
We'll then output a different format based on whether the date is the same or after the start of today, the start of yesterday, or before that.
function formatDisplayDate(input) {
const dt = moment(input);
const startOfToday = moment().startOf('day');
const startOfYesterday = moment().startOf('day').subtract(1, 'day');
if (dt.isSameOrAfter(startOfToday)) {
return dt.format('h:mm a');
} else if (dt.isSameOrAfter(startOfYesterday)) {
return 'Yesterday';
} else {
// Before yesterday
return dt.format('D.M.Y');
}
}
const inputs = [ new Date().toLocaleString('sv'), new Date(Date.now() - 86400000).toLocaleString('sv'), new Date(Date.now() - 4*86400000).toLocaleString('sv') ];
for (let input of inputs) {
console.log(`Input: ${input}, output: ${formatDisplayDate(input)}`);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" referrerpolicy="no-referrer"></script>

Javascript yyyy-mm-dd converting to incorrect date (mm/dd/yyyy)

Here is the code where the user enters a date: (it has to be a date picker on my end, but the form has to submit the date field as text – don’t ask)
On submit, I call validation logic in javascript. I’ve attached a screenshot of what it looks like when I try to enter 01/01/2001 as the users birthday. It looks like when I’m converting the value string to a Date object, it’s converting to the wrong date and time. If it would just convert correctly, I could adjust the month and day and year and build a string to send in my second object.
Attaching the picture…
I’ve messed around with UTC and timezones, but to no avail.
I need my output to be a text string "01/01/2001" which I can build as long as I have the correct date going in..but it seems to calculate wrong no matter what I try.
When you construct the Date it is assumed that the string represents a time in UTC since no timezone was provided. The string is parsed as UTC, but the Date object uses your browser's local timezone.
One way to fix that is to use getUTCDay instead of getDay. Same applies to month and year.
Using a jquery datapicker library did the trick.
function initDatePickers() {
jQuery('.om-datepicker-trigger').click(function () {
var defaultDatePickerOptions = {
showOtherMonths: true,
changeMonth: true,
changeYear: true,
defaultDate: '-45y',
dateFormat: 'mm/dd/yy',
beforeShow: function (input, inst) {
var widget = jQuery(inst).datepicker('widget');
widget.css('margin-left', jQuery(input).outerWidth() + 3 -
widget.outerWidth());
},
//buttonImage: "/img/button_calendar.png",
//buttonImageOnly: true,
showOn: "both"
};
var $input = jQuery(this).parent().find('.om-input-date').first();
if ($input.hasClass('om-min-date-today')) {
var minDateTodayOptions = defaultDatePickerOptions;
minDateTodayOptions.defaultDate = 0;
minDateTodayOptions.minDate = 0;
$input.datepicker(minDateTodayOptions);
$input.datepicker('show');
} else {
$input.datepicker(defaultDatePickerOptions);
$input.datepicker('show');
}
});
jQuery('.om-input-date').click(function () {
jQuery(this).next('.om-datepicker-trigger').trigger('click');
});
// Datepicker
// --------------------------------------------------------
jQuery('.om-input-date').keyup(function () {
var inputDOBBox = jQuery(this);
var dateValue = inputDOBBox.attr('value');
if (dateValue.length == 3 || dateValue.length == 6) {
var first = dateValue.substring(0, dateValue.length - 1);
var last = dateValue.substring(dateValue.length - 1);
if (last != "/" && last != "-") {
inputDOBBox.attr('value', first + "/" + last);
}
}
});

How can I dynamically set an end date based on a given start date and term duration?

I want to dynamically set the Term End date based on the Term Type (annual, semiannual, quarter, month) and the Term Start date.
So if we have a term that is set to annual and the start date is set to 03/10/2016, the term end should be set to 03/10/2017.
Here's what I have so far, but not sure how to work in casing the Term Type (annual == 12 months, etc.). I'm also getting a console error for the format of the date. Any help or pointers would be greatly appreciated.
.field
= f.label :term_start_date, 'Term Start', class: 'label'
.input
= f.date_field :term_start_date, id: 'termStart'
.field
= f.label :term_end_date, 'Term End', class: 'label'
.input
= f.date_field :term_end_date, id: 'termEnd'
:javascript
$("#termStart").val();
$("#termEnd").val({
onSelect: function () {
var toDate = $(this).val('getDate');
toDate.setDate(toDate.getDate()+7)
$("#termStart").val("setDate", toDate);
}
});
The specified value "[object Object]" does not conform to the required format, "yyyy-MM-dd".
What you're attempting isn't difficult, but there's a bit of work to do. A date library can help, but it doesn't take a lot of effort to write your own methods. You need to do a few things:
Parse and validate the input start date
Get the term and add some months to the start date
Handle invalid input
Display the result
The following doesn't use any jQuery (you can use it if you like but it doesn't offer any benefits) and should give you some idea of what needs to be done.
var dLib = {
// Given a date string in format m/d/y, return a Date instance
parseMDY: function (s) {
var b = s.split(/\D+/);
var d = new Date(b[2], --b[0], b[1]);
return /\d{1,2}\/\d{1,2}\/\d{1,4}/.test(s) &&
d.getMonth() == b[0]? d : new Date(NaN);
},
// Given a Date, return date string formatted as mm/dd/yyyy
formatMDY: function (date) {
function z(n){return (n<10?'0':'')+n}
if (isNaN(date)) return date.toString();
return z(date.getMonth() + 1) + '/' + z(date.getDate()) + '/' + date.getFullYear();
},
// Add months to a date. If end date itn's equal to start date,
// must have gone past end of month so set to last day of month
addMonths: function (date, months) {
var startDate = date.getDate();
date = new Date(+date);
date.setMonth(date.getMonth() + Number(months))
if (date.getDate() != startDate) {
date.setDate(0);
}
return date;
},
};
function setTermEnd(el) {
var form = el.form;
var term = form.termInMonths.value;
var startDate = dLib.parseMDY(form.termStart.value);
// Check that a valid date was entered. If no date entered
// yet, do nothing. Else, put error message in termEnd
if (isNaN(startDate)) {
form.termEnd.value = form.termStart.value == ''? '' : 'Term start date is invalid';
// Add months and set term end
} else {
form.termEnd.value = dLib.formatMDY(dLib.addMonths(startDate, term));
}
}
// Attach listeners and run to initialise
window.onload = function() {
var form = document.forms[0];
form.termInMonths.onchange = function() {setTermEnd(this)};
form.termStart.onchange = function() {setTermEnd(this)};
form.termStart.onchange(form.termStart);
}
.hint {
color: #999999;
font-size: 75%;
}
<form>
<table>
<tr><td>Term:<td><select name="termInMonths">
<option value="12" selected>Annual
<option value="6">Semi-annual
<option value="3">Quarter
<option value="1">Month
</select>
<tr><td>Term start:<td><input name="termStart" value="1/31/2016"><span class="hint">m/d/y</span>
<tr><td>Term End:<td><input name="termEnd" readonly><span class="hint">m/d/y</span>
</table>
</form>
Javascript's built-in datetime manipulation is very limited.
Your best bet is to use a javascript datetime library. moment.js is a good one
Your second best bet is going to be to work with the time in seconds as that is how javascript does it. Use Date.parse(toDate) to convert it to seconds since the epoch, then add the number of seconds, then convert it back to a date.

How does beforeShowDay method work? (Jquery UI datepicker)

I have been trying to find a clear explanation for a significant amount of time now but I just can't seem to understand how this method works. Below is the official documentation from the Jquery UI API. It may be clear to others but I find it a bit vague. I simply want to take an array of dates and disable them. I am able to make all dates not selectable but not the ones I want.
beforeShowDayType: Function( Date date )
Default: null
A function that takes a date as a parameter and must return an array with:
[0]: true/false indicating whether or not this date is selectable
[1]: a CSS class name to add to the date's cell or "" for the default presentation
[2]: an optional popup tooltip for this date
The function is called for each day in the datepicker before it is displayed.
This is my (incomplete) code so far.
$(document).ready(function() {
var array = ["2014-01-03","2014-01-13","2014-01-23"];
$('#fromDate').datepicker({
dateFormat: "yy-mm-dd",
beforeShowDay: function(date) {
{
return [false, "", "Booked out"];
} else {
return [true, "", "available"];
}
}
});
});
Try this:
beforeShowDay: function(date) {
if($.inArray($.datepicker.formatDate('yy-mm-dd', date ), array) > -1)
{
return [false,"","Booked out"];
}
else
{
return [true,'',"available"];
}
}

jQuery datepicker onChangeMonthYear doesn't fire on current month

I'm using the datepicker inline, highlighting the dates that has events. Every time the month changes, it has to load the new events from the server. The problem is, the onChangeMonthYear only fires on months different from the current month. For instance, if the is august, it fires on all previous months (july, june, may...) and all following months (september, october...), but not when I change back to august. This is my code:
function highlightDates(date) {
for ( var i = 0; i < agenda.length; i++) {
var start = agenda[i].start.toShortDate();
var end = agenda[i].end.toShortDate();
if (start >= date && end <= date) {
return [ true, 'dateHasEvent' ];
}
}
return [ true, '' ];
}
Date.prototype.toShortDate = function() {
var date = new Date(this.getFullYear(), this.getMonth(), this.getDate());
return date;
};
function monthChanged(year, month, instance) {
var date = new Date(year, month - 1);
$.getJSON('json.do', {
time : date.getTime()
}, updateCalendars);
}
function updateCalendars(data, status) {
agenda = data;
$('#calendar').datepicker('refresh');
}
var agenda = [];
$(function() {
$('#calendar').datepicker( {
beforeShowDay : highlightDates,
onChangeMonthYear : monthChanged,
changeMonth : false,
changeYear : false,
showButtonPanel : false
});
});
Does anybody know if this is a bug or expected behaviour?
Well, that was stupid on my part. The problem was the getJSON method. It is apparently very picky about what json it accepts. So after I produced correct json, everything worked as it should.

Categories

Resources