I've got a website where users can potentially type in their own dates before they're sent to the server. So I obviously need to parse what they give me and get it into a standard format before actually using it. So I used PHP's strtotime() function, which is pretty forgiving about what it will accept as input.
But I'm also using date.js on the site and its parse() function is pretty good, too. Should I use that on the user input before sending it to the server? Which one is better?
I'll keep strtotime() on the back end for safety, but if date.js is better I'll add that to the client.
(To clarify, I'm expecting mostly American date formats. But should that change, anything that eases that transition is preferred.)
As long as you feel strtotime() is meeting your needs, there isn't a great reason to change it on the client side. However, strtotime() makes a couple of assumptions which you need to stay on top of:
From the strtotime() documentation:
Dates in the m/d/y or d-m-y formats are disambiguated by looking at the separator between the various components: if the separator is a slash (/), then the American m/d/y is assumed; whereas if the separator is a dash (-) or a dot (.), then the European d-m-y format is assumed.
If you are allowing your client to send the date in any format they choose, the above could be a source of confusion. I just tested the dates 5/1/12 and 5-1-12 in date.js, and both were parsed as May 1st, 2012. PHP would interpret the two as May 1st 2012 and Jan 12th 2005(!!) respectively.
echo date("Y-M-n", strtotime("5/1/12"));
// 2012-May-5
echo date("Y-M-n", strtotime("5-1-12"));
// 2005-Jan-1 (whoops!)
However, pre-formatting the date has the obvious benefit of some insurance that the entered date is valid. Keeping strtotime() on the backend also ensures that you don't need a JavaScript-enabled client to send requests. Your PHP could still be called as a web service, etc, without the client needing to be a web browser.
Both.
You absolutely do need to validate the date in PHP, because one or more of your users may have disabled Javascript.
Javascript validation is nice because you can have a date entered in an input control, validate it in the onblur handler, and write the validated date back to the control. So after the user types the date of birth 2/5/01 and leaves the control, the date changes to 2 May 2001 and not only does the user knows that the date is interpreted correctly, but strtotime() also gets an unambiguous value.
You can also use much better validation feedback, from a UX point of view. Javascript can do the following (which is probably well known but it took a bit of searching for me to find it). Put an element in the page, like
<div id="DobReply"></div>;
Then the function which validates the Date of Birth can do
document.getElementById("DobReply").innerHTML = "Current age is "+age;
where age is a variable calculated from the entered date of birth, and today's date. As soon as the user leaves the control, the calculated age pops up, and if the user entered the current year (it happens) then she might spot the error immediately.
So I wouldn't get rid of datejs validation either. But all of this is lost if Javascript is disabled, you can get invalid dates, dates in weird formats, or anything. So if you can only have one, keep the strtotime().
Related
When asking for a date in a HTML form, in the past I was using different solutions:
Using 3 different fields for day, month, year
Using a text field, but adding to the label something like "Start date (example: 31-12-2013)"
Using a reverse date, e.g. 2013-12-31
Now I would like to use HTML5, and just have a input type='date'. This should also help users with mobiles.
For some browsers this may offer a date picker, for some just a text fields. But we all know there are different date formats, and sometimes the user's machine is not configured as he/she would expect. For instance, if I enter a date of 3 January 2014 as 03-01-2014 this can be interpreted as 1 March 2014 in other cultures.
What is the best I can do to help users to prevent mistakes? Is there a way to write an example, without knowing in advance what are his/her browser settings? And how do I know that PHP will interpret the date correctly?
Use ISO 8601 (YYYY-MM-DD) + HTML5's pattern and placeholder attributes
An alternative to falling back to javascript datepickers, might be using a validation (regex) pattern as fallback for browsers that don't support the date type:
<input type="date" placeholder="YYYY-MM-DD" name="date" value="" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" required="required">
While support for the date input type is still very limited, the pattern attribute's support is already more widespread. Of course you'll still need to check all submissions server side, but this should give a nice non-javascript solution for client side validation. You can use the placeholder attribute to show the right format, but since it's not supported by older browsers (IE9 and lower) it might be better to display the example outside of the form field.
html5pattern.com has a nice collection of ready-to-use input patterns, including date patterns but other ones as well. The benefit is that you can use the same pattern for your server side validation as well.
Y-m-d is likely the best method, If you use jQuery UI's date picker you can specify the format.
http://jqueryui.com/datepicker/
you can also show an alternative format.
According to dev.w3.org it will supply you with "A valid full-date as defined in [RFC 3339]".
However there is no way to know what the user meant if they enter 1/2/2014, although 2014-02-01 is unambiguous). As others have mentioned, go with a date picker if possible.
The HTML5 date input specification specifies a full-date format equal to: yyyy-mm-dd. You will receive date in this format always.
Follow this link for more information
Goal: Convert any local date to the according ISO date
My Approach: http://codepad.viper-7.com/XEmnst
strftime("%Y-%m-%d",strtotime($date))";
Upside: Converts a lot of formats really well
Downside / Problem: Converts strings and numbers that are obviously not a date. E.g.
strftime("%Y-%m-%d",strtotime("A")) => 2012-10-29
strftime("%Y-%m-%d",strtotime("1")) => 1970-01-01
Questions:
Is there a better way to identify and convert dates to ISO dates?
Do you know of any library / regex that is capable of do so in php?
PHP's strtotime() function already does a best-effort attempt at taking an arbitrary string and working out what date format it is.
I dislike this function for a number of reasons, but it does do a reasonable job of working things out, given a string of unknown date format as input.
However, even strtotime()'s best efforts can never be enough, because arbitrary date formats are ambiguous.
There is no way to tell whether 05-06-07 is meant to be the 5th of June 2007 or the 6th of May 2007. Or even the 7th June 2005 (yes, some people do write dates like that).
Simple plain truth: It's impossible.
If you want your dates to be reliable in any meaningfuly way, you must abandon the idea that you'll be able to accept arbitrary input formats.
[EDIT]
You say in the comments that the input is coming from a variety of Excel and CSV files.
The only hope you have is if each of those files is consistent in itself. If you know that a file from a given source will have a given input format, you can write a custom wrapper for each file type that you import, and process it for that format. This is a solution I've used myself in the past, and it does work as long as you can predict the format for the file you're processing.
However, if individual files contain unpredictable or ambiguous dates, then you are out of luck: You have an impossible task. The only way you'll avoid having bad data is to kick back to the suppliers of the files and ask them to fix their data.
I think the problems will really arise when faced with dates such as 5-6-2012 when it is unclear whether you are dealing with 5th June, or 6th May and you could be taking input from European countries where DD MM YYYY is the norm.
If you are analyzing just one input field, then you might have a chance of detecting the delimeters and splitting the string up looking for what might look like a real date.
In this case the PHP function checkdate might come in handy as a last ditch double check.
Be aware also that Mysql (if this is where the data is heading) is also quite lenient about what it will put into a DATE field, the delimeters, the absence of leading zeros etc. But still, you have to get the Y M D order correct for it to have a chance.
I suppose the ultimate answer is to disallow free-text input for dates, but give them pickers - but of course you may not be in a position to influence the incoming date ...
Is there a way to specify your own date pattern besides the included ones (small, medium, full). The main point here is that it should work with i18n. I've tried a couple of things but I couldn't get it to work...
Yii::app()->dateFormatter->format("l d/m/Y",$slide->date_start);
I know about strftime but the problem here is that different hosting providers use different locale string... and you have to customize it...
I'm looking for an elegant way of doing this.
I'd like to display the date in l d/m/Y form...
Update:
Never mind... I've just found out that dateFormatter doesn't use standard php date format...
I think you should measure time solely in Unix Time because Timezones & date formats are a presentation-layer problem. Unix time is always UTC & It's a single number, so easier to pass around in code.
As far the problem of "hosting providers use different locale string", just ask the user his timezone & display according to that. far less error-prone than trying to guess.
For date formatting, have a look at YII's format()
Hope it answers your question
Here's a related yii forum discussion
The yii forum solution worked for me to avoid raw SQL NOW() statements but still produce database-friendly date strings with PHP date() and time() functions which otherwise return integers.
In protected/config/main.php:
...
'params'=>array(
'mysqlDateTimeFormat' => 'Y-m-d H:i:s', # ':u' adds microsecond precision,
...
Then, wherever you want to put a date-time string into a model field use
$myModel->myDate = date(Yii::app()->params['mysqlDateTimeFormat']);
Obviously you can enter the date/time format into the date (or time) functions directly if you prefer.
In my project I am importing users from a csv file. In that some columns have date values
Eg:- date of birth, project start date, project dead line date etc. I know the date column headers but the user can enter date format in different ways. How can I validate the value get from csv is a valid date or not?
While not always accurate, you can try strtotime(). It's not a perfect solution but it's worth trying. Just read the notes because it can have varying behavior depending on what your date formats look like.
The php function strtotime can take many different date formats and returns a timestamp. If your can make thoughtful assertions on the valid timestamp values, validation can be done with those assertions.
But it could be prone to side effects,like day and month mismatch, so it would be a poor validation. I suggest that you enforce a common date pattern on the input file.
I use Zend_Date, as the constructor will validate the date for me, but of course if you don't expect a precise format you'll get in the kind problems that #gordon points out with 11/10/09 type of date. This will generate you a valid date, but for a default locale/format which can be of course wrong.
I've been using PHP's strtotime() method to accept a date field on a form. I love how powerful it is, in how it will accept "Tomorrow", "Next Thursday", or (supposedly) any date representation and convert it to the Unix timestamp.
It's been working great -- until yesterday. Someone entered "2-4-10" and instead of logging Feb 4th, 2010, it logged April 10, 2002! So it expected Y-M-D instead of M-D-Y.
I thought maybe the problem was just using a 2-digit year, so we tried again with "2-4-2010". That logged April 2nd, 2010! At that point I just don't understand what strtotime() is doing. PHP.net says it expects a US English date format. Why then would it assume D-M-Y?
Is there a way around this? Or do I have to stop using strtotime()?
Note: I just now did a test. When you use slashes instead of hyphen/dashes, it works fine, even with 2/4/10. Why on earth does that matter? And if that's all it is, should I just run str_replace("-", "/", $input) on the form input before passing it to strtotime()?
The - indicates an ISO Date:
03-02-01 => 1. february 2003 (ISO)
01.02.03 => 1. february 2003 (European)
02/01/03 => 1. february 2003 (US)
The behavior of strtotime() is based largely on the GNU date input formats spec. But as powerful as it is, it shouldn't be expected to read minds. Allowing free-form user date input is asking for perpetual trouble.
I had this problem and solved it by doing exactly what you suggested - do a str_replace on the user-entered date to replace the dashes with slashes. This prevents strtotime from using an ISO date and solves the problem.
strtotime is by its very nature fuzzy, so you can't assume that it will always do what you want. If you enter 2010-04-02 then you would expect that to return 2nd April 2010, which is what strottime is trying to do. Running an str_replace from hyphens to slashes might mean that people entering in that format get the wrong date.
If you're running PHP 5.3 or above, consider date_parse_from_format() or for PHP 5.1 and above on Unix consider strptime(). Both functions take a format, so remove potential ambiguity (if you tell users what format you are expecting - if you're running an international site and have a text box labelled date which the user enters 2/4/2010 into then there is no way to know what their intended date is).