For a website blocking/productivity app written in javascript, I'd like to have a user input a series of time ranges available for free time (in his local time). Example:
06:00-06:30
12:30-13:00
23:45-00:15
24 format is not required (I'm just assuming it will be easier)
The program should then check the specified free time ranges against the current time to determine whether block mode is on or off.
My rough idea is to use an inbuilt Javascript function to grab the current DateTime, and convert it to 24 hour format if necessary. I think the time ranges will be in an array. I'm not sure how to check if the current date is within a range in the array.
The logic must also be able to handle the crossover from 23:00 to 0:00.
Anyways that is my line of thought. Thanks for any suggestions including ones that take a different path.
Anyways that is my line of thought. Thanks for any suggestions including ones that take a different path.
If you want to check if current time is in range then the range should be DATE + TIME as current time is DATE + TIME
Possible solution
You can convert all three datetimes to moment instances and just use
start.isBefore(current)
&& current.isBefore(end)
Moment docs on isBefore and isAfter : https://momentjs.com/
"I'm not sure how to check if the current date is within a range in the array."
You'd loop through the array and compare. I'd suggest storing the ranges as an array of objects where each individual range is something like {start: "10:15", end: "10:45"}.
JavaScript doesn't have time objects, only Date that includes date and time information. So I think it would be easiest to convert the current time to the "hh:mm" format as a string and then just do simple string compares against your range values, rather than worrying about converting the range values to date objects. So:
var timeRanges = [
{ start: "06:00", stop: "06:30" },
{ start: "12:30", stop: "13:00" },
{ start: "23:45", stop: "00:15" }
]
function isTimeInRange(time) {
for (var i = 0; i < timeRanges.length; i++) {
var range = timeRanges[i]
if ((time > range.start && time < range.stop)
|| (range.start > range.stop && (time > range.start || time < range.stop))) {
return true
}
}
return false
}
But you can make the code neater, and shorter, using the array .some() method, with or without arrow functions:
var timeRanges = [
{ start: "06:00", stop: "06:30" },
{ start: "12:30", stop: "13:00" },
{ start: "23:45", stop: "00:15" }
]
function isTimeInRange(time) {
return timeRanges.some(range =>
(time > range.start && time < range.stop)
|| (range.start > range.stop && (time > range.start || time < range.stop))
)
}
function pad(d) {
return ("0" + d).slice(-2)
}
function getCurrentTime() {
var now = new Date()
return pad(now.getHours()) + ":" + pad(now.getMinutes())
}
console.log("Current time in range? " + isTimeInRange(getCurrentTime()))
// test some other times
console.log(isTimeInRange("06:15")) // true
console.log(isTimeInRange("12:35")) // true
console.log(isTimeInRange("13:30")) // false
console.log(isTimeInRange("23:50")) // true
console.log(isTimeInRange("00:14")) // true
Obviously you can change < and > to <= and >= if desired.
Try it
start = "10:10";
end = "16:30";
function isInRange(start, end, current){
start = convertTimeToMinute(start);
end = convertTimeToMinute(end);
current = convertTimeToMinute(current);
return start <= current && current <= end;
}
function convertTimeToMinute(time){
return +time.slice(0,1)*60 + +time.slice(3,4);
}
console.log(isInRange(start,end, "15:20"));
console.log(isInRange(start,end, "20:20"));
Related
I have a bunch of timestamps that have the following format: Year:Month:Day:Hour:Minute:Second, for example, 2017:01:01:23:59:59. All domains are zero-padded decimal numbers.
I am trying to write a function to determine if a given timestamp is within a range:
function isBetween(start, end, toCompare) {
}
for example, isBetween('2017:01:01:23:59:58', "2017:01:02:23:59:58", "2017:01:01:23:59:59") should return true as "2017:01:01:23:59:59" is between '2017:01:01:23:59:58' and "2017:01:02:23:59:58"
I couldn't find a clean way to do it. Can someone help me with this?
In JavaScript, Date objects can be compared fairly easily. However, as you've probably noticed, the format of the string you provided is not a format that can be parsed by JavaScript's Date object, so we will first have to fix that. Fortunately, this format is extremely predictable.
The first thing I notice is that the "Month" and "Date" are preceded by a zero if they're a single digit. This means that the date portion is always the exact same amount of characters (10). Because this is the case, we can use String.prototype.substring() to get the first 10 characters for the date, and get everything after the 11th character to get the time while skipping the colon in the middle.
var datetime = "2017:01:01:23:59:58";
var date = datetime.substring(0, 10);
var time = datetime.substring(11);
console.log("Date: " + date);
console.log("Time: " + time);
Now that they're separate, all we need to do is replace the colons in the date with forward slashes, then concatenate it with the time separated by a space. After this, we will have a date string in the MM/dd/yyyy hh:mm:ss format, which we can then parse using JavaScript's built in Date class.
var input = "2017:01:01:23:59:58";
var date = input.substring(0, 10).replace(/:/g, "/");
var time = input.substring(11);
var datetime = date + " " + time;
console.log(new Date(datetime));
Now we can throw this into it's own function, then use simple comparison to figure out if toCompare is between start and end.
function isBetween(start, end, toCompare) {
var startDate = convertDate(start);
var endDate = convertDate(end);
var compareDate = convertDate(toCompare);
return compareDate > startDate &&
compareDate < endDate
}
function convertDate(input){
var date = input.substring(0, 10).replace(/:/g, "/");
var time = input.substring(11);
var datetime = date + " " + time;
return new Date(datetime);
}
var between = isBetween("2017:01:01:23:59:58", "2017:01:02:23:59:58", "2017:01:01:23:59:59");
console.log(between)
This could work for you:
function isBetween(start, end, toCompare) {
start = dateGenerator(start)
end = dateGenerator(end)
toCompare = dateGenerator(toCompare)
if(start <= toCompare && toCompare <= end) return true
return false
}
function dateGenerator(str) {
str = str.split(":")
let date = new Date(`${str[0]}-${str[1]}-${str[2]}`)
date.setHours(str[3],str[4],str[5])
return date.valueOf()
}
const truthy = isBetween('2017:01:01:23:59:58', "2017:01:02:23:59:58", "2017:01:01:23:59:59")
console.log(truthy)
Firstly get individual values and add accordingly to Date constructor of JS and set the hours accordingly.
For comparison we can convert this unix figures (valueOf), hence it will be easier to compare.
This may seem as complex approach but it works.
I have an array which contains a set of start/end date objects (time included)
i.e.
results["records"] =
[0] -[startDate,endDate]
[1] -[startDate, endDate]
I also have another two date objects stored locally as JS variables.
How do I check if these variables i.e. startDateObj && endDateObj OVERLAP with ANY record in the array, by that I mean crossover with any time between and including any start date or end date.
Thank you in advance
Initial attempt below
$(results['records']).each(function() {
console.log('end:' + this[1])
console.log('start:' + this[0])
if(startDateObj < this[1].end && endDateObj > this[0].start) {
alert('this overlaps')
}
});
EDIT: Answer added below
Have a great day!
I'm assuming this structure in your "dateArray" because to check overlaps you need to define a range of date and time.
dateArray: [{start: Date, end: Date}];
dateArray.forEach(date => {
if(startDateObj < date.end && endDateObj > date.start) {
//this is an overlap
}
});
The answer was the full date objects in the array where being treated as a string
so to convert and fix
function toDateString(date)
{
var formatedDate = new Date(date);
return formatedDate;
}
$(results['records']).each(function() {
if(startDateObj < toDateString(this[1]) && endDateObj > toDateString(this[0]))
{
//overlaps
}
Am doing a script in which have need of know if the store is open or closed, depending on the time local. Currently, I get the data of an api, which I keep in my database and call it via an Ajax request. The data that return are (equal that which I get):
["Mo-Sa 11:00-14:30", "Mo-Th 17:00-21:30", "Fr-Sa 17:00-22:00"]
I have been assessing the possibility of converting it as well (I still have to see how to do it):
{
"monday": ["11:00-14:30", "17:00-21:30"],
"tuesday": ["11:00-14:30", "17:00-21:30"],
"wednesday": ["11:00-14:30", "17:00-21:30"],
"thursday": ["11:00-14:30", "17:00-21:30"],
"friday": ["11:00-14:30", "17:00-22:00"],
"saturday": ["11:00-14:30", "17:00-22:00"],
"sunday": null
}
I've seen examples in these questions:
Create a function to check if a business is open and write text to html
Determine If Business Is Open/Closed Based On Business Hours (php)
Before programming code with any routine for this, I want to know if anyone knows any way to make it simple or has seen some portion of code on the web; to not reinvent the wheel. Thanks a lot.
Best regards
With opening times, I prefer to convert all values to be compared to full minutes (hour*60 + minutes). That way it's easier to compare to actual times.
Having minutes as starting point, I would do the conversion somewhat different by using an array for each weekday (with the same index as returned by Date.getDay() ), with each day containing sub-arrays with the open start-end times in minutes (start and end also in sub arrays, or in an object)
const arr= ["Mo-Sa 11:00-14:30", "Mo-Th 17:00-21:30", "Fr-Sa 17:00-22:00"],
days = ['Su','Mo','Tu','We', 'Th', 'Fr', 'Sa'], //start with sunday to be compatible with Date.getDay
times = Array.from(days, (d,i) => []),
getDay = (s,i) => days.indexOf(s.slice(i,i+2)), //helper function for parsing day name
getMinutes = s => s.split(':').reduce((m, n) => m * 60 + parseInt(n,10),0); //helper to store time in minutes of day
//convert to new format
for(let s of arr){
let d = getDay(s,0), end = getDay(s,3);
while(true){
times[d].push( s.slice(6).split('-').map(getMinutes));
if(d===end)break;
d = ++d % 7; //the %7 makes it possible to have ranges as Th-Mo
}
}
//now times contains an array with a day in each index, containing subarrays of the opening times in minutes
function isOpen(dt){
let mins = dt.getHours() * 60 + dt.getMinutes();
return times[dt.getDay()].some(a=>a[0] <= mins && a[1] >= mins)
}
//----------------------------------------------------------
//test functions only
console.log('Is open now: ' , isOpen(new Date()));
function test(dts){let dt = new Date(dts); console.log(days[dt.getDay()], dts,':', isOpen(dt));}
test('2016/12/29 8:00'); //th
test('2016/12/29 10:59');
test('2016/12/29 11:00');
test('2016/12/29 12:00');
test('2016/12/30 12:00'); //fr
test('2017/1/1 12:00'); //su
test('2016/12/29 21:45'); //th
test('2016/12/30 21:45'); //fr
var dates=yourdateobj;
//its easier to work with numbers then string for example (1-3 is easier then mo-wed)
var daytoindex={"Mo":1,"Tu":2,"Wed":3,"Thu":4,"Fr":5,"Sat":6,"Sun":7};
//the better structured table:
var destructdates=[];
//for each old timestring:
dates.forEach((e,i)=>{
//destructure timestring
e=e.split(" ");
var days=e[0].split("-");
var hours=e[1];
//add time to all days inbetween (1-3 (Mo-Wed) is 1,2,3 (Mo,Tue;Wed)
for(var i=daytoindex[days[0]];i<=daytoindex[days[1]];i++){
//the day is an array,add the open hours
destructdates[i]=destructdates[i]||[];
destructdates[i].push(hours);
}
});
That creates your second object ( similar):
destructdates:
[
1:["12:33-15:44","12:33-0:30"] //Mo
2:...
]
Now you can do this:
function open(day,hour,second){
//get the todays times Array
var dayhours=destructdates[daytoindex[day]];
//if now falls into one of the times:
return dayhours.some((e,i)=>{
//destructure the times:
e=e.split("-");
var start=e[0].split(":");
var starthour= +start[0];
var startminute= +start[1];
var end=e[1].split(":");
var endhour= +end[0];
var endminute= +end[1];
//check:
if(starthour<=hour && startminute<=minute && endhour>=hour &&endminute>=minute){
return true;
}
return false;
});
}
Use like this:
alert(open("Tu",12,33)?"Open":"Close");
Problems/TODO ( i dont do all of your work):
Sunday-Friday wont work, the for loop will fail.
You need to convert the todays date into the open arguments somehow.
I have an array with many dates, they are not in the date type but string like: "2016-08-12" for example. Then what I would like to do is to remove all dates that we already have passed. So therefor im trying to compare them to todays date and then remove it if its passed. Using typescript by the way.
my array, named datoArray, looks like this:
["2016-08-02", "2016-08-11", "2016-08-22", "2016-09-10"]
just with a lot more of the same...
then here's what I try to do:
for(var i = 0; i < this.datoArray.length; i++){
this.skoleAar = parseInt(this.datoArray[i].slice(0,4))
this.skoleMaaned = parseInt(this.datoArray[i].slice(5,8))
this.skoleDag = parseInt(this.datoArray[i].slice(8,10))
if(this.skoleAar < dagensAar){
this.datoArray.splice(i, 1);
}
if(this.skoleAar == dagensAar && this.skoleMaaned < dagensMaaned){
this.datoArray.splice(i, 1);
}
if(this.skoleAar == dagensAar && this.skoleMaaned == dagensMaaned && this.skoleDag < dagensDag){
this.datoArray.splice(i, 1);
}
}
the "dagensAar", "dagensMaaned" and "dagensDag" variables im getting from another function that works. If i "console.log" the variables it prints out int values like 2016 for the year and 8 for the month if i take from the start of the array, and for the "dagensAar", "dagensMaaned" and "dagensDag" it prints 2016 11 20, which is todays year, month and day. all is in Int type, so what im not getting here is why my "if" doesnt work? It seems like there is something wrong with the way i compare the, but i thought this was the way to compare int values?
If the dates are in ISO-8601 format then you can simply filter using Date.parse().
var dates = ["2016-08-02", "2016-08-11", "2016-08-22", "2016-09-10", "2016-12-15"];
function removePastDates(data) {
var today = new Date();
console.log('Initial state: ' + data);
var modified = dates.filter(function(dateString) {
return Date.parse(dateString) >= today;
});
console.log('Final state: ' + modified);
return modified;
}
var newDates = removePastDates(dates);
Your dates seem to be RFC compliant, meaning they can be directly fed into a new Date object. Simply compare to today and filter by that:
var today = new Date()
var futureDates = this.datoArray.filter(d => new Date(d) >= today)
(pre-ECMA6:)
var today = new Date()
var futureDates = this.datoArray.filter(function (d) {
return new Date(d) >= today;
})
I think the problem is not related to the dates.
I think the problem is that you are removing items from the array while looping the same exact array.
You should maybe try looping from the end of the array to the beginning or just save the indexes that you need to remove and later do the actual removing.
Keep in mind that when you remove an item you change the index of every item in the remaining of the array - maybe you should start removing from the greatest index so it will not confuse you.
Would this code work if both the timestamps are in mysql 2013-03-21 18:16:50 format?
$.get('current.php', { lastTime: time, current: true, Cla: Class }, function(html) {
var Date1 = Date.parse($(html).find("#timestamp").val());
var Date2 = Date.parse($("#timestamp").val());
if (Date.parse(Date1) < Date.parse(Date2)) {$(html).find("#timestamp").remove();}else {$("#timestamp").remove();}
}
I think you want something like this:
$.get('current.php', { lastTime: time, current: true, Cla: Class }, function(html) {
var $dateInput1 = $(html).find('#timestamp'),
$dateInput2 = $("#timestamp");
if ($dateInput1.val() < $dateInput1.val()) {
$dateInput1.remove();
} else {
$dateInput2.remove();
}
// ... add $html somewhere to the DOM ...
});
Since your dates are already in a format that is comparable lexically, it doesn't require using Date.parse() at all. Also, you'd only be creating one jQuery object per element, so it's more efficient in that way, too.
Date.parse return time in milliseconds which you can use it for comparison.
if (Date1 < Date2) {
$(html).find("#timestamp").remove();
}else {
$("#timestamp").remove();
}
You are calling Date.parse twice on the values, and Date.parse does not accept the milliseconds number returned by the previous invocation as a valid date.
if (Date1 < Date2)
is enough to compare them. Notice that if either of them could not be parsed, you will be comparing to NaN.