Extracting valid date from a string - javascript

I need to extract a valid date from a list of random strings. The date can be present in any date format("01/25/16", "25/01/2016", "20-01-2016", "3-Nov-2016" etc) with different kind of separators.
I tried the using Date.parse() and new Date() but these method also return a valid value for any number passed which ideally is not a date.
For Ex: Date.parse("1") = 978336000000
My current solution is to check each string with the following regex
if(!string.match(/^\d+$|[a-zA-Z]+\s*[a-zA-Z0-9]*/) && (string.length > 7)) {
const date = Date.parse(string)
return (!isNaN(date))
}
This regex works to identify date strings like "01/25/16", "25/01/2016", "20-01-2016"
This regex matches most of the regular text like "100", "hello", "123hello", "1h ello12" and lets in values like "123-123", "01/25/16" and Date.parse() identifies pretty good.
But this misses the date string like "23-Nov-2016" so I added one more regex along with previous one
if(((!string.match(/^\d+$|[a-zA-Z]+\s*[a-zA-Z0-9]*/) && (string.length > 7)) || ((string.match(/^\d+$|[a-zA-Z]+\s*[a-zA-Z0-9]*/) && string.toLowerCase.match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/))) {
const date = Date.parse(string)
return (!isNaN(date))
}
I definitely believe that there exists a much simpler solution than using this large sets of regex in javascript.
EDIT : I don't control the date input rules to specifically validate certain formats.

Unfortunately, I don't think there is a better solution than using a set of regular expressions.
The problem is that there are at least a million different ways to write the same date. It seems like no matter what date formats you have planned for, your users will always come up with something that doesn't fit. So I approached this in the following way for a project I'm working on:
Make a list of acceptable date formats.
Tell the users not to use different formats and enforce it via client-side validation.
In my case, I'm living in the US, and dates are usually written like 'M/D/YY'. To allow for a reasonable range of variation, I wrote my code to accept M/D/YY, M/D/YYYY, and M/D (where the current year is substituted if the year is omitted). These formats are recognized using regular expressions then parsed using the Moment.js library.
You may want to expand the list of permitted formats if your users habitually use them - that's fine. But the important thing is to realize that you can't plan for all possible formats - there are just too many variations.
If you can meet your users' expectations 90% of the time (with the most common formats) and train your users that these are the accepted formats, you'll have happy users and date parsing code that's not 10,000 lines long.

Related

nativeElement.value is NaN if there is a comma

I'm trying to set the number of decimals at 2 in an input. When I type a comma in it, the value becomes NaN so I would like get my number instead of this.
TS
#ViewChild('number') input;
limitNbOfDecimals() {
var regex =
this.form.number.search(/^(\d+(?:[\.\,]\d{0,2})?)$/) == 0
? true
: false;
if (regex == false) {
// Convert the value to a number
var nb: number = +this.input.nativeElement.value;
//set decimals at 2
this.input.nativeElement.value = nb.toFixed(2);
}
}
HTML
<input class="form-control" type="text" [(ngModel)]="form.number"
#number
name="number"
(input)="limitNbOfDecimals()"
/>
EDIT
I manage to add a comma in the number but if I try to add more than 2 decimals after it removes the numbers after the comma like 1,11 -> 1
This isn't a full answer, in the sense of having a total solution, but hopefully helps you get to one (and it's too long for a comment).
The spec at https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number) states:
This specification does not define what user interface user agents
are to use; user agent vendors are encouraged to consider what would
best serve their users' needs. ..... a user agent designed for the
French market might display the value with apostrophes between
thousands and commas before the decimals, and allow the user to enter
a value in that manner, internally converting it to the submission
format described above.
It would seem that the only sure way - if you don't have control over what browsers your users have - of ensuring they can type numbers in the format they are used to in their local setting is to take input as type text and on each keystroke check that they have typed something valid (as you have defined it) and when they submit it convert to a decimal number.
Searching provides code for doing this, depending on exactly what your requirement is for the number formats though you may be better off coding it from scratch.
To add more than 2 decimal values, you need to tell like .toFixed(4) etc..

Writing a regex filter to parse a range of dates stored in reverse-timestamp order

I have to parse a range of row keys by date in Google Cloud BigTable using a regex filter, where each date in the rowkey is stored as a reverse-timestamp according to the Unix Epoch (00:00:00 on January 1st, 1970).
For example, given Date d = "2018-03-09T10:48:00.000Z", this date in MS since the Unix Epoch is d.valueOf() = 1520592480000. In JavaScript, the maximum allowable integer is Number.MAX_SAFE_INTEGER = 9007199254740991, and so we calculate d's reverse date r by taking the difference: var r = Number.MAX_SAFE_INTEGER - d where r = 9005678662260991.
Given two reverse dates r1 and r2, how can I write a regular expression in RE2 to get all date strings within this range? I'm trying to find all dates within a range using reverse-timestamp rowkeys in BigTable using NodeJS (limited documentation), so if there are any easier solutions than this that you're aware of I'll also accept those!
Thanks
The workaround for the Javascript maximum integer is making the lookup less natural and efficient.
I would suggest expressing the rows in a way that are easily understandable by both Javascript and Bigtable, while maintaining an natural order and properties for filtering.
If you need reversal to prevent hotspotting, you can try reversing only part of the timestamp (10's of seconds?) or adding a salt into the key (you can check the "Salting" section here https://cloud.google.com/bigtable/docs/schema-design-time-series).
There's not a good way to handle integer ranges using pure regular expressions, only character ranges:
https://stackoverflow.com/a/7861720/643848
https://github.com/google/re2/wiki/Syntax
Moreover, Bigtable will be much more efficient if you can express your queries to take advantage of lexicographical row keys for filtering/scanning, i.e. if you can set up your schema such that you can express the range you want using a simpler query like a RowRange (https://cloud.google.com/bigtable/docs/reference/data/rpc/google.bigtable.v2#rowrange).
You may also want to consider just issuing a broader query and then doing more processing and filtering client-side.

Why does qUnit's assert.equal think these 2 strings are different in IE?

I am working with some qUnit tests written by another developer and am having some trouble understanding why a particular test in IE is failing.
There is a function that can convert a number of differently formatted string dates into a UTC date and it seems to function correctly. However, I am having some issues with testing it in IE.
In order to test it I am taking the return of the function (which is a number rather than a standard formatted date), creating a new date from it and then using JavaScript's toLocaleString() function to get a string I can compare to another string I created. An example of the test is below; minus the call to the function, I have replaced the call to the function with the output I get from it.
var expectedResult = "11/11/2000 12:56:00";
var actualResult = new Date(973947360000).toLocaleString():
assert.equal(expectedResult, actualResult);
This fails but I cannot see why, I am not using a deepEqual() and the types are the same anyway (I have debugged and checked). I think it may be down to IE's encoding but am not sure of 1, how to ascertain this is the case and 2, get around it/effectively test it. It is worth noting that this test passes fine in FF and Chrome, though Chrome appends "PM" to the end of the date.
Any help would be greatly appreciated.
Below is a snapshot of the output from IE.
qUnit output difference
Seeing as no one else is coming forward with an explanation I am going to simply submit my workaround as an answer.
It would appear that .toLocaleString() in Internet Explorer is encoding the space between the 2 dates differently to how a string initialised by JavaScript does. Using the following code replaces the .toLocaleString() space and allows an effective evaluation of the equality of the 2 values.
.replace(/[^ -~]/g, '');
As I only need to know that the displayed date has the same value as the input date this is acceptable.

How to "unformat" a numerical string? JavaScript

So I know how to format a string or integer like 2000 to 2K, but how do I reverse it?
I want to do something like:
var string = "$2K".replace("/* K with 000 and remove $ symbol in front of 2 */");
How do I start? I am not very good regular expressions, but I have been taking some more time out to learn them. If you can help, I certainly appreciate it. Is it possible to do the same thing for M for millions (adding 000000 at the end) or B for billions (adding 000000000 at the end)?
var string = "$2K".replace(/\$(\d+)K/, "$1000");
will give output as
2000
I'm going to take a different approach to this, as the best way to do this is to change your app to not lose the original numeric information. I recognize that this isn't always possible (for example, if you're scraping formatted values...), but it could be useful way to think about it for other users with similar question.
Instead of just storing the numeric values or the display values (and then trying to convert back to the numeric values later on), try to update your app to store both in the same object:
var value = {numeric: 2000, display: '2K'}
console.log(value.numeric); // 2000
console.log(value.display); // 2K
The example here is a bit simplified, but if you pass around your values like this, you don't need to convert back in the first place. It also allows you to have your formatted values change based on locale, currency, or rounding, and you don't lose the precision of your original values.

Convert string, format 10:30AM, to which type for comparison in javascript

I have strings of the format "10:30AM", "3:00PM" etc that I want to be able to use basic operations on, for example > or < and how many hours until say 10:30 based on current time. I would like to make the conversion on the client side (javascript/jQuery) prior to database insertion.
Should I convert these to javascript date-time objects? or would a regex to change it to say a number in 24hour time format be more suitable to perform these operations on? Or am I making this more difficult than it should be?
Thanks in advance.
You are going to want to convert to a date time -- there are a lot of edge cases when comparing numbers as strings -- much easier to just bite the bullet and make a date out of it. There are a million example libraries to use or take inspiration from.
I personally think if it is basic operations i would convert it to 24h and then compare. If it was anything more complex then I would convert it to a date-time object.
I would suggest you to use a library for that. I prefer Moment.js which allows you to perform compare or know how many hours from the current time.
It's a bit late but when you're sure you have such a specific string that needs converting in a specific way you could write your own implementation to convert the time, it'll be lighter and quicker to sort or compare:
var Time=function(time){
// TODO: you an check here what format the time variable
// is and if it's possible to convert it to time or milTime
this.time=time;
this.milTime=this.toMilTime();
this.val=this.setVal();
};
Time.prototype.toMilTime=function(){
return this.time.replace(/([0-9]{1,2}).([0-9]{1,2})([\w]{2})/,function(){
//TODO: put this in a try catch and check if hours and numbers
// are correct numbers. throw a new Error with the correct description
var hours=(arguments[1].length===1)?"0"+arguments[1]:
arguments[1],
minutes=(arguments[2].length===1)?"0"+arguments[2]:
arguments[2],
pam=arguments[3].toUpperCase();
if(pam==="PM"){
hours=parseInt(hours,10)+12;
}
return hours + ":" + minutes;
});
};
Time.prototype.setVal=function(){
return parseInt(this.milTime.replace(/:/,""),10);
}
// used for sorting
Time.prototype.valueOf=function(){
return this.val;
};
// needed for <> comparison
Time.prototype.toString=function(){
return this.milTime;
};
var t = new Time("10:30AM"),
t1=new Time("1:00PM"),
t2=new Time("10:30AM");
console.log(t.milTime,t1.milTime);
console.log(t>t1);// this will use toString()
console.log(t1>t);// this will use toString()
console.log(t===t2);//false
console.log(t==t2);//false
console.log(t.milTime===t2.milTime);//true
var arr=[t,t1,t2].sort(function(a,b){
return a.valueOf()-b.valueOf();// force it to use valueOf
});
console.log(arr);

Categories

Resources