validate and sort date in jquery - javascript

I am using the table sorter plugin to sort my tables.
I want to be able to catch date column in format:
dd/MM/yyyy HH:mm
and then sort them correctly (for this I have to switch days with years).
Here is what I've so far:
ts.addParser({
id: "hebreLongDate",
is: function (s) {
return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4} d{1,2}:d{1,2}/.test(s);
}, format: function (s, table) {
var c = table.config;
s = s.replace(/\-/g, "/");
// reformat the string in ISO format
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
return $.tablesorter.formatFloat(new Date(s).getTime());
}, type: "numeric"
});
It does not work.
I would appreciate any help, especially if it comes with an explantation on the meaning of the correct regex.
Thanks,
Omer

The parser doesn't really validate the date. The is function only detects if the format matches the pattern for the format function which is why it is just easier to make it return false and manually set the parser for a column using the headers option:
headers: {
1: { sorter: "hebreLongDate" }
},
The is function above is requiring a HH:mm within the pattern, so if the first table cell in the column doesn't match, it ignores that parser. So either way it would be better to manually set the parser.
Anyway, here is how I would write the parser you are describing (demo):
$.tablesorter.addParser({
id: "hebreLongDate",
is: function(s) {
return false;
},
format: function(s, table, cell, cellIndex) {
s = s
// replace separators
.replace(/\s+/g," ").replace(/[\-.,]/g, "/")
// reformat dd/mm/yyyy to yyyy/mm/dd
.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1");
return s ? $.tablesorter.formatFloat( (new Date(s).getTime() || ''), table) : s;
},
type: "numeric"
});
As for explaining the regex, there isn't that much of a difference between the code above and what you have in your question. The biggest difference is that the above code ensures that only one space exists between the date and time and that the date can be separated by a slash, dash, period, comma or space (i.e. 1-1-2000, 1 1 2000 etc).
Update: if you want to have this parser be autodetected, then use the following is regex (updated demo). But it is important to note, that this regex cannot distinguish mmddyyyy from ddmmyyyy so it will always detect ddmmyyyy. To override this, set the header sorter option to "shortDate":
is: function(s) {
// testing for ##-##-####, so it's not perfect; time is optional
return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})/).test((s || '').replace(/\s+/g," ").replace(/[\-.,]/g, "/"));
},

Related

jQuery Tablesorter is not working with date

My code:
<th class="sorter-shortDate">Date</th>
$('table').tablesorter({dateFormat: "yyyymmdd"});
Sorting is working on numbers, but it is not working on dates,
My date format is y-m-d H:i,
I also tried to add custom parser:
$.tablesorter.addParser({
id: "customDate",
is: function(s) {
return false;
//use the above line if you don't want table sorter to auto detected this parser
//else use the below line.
//attention: doesn't check for invalid stuff
//2009-77-77 77:77:77.0 would also be matched
//if that doesn't suit you alter the regex to be more restrictive
//return /\d{1,4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}/.test(s);
},
format: function(s) {
s = s.replace(/\-/g," ");
s = s.replace(/:/g," ");
s = s.replace(/\./g," ");
s = s.split(" ");
return $.tablesorter.formatFloat(new Date(s[0], s[1]-1, s[2], s[3]).getTime()+parseInt(s[6]));
},
type: "numeric"
});
Can anyone help me please?
There's a minor bug in the shortDate parser; it replaces all . with a / while converting the date into something the built-in JS date parser can understand (e.g. 2009-12-31 08:09:10.1 becomes 12/31/2009 08:09:10/1 - that last decimal place getting changed into a slash is breaking everything.
You can get around this by modifying the short date parser's regular expression - demo
$.tablesorter.regex.shortDateReplace = /-/g;
$(function() {
$('table').tablesorter({
theme: 'blue',
dateFormat: 'yyyymmdd'
});
});

Show only one month name in FullCalendar when WeekView wraps two different months

I am developing using fullcalendar.js, and in the week view, when a week is from 2 different months (for example 27 july - 2 august) the fullcalendar week view shows the two months text. I am searching everywhere but there is no solution for this. Maybe stackoverflow users can help me.
That's what I have:
And that's what I need:
I see the date format but is MMMM YYYY, and it returns two months or one automatically and it seems impossible to change this.
In Calendar.defaults (aprox. line 8300 in non-minimized code) object I can notice this:
titleRangeSeparator: ' \u2014 ', // emphasized dash
monthYearFormat: 'MMMM YYYY', // required for en. other languages rely on datepicker computable option
As I explained, monthYearFormat seems to only be one month, but in a specific moment it merges with titleRangeSeparator to become two months.
Do you know how this is solvable?
Thank you.
EDIT
I found the functions that make this complex string, but is used by month and day views that I don't want to change (I need only to week view). The code is the next. How can I modify this code to solve it?
// Date Range Formatting
// -------------------------------------------------------------------------------------------------
// TODO: make it work with timezone offset
// Using a formatting string meant for a single date, generate a range string, like
// "Sep 2 - 9 2013", that intelligently inserts a separator where the dates differ.
// If the dates are the same as far as the format string is concerned, just return a single
// rendering of one date, without any separator.
function formatRange(date1, date2, formatStr, separator, isRTL) {
var localeData;
date1 = fc.moment.parseZone(date1);
date2 = fc.moment.parseZone(date2);
localeData = (date1.localeData || date1.lang).call(date1); // works with moment-pre-2.8
// Expand localized format strings, like "LL" -> "MMMM D YYYY"
formatStr = localeData.longDateFormat(formatStr) || formatStr;
// BTW, this is not important for `formatDate` because it is impossible to put custom tokens
// or non-zero areas in Moment's localized format strings.
separator = separator || ' - ';
return formatRangeWithChunks(
date1,
date2,
getFormatStringChunks(formatStr),
separator,
isRTL
);
}
fc.formatRange = formatRange; // expose
function formatRangeWithChunks(date1, date2, chunks, separator, isRTL) {
var chunkStr; // the rendering of the chunk
var leftI;
var leftStr = '';
var rightI;
var rightStr = '';
var middleI;
var middleStr1 = '';
var middleStr2 = '';
var middleStr = '';
// Start at the leftmost side of the formatting string and continue until you hit a token
// that is not the same between dates.
for (leftI=0; leftI<chunks.length; leftI++) {
chunkStr = formatSimilarChunk(date1, date2, chunks[leftI]);
if (chunkStr === false) {
break;
}
leftStr += chunkStr;
}
// Similarly, start at the rightmost side of the formatting string and move left
for (rightI=chunks.length-1; rightI>leftI; rightI--) {
chunkStr = formatSimilarChunk(date1, date2, chunks[rightI]);
if (chunkStr === false) {
break;
}
rightStr = chunkStr + rightStr;
}
// The area in the middle is different for both of the dates.
// Collect them distinctly so we can jam them together later.
for (middleI=leftI; middleI<=rightI; middleI++) {
middleStr1 += formatDateWithChunk(date1, chunks[middleI]);
middleStr2 += formatDateWithChunk(date2, chunks[middleI]);
}
if (middleStr1 || middleStr2) {
if (isRTL) {
middleStr = middleStr2 + separator + middleStr1;
}
else {
middleStr = middleStr1 + separator + middleStr2;
}
}
return leftStr + middleStr + rightStr;
}
This isn't directly supported unfortunately, but there is still a better way than modifying the FC source (that get's messy with patches and stuff).
There are several render hooks available that we can use to fix the formatting after the fact. viewRender doesn't work because it's called before the title changes. So we can use eventAfterAllRender instead.
eventAfterAllRender:function(){
if(view.name!=="agendaWeek")
return;
var $title = $("#calendar").find(".fc-toolbar h2"); //Make sure this is the right selector
var text = $title.text();
text = text.match(/.*? /)+text.match(/[0-9]+/);
$title.text(text); //replace text
}
JSFiddle - titleFormat hack
Not the most elegant thing in the world, but it should work better than modifying the source. Let me know if there are any issues.
Edit:
Also, if you're having problems with it flashing the wrong dateformat before the correct one, use css the make the title invisible. Then add a class to the element in eventAfterAllRender that makes it visible again.

jquery tablesorter: addParser does not sort properly

I have created a table and sorted it with tablesorter. One of the columns is a mix of letters and numbers (chr1, chr2, ..., chr10, ... , chrM). I want this column to be sorted as if it were only numbers (from 1 to 22 and then X, Y and M in this order).
I have created my own parser and it works but just for some rows. Then, I find another block of rows which are correctly sorted followed by some other blocks. I do not know why this blocks are formed.
The code is here. Maybe with a smaller table it would work properly, because of that I have shown a big one.
Thanks in advance!
Try this parser (demo)
$.tablesorter.addParser({
// set a unique id
id: 'chrom',
is: function (s) {
// return false so this parser is not auto detected
return false;
},
format: function (s) {
// format your data for normalization
return s.toLowerCase()
.replace('chr', '')
.replace('x', '97')
.replace('y', '98')
.replace('m', '99');
},
// set type, either numeric or text
type: 'numeric'
});

Looping over array of objects

I'm working on an asp.net app that is utilizing a lot of jQuery UI controls particularly the datepicker.
In my web service I am making a call to the database and retrieving a list of objects and then passing them back to my javascript where I parse them out into an array containing 1 or more objects that look like this:
I need to include some kind of logic in which I can loop through this array of objects and check to see if a javascript Date falls in between the EndDate and StartDate properties of the object so that I can apply a css style for the DatePicker. First question, is there a way to convert the EndDate/StartDate property from this format to a valid javascript Date?
And if so how can I iterate over the array and apply the logic to see if the date falls inside the range?
Any help is greatly appreciated!
Edit: I noticed the image here is kind of hard to see you can more clearly read the properties here:
image link
As requested here is some example code:
function createDateRangesForCalendar() {
$.ajax({
type: "POST",
url: "../Services/BookingService.asmx/GetCalendarDateRanges",
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false,
success: function (response) {
dateRanges = $.parseJSON(response.d);
},
error: function (xhr, textStatus, thrownError) {
alert(textStatus);
alert(thrownError);
}
});
}
function markInvalidDates(date) {
var isHoliday = false;
dmy = date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear();
isHoliday = checkIsHoliday(date);
if ($.inArray(dmy, invalidDays) == -1) {
for (var i = 0; i < dateRanges.length; i++) {
// if date falls in between start and end date of object[i] return something like: return [true, "holiday", "Holiday Rates Apply - Minimum 14 days"];
// else loop through to the next object and try there
}
if (isHoliday == true) {
return [true, "holiday", "Holiday Rates Apply - Minimum 14 days"];
} else {
return [true, ""];
}
} else {
return [false, "unavailable", "Unavailable"];
}
}
First question, is there a way to convert the EndDate/StartDate property from this format to a valid javascript Date?
The format seems to be this: /Date(MILLISECONDS)/. A valid JS date object can be obtained like this: new Date(s.match(/Date\((\d+)/)[1]).
And if so how can I iterate over the array and apply the logic to see if the date falls inside the range?
var re = /Date\((\d+)/;
for(var i in arr) {
var start = new Date(arr[i].startDate.match(re)[1]),
end = new Date(arr[i].endDate.match(re)[1]);
if(myDate < end && myDate > start)
// do something.
}
The above seems to answer your question, the way I understand it.
StartDate and EndDate seem like valid JSON to me, except for the slashes at end and beginning. Otherwise, a simple eval of the value should produce a JS Date Object on which you can operate.
For your second point, what keeps you from classic looping over the array ? Some code would be much more useful to say more.
Just return your start and end dates as numerics, without the \Date()\ wrappers.
In your loop, create a JavaScript date from your target date, i.e. new Date(1334548800000) then use simple comparisons between your target date and those start and end dates.
While you can loop with $.each(yourArray, function(id,item){ date comparison logic here }); I recommend you look into the Underscore library for a decent set of utilities to manipulate JS objects.

jQuery table sorting and currency issue

This one may be a bit specialized, but here goes anyway:
Page for reference: http://greg-j.com/icvm/anticartel/search-results.html
Plugin for reference: http://tablesorter.com/
If you look at the last two columns for "Total Fines", you'll see the currency output includes $x.xxx billions and $x.xxx millions. The built-in currency parser does not account for this format. The plugin, fortunately, allows for you to write your own parser. However, I'm not getting anywhere.
See if this works, I haven't tested it:
$.tablesorter.addParser({
id: 'monetary',
'is': function(s) {
return false;
},
format: function(s) {
var i = s.split('$').join('');
var suffixes = [
{name:'thousand', mult: 1000},
{name:'million', mult: 1000000},
{name:'billion', mult: 1000000000},
{name:'trillion', mult: 1000000000000}
];
for (var j in suffixes) {
if (i.indexOf(' '+suffixes[j].name) != -1) {
i = i.split(' '+suffixes[j].name).join('');
val = parseFloat(i) * suffixes[j].mult;
}
}
return val;
},
type: 'numeric'
});
$("#cartels").tablesorter({
widgets: ['zebra'],
sortList: [[0,0]],
headers: {
4: {
sorter:'monetary'
},
5: {
sorter:'monetary'
}
}
});
Can you post the code you've tried?
It looks like what they are doing in the example you posted is assigning each word with a number representation, and then sorting by that:
return s.toLowerCase().replace(/good/,2).replace(/medium/,1).replace(/bad/,0);
So in your case one way might be to replace million with the correct number of zeros and the same for billion. So essentially $1 million gets evaluated to $1,000,000 as far as the parser is concerned.
return s.toLowerCase().replace(/million/,000000).replace(/billion/,000000000);
So s is evaluating to $1000000 once the replace function is evaluated.
Just a thought. Not sure if it works, but it might get you on the right track.

Categories