How do I validate dates in a Perl CGI-script? - perl

I'm creating a small reporting script in Perl CGI. I want to have the start/end date there to filter the events on the report. So now I wonder how to validate the input in some easy, Perl-way.
The way I generate the form with those fields is:
print textfield({-name=>'start_date', -value=>$start_date});
print textfield({-name=>'end_date', -value=>$end_date});
Then it goes to the database query.
Is there a simple, Perl-ish way to validate those dates? Not only as having the right number of characters, as this is simple enough via a regexp, but I'd like to report some error if the user enters 29.02.1999 or so.

I'll just go ahead and own up to being crazy, but what I like to do is use Time::Local, and flip the time back to epoch time, and then, when it's a nice clean integer, you can impose whatever sort of sanity check you like on it.

For general form validation you should use a nice framework like Data::FormValidator which has a Data::FormValidator::Constraints::DateTime module for date validation
Disclosure: I'm the author of Data::FormValidator::Constraints::DateTime

FormFu seems to be the popular library for handling forms in Perl these days. I believe it replaces the CGI HTML generation code, but it is quite powerful.
It includes a bunch of code for validating common data types, including dates.

You can calculate dates in Perl with Time::Local, which can be set to accept invalid times and still calculate the correct date (e.g. the 32th day of a month) or the check the dates and reject invalid dates.

I nearly always opt to convert something like this into DateTime object. And I use something like DateTimeX::Easy to help ease the process:
use DateTimeX::Easy;
my $start_date = DateTimeX::Easy->new( '30th Oct 2009' );
my $from_date = DateTimeX::Easy->new( 'today' );
say $start_date->dmy;
say $from_date->dmy;
# => 30-10-2009
# => 01-10-2009
The only caveat is that DateTime and its associated modules are a bit hefty on memory which is something you may want to keep an eye on when using it in CGI.
/I3az/

Related

Why is my date out of range for some dates but not others?

I'm using bootstrap-datepicker-rails (v. 1.3.0.2) in my rails 4.0.9 project, and seeing some strange behavior with it in development and production, as the title explains. I've already had a look at this question, but it seems my issue is not the same, and more bizarre, than the OP's.
Like him, I'm getting an argument out of range error when I submit my form (whether it be for a create action or an update action), but only for some dates, and not for others.
For example, this date is out of range (copied from my development log - a snippet from the parameters sent to the controller):
"date_received"=>"08/28/2014"
Yet this date is OK:
"date_received"=>"08/07/2014"
I think the fact that some date selections save correctly rules out a formatting issue. I'm stumped as to how this could be happening. Any date before 08/13/2014 works, and any date after that returns the out of range error.
Here's the ugly details:
application.js:
//= require jquery
//= require jquery.turbolinks
//= require jquery.tablesorter
//= require jquery_ujs
//= require bootstrap
//= require bootstrap-datepicker/core
//= require jquery-ui
//= require turbolinks
application.css:
*= require jquery-ui/theme
*= require bootstrap-datepicker
*= require font-awesome
*= require custom
*= require theme.blue
*= require_self
jobs.js.coffee:
$(document).ready ->
$ ->
$(".tablesorter").tablesorter( {sortList: [[2,0]]} )
$('.datepicker').datepicker()
return
job.rb:
(validation for the field in question):
validates :date_received, presence: true
jobs_controller.rb:
(line that the error occurs on):
if #job.update_attributes(job_params)
job_params definition:
def job_params
params.require(:job).permit(:number, :name, :display_name, :date_received, :market,
:job_type, :pose_selection, :pose_selection_label, :pose_selection_deadline,
:pk_id, :pk, :flyout_id, :flyouts, :code, :tax_rate, :shipping_handling, :mail_home,
:mail_home_amount, :line, :notes, :entered, :entered_by, :verified, :verified_by,
:printed, :printed_by, :assembled_by, :shipped, :active, :discount_amount, :data)
end
Where do I go from here to troubleshoot this?
The default format for a date that bootstrap-datepicker-rails returns is "mm-dd-yyyy". Submitting dates formatted this way either saves the date incorrectly, or results in "out of range" errors, because Rails is misinterpreting the day as the month. h/t to #SteveTurczyn for opening my eyes to this fact.
#AndrewMorton suggested making bootstrap-datepicker-rails submit a Date rather than a String, but there's no such mechanism built into that gem, AFAIK.
So I needed to change the format of the String being returned by datepicker. After some console testing, I found that a format of "dd-mm-yyyy" would successfully save (among other formats, of course).
Changing this line (from jobs.js.coffee):
$('.datepicker').datepicker()
... to this:
$('.datepicker').datepicker( { dateFormat: 'dd-mm-yy' } )
forces the date returned to the form to be in a format of "dd-mm-yyyy".
The only downside to this fix is that after selecting a date via the datepicker, the date displayed on the form is not formatted for the U.S. audience (e.g. "23-09-2014" instead of "09-23-2014"). But in all other views I'm able to display the U.S. formatting via a strftime conversion. It's a sacrifice I'm willing to make for simplicity's sake. At least until I don't have bigger fish to fry.
UPDATE: This is really a terrible 'solution.'
After too much searching for anybody's good, I came across this post which does a much better job of using datepicker with U.S. date formatting. It has it's drawbacks (see my comment on the post), but will work for my purposes.

How to handle date input in Laravel

I'm working on an app that allows the user to edit several dates in a form. The dates are rendered in the European format (DD-MM-YYYY) while the databases uses the default YYYY-MM-DD format.
There are several ways to encode/decode this data back and forth from the database to the user, but they all require a lot of code:
Use a helper function to convert the date before saving and after retrieving (very cumbersome, requires much code)
Create a separate attribute for each date attribute, and use the setNameAttribute and getNameAttribute methods to decode/encode (also cumbersome and ugly, requires extra translations/rules for each attribute)
Use JavaScript to convert the dates when loading and submitting the form (not very reliable)
So what's the most efficient way to store, retrieve and validate dates and times from the user?
At some point, you have to convert the date from the view format to the database format. As you mentioned, there are a number of places to do this, basically choosing between the back-end or the front-end.
I do the conversion at the client side (front-end) using javascript (you can use http://momentjs.com to help with this). The reason is that you may need different formats depending on the locale the client is using (set in the browser or in his profile preferences for example). Doing the format conversion in the front-end allows you to convert to these different date formats easily.
Another advantage is that you can then use the protected $dates property in your model to have Laravel handle (get and set) these dates automatically as a Carbon object, without the need for you to do this (see https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Eloquent/Model.php#L126).
As for validation, you need can then use Laravel's built-in validation rules for dates, like this:
'date' => 'required|date|date_format:Y-n-j'
While client-side is good for UX, it doesn't let you be sure, all will be good.
At some point you will need server-side validation/convertion anyway.
But here's the thing, it's as easy as this:
// after making sure it's valid date in your format
// $dateInput = '21-02-2014'
$dateLocale = DateTime::createFromFormat('d-m-Y', $dateInput);
// or providing users timezone
$dateLocale =
DateTime::createFromFormat('d-m-Y', $dateInput, new DateTime('Europe/London'));
$dateToSave = $dateLocale
// ->setTimeZone(new TimeZone('UTC')) if necessary
->format('Y-m-d');
et voila!
Obviously, you can use brilliant Carbon to make it even easier:
$dateToSave = Carbon::createFromFormat('d-m-Y', $dateInput, 'Europe/London')
->tz('UTC')
->toDateString(); // '2014-02-21'
Validation
You say that Carbon throws exception if provided with wrong input. Of course, but here's what you need to validate the date:
'regex:/\d{1,2}-\d{1,2}-\d{4}/|date_format:d-m-Y'
// accepts 1-2-2014, 01-02-2014
// doesn't accept 01-02-14
This regex part is necessary, if you wish to make sure year part is 4digit, since PHP would consider date 01-02-14 valid, despite using Y format character (making year = 0014).
The best way I found is overriding the fromDateTime from Eloquent.
class ExtendedEloquent extends Eloquent {
public function fromDateTime($value)
{
// If the value is in simple day, month, year format, we will format it using that setup.
// To keep using Eloquent's original fromDateTime method, we'll convert the date to timestamp,
// because Eloquent already handle timestamp.
if (preg_match('/^(\d{2})\/(\d{2})\/(\d{4})$/', $value)) {
$value = Carbon\Carbon::createFromFormat('d/m/Y', $value)
->startOfDay()
->getTimestamp();
}
return parent::fromDateTime($value);
}
}
I'm new in PHP, so I don't know if it's the best approach.
Hope it helps.
Edit:
Of course, remember to set all your dates properties in dates inside your model. eg:
protected $dates = array('IssueDate', 'SomeDate');

How to make Jasper Reports programmatically determine the Name of Columns within the report it self?

I am generating a report with that will have a 7 columns where the last 6 should have the the last 6 months listed. So as of the time of this writing it should be:
NAME -> September -> August -> July -> June -> May -> April
ss the column headers. I am trying to avoid having to pass them in as parameters, and am trying to get Jasper Reports to figure it out at runtime. I can get the first month pretty easily using a Text Field Expression. It looks like:
new java.text.SimpleDateFormat("MMMMM").format(new Date())
The issue comes in with the other months. I initially tried
new java.text.SimpleDateFormat("MMMMM").format(java.util.Calendar.getInstance().add(Calendar.MONTH, new Integer("-1)).getTime())
This does not work since Calendar.add does not return a Calendar instance. I then tried using a variable and then a combination of variables which also did not work.
How to make Jasper Reports programmatically determine the Name of Columns within the report it self?
I think the best approach to solving this problem is to use Commons Lang. That package provides utilities to make calculations like this very easy. By adding one extra jar you can then use expressions like this:
DateUtils.addMonths(new Date(),-1)
I find that easier to maintain than first creating a helper Calendar class and then using the ternary operator but ignoring its results.
$P{cal}.add(Calendar.MONTH, -1)
? null : $P{cal}.getTime()
If you ever need to generalize the solution then it's a lot easier to get a Date back from a SQL query than to get a Calendar. So you can quickly change to "DateUtils.addMonths($F{MyDate},-1)". Initializing a Calendar to match a date returned by a query isn't nearly as simple. But of course there's a certain benefit to not having to add one more .jar file. So sometimes that ternary operator technique is the quickest way to get things done.
I wrote about using the Commons Lang approach a couple of years ago here: http://mdahlman.wordpress.com/2009/09/09/jasperreports-first-dates/
I also needed a certain format for the previous month. I ended up combining the other two answers:
new java.text.SimpleDateFormat("yyyyMM").format(
org.apache.commons.lang3.time.DateUtils.addMonths(
new java.util.Date(),
-6
)
)
This way I don't need to add another parameter. Adding the Commons Lang jar is a non issue for me since JasperServer 5.5 comes with version 3.0 out of the box.
I hope this helps someone who stumples upon this page, just like I did.
I found a working solution that is pretty ingenious (no I did not come up with it). I found it here. The gist of it is create a parameter called call, with a default value of:
Calendar.getInstance()
and un-check the option 'Use as a prompt'. Then in your text field expression you would do:
new java.text.SimpleDateFormat("MMMMM").format(
(
$P{cal}.add(Calendar.MONTH, -1)
? null : $P{cal}.getTime()
)
)
What happens is it will set the default value for the calendar instance, then execute the add method, which will resolve to false, so then it will then return the result from getTime() method which gets formatted how I want.

MongoImport Dates Occurring Before The Epoch

I am writing a utility at work which converts our relational DB at work to a complex JSON object and dumps to files grouped by subject. I then would like to import these files into MongoDB collections using the mongoimport tool.
Our data includes timestamps which represent dates occurring before the epoch, the appropriate JSON representation of which yields negative numbers. While MongoDB itself will handle these fine, the import tools JSON parser uses unsigned long long variables and fails.
If you use Mongo's special JSON date representation format ({"key": { "$date": "value_in_ticks" } }), the import tool will throw an error on those documents and skip the import. You can also use the JavaScript date notation ({"key": new Date(value_in_ticks) }) which will be successfully imported but parsed as an unsigned value creating a garbage date.
The special date format fails because of an assertion checking for reserved words. This code is reached because the presence of the negative sign at the beginning of the value causes the special date parsing to exit and return to normal document parsing.
The code to parse JSON dates explicitly calls the boost library uint_parser. There exists a signed version of this function and an issue on their JIRA tracker already exists to utilize it (on which I commented that I would attempt).
Short of diving into the code immediately to try and update this to be signed, is there an alternate route that I can take to load these dates for now?
I want to run this nightly via cron for a few months for testing so I would prefer it be very easy. These dates exist in many different parts of documents in many different collections so the solution should be generalized.
A little late to the party, but I have just come up against the same issue.
My workaround was to import the dates as strings (e.g. "1950-01-01"), and to script the conversion using Ruby on Rails with Mongoid:
Dates.each do |d|
d.mydate = d.mydate.to_date
d.save
end
Hopefully you can adapt this to whatever language/framework you are using.
This Python snippet works for me.
import time, struct
def bson_datetime(adatetime):
try:
ret = int(1000*(time.mktime(adatetime.timetuple()) + 3600))
if ret < 0:
ret = struct.unpack('Q', struct.pack('q', ret))[0]
return {'$date': ret}
except ValueError:
return None
I.e.
import datetime
print bson_datetime(datetime.datetime(1950, 12, 30, 0, 0))
yields {"abc" : {"$date" : 18446743473920751616}}.
Step 1: go to groups.google.com/group/mongodb-user and post the issue "mongoimport does not support dates before the epoch". Response times on the groups tend to be very good.
Step 2: think of running dates in a universally accepted format like "1964-04-25 13:23:12"
It will take a little bit more space in MongoDB because you'll be storing string. However it should be easy to interpret for anyone pulling out the data.

sqlite writing a date into an email

I am exporting a date value from sqlite and placing it into an email. The date appears like this
279498721.322872
I am using Objective C in an Iphone App. Does anyone know how to make this export out as a regular date whether it is all number like
2009-02-10 or anything legible?
Well, if you take the number 279498721.322872 and throw it into an NSDate object using +dateWithTimeIntervalSinceReferenceDate, you get (here in the MDT timezone): 2009-11-09 15:32:01 -0700, which was just under 4 hours ago. If that's the time you're expecting, then formatting it is as simple as using an NSDateFormatter.
However, the thing to notice is that sqlite (by default) stores dates as textual representations (unless you specify differently in the sql statement). So the real question here is "where are you getting that number from?"
echo date("Y-m-d",time(279498721.322872));
Thanks for the responses. The answer came from my Guru Alex Cone. He told me to use the following:
NSTimeInterval tempInterval = (NSTimeInterval)sqlite3_column_double(statement, 4);
The tempInterval variable can then be loaded into the the NSDate method.