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');
Related
I want to display formatted month name in the basic form (nominative) as a label. In Czech language (and several next Slavic languages) we use declension. So if I use =TEXT(NOW();"MMMM") the cell shows month name in genetive instead of nominative (i.e. srpna instead of srpen).
Q: How to format the date in the nominative? The acceptable solution will offer some native way how to solve it but not:
manipulating with strings (month names are too complex)
having some another list with month names
calling own formula (Google script)
Maybe there is no native way how to solve this and it will be implemented in the future, so Google Script seems to be the easiest hotfix for now. And because I'm expecting many answers with the script, I'm putting the one here but this question is not about the javascript/google-script.
function monthName(date) {
var months = ["Leden","Únor","Březen","Duben","Květen", "Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"];
return months[date.getMonth()];
}
I'm struggling with Laravel date formats, using it with PgSQL.
My need :
input date must be in this ISO8601 format: Y-m-d\TH:i:s\Z (example: 2016-05-13T10:05:00Z)
output must be in the same format
PgSQL stores it in the format Y-m-d H:i:s (example: 2016-05-13 10:05:00)
The hard part is to configure Laravel to globally accept a particular date format in input and output, but without changing "internal" format.
A property can be overridden to customize format, but it acts globally:
protected $dateFormat = 'Y-m-d\TH:i:s\Z';
This leads to errors when retrieving dates from database (trying to convert a PgSQL date with a custom mask: InvalidArgumentException).
I would like to avoid using a Middleware or do it manually in each controller response, is there any way?
Sounds like you want to use accessors and mutators. Here's a basic example with a class that has a my_date column, the conversion might not be exactly right (I'm just assuming strtotime() will work) but that should be easy for you to fix.
class SomeModel extends Model
{
// triggered whenever you retrieve the date from the db
public function getMyDateAttribute($value)
{
return date('Y-m-d\TH:i:s\Z', strtotime($value));
}
// triggered whenever you insert the date into the db
public function setMyDateAttribute($value)
{
$this->attributes['my_date'] = date('Y-m-d H:i:s', strtotime($value));
}
}
Now, your database can use one datetime format and the other parts of your application can use another. If you want it to be reusable for other models, you can extract it to a trait as long as the columns are named the same.
I am using Google Script to export some calendar events to a spreadsheet; the relevant portion of my script is below:
var details=[[mycal,events[i].getTitle(), events[i].getDescription(), events[i].getLocation(), events[i].getStartTime(), myformula_placeholder, ('')]];
var range=sheet.getRange(row,1,1,7);
range.setValues(details);
This code works but the "time" that is put into the spreadsheet is a real number of the form nnnnn.nn. On the spreadsheet itself the date looks great using the integer to the left of the decimal (eg 10/15/2017) but the decimals are part of the value and therefore are part of the spreadsheet value.
My script drops the data into a sheet in my workbook, and another sheet reads the rows of data with the above date types, looking for specific date info from the other sheet using the match function (for today()). That would work fine if I could get rid of the decimals.
How can I use what I have above (if I stray far from what I have found works I will be redoing hours of work) but adding just what is needed to only put into the output spreadsheet the whole number portion so I have a pure date that will be found nicely by my match function using today()?
I have been digging, but errors abound in trying to put it all together. "Parse" looked like a good hope, but it failed as the validation did not like parse used within getStartTime. Maybe I used it in the wrong manner.
Help would be appreciated greatly.
According to the CalendarApp documentation, getStartTime() generates a Date object. You should be able to extract the date and time separately from the date object:
var eventStart = events[i].getStartTime(); // Returns date object
var startDate = eventStart.toDateString(); // Returns date portion as a string
var startTime = eventStart.toTimeString(); // Returns time portion as a string
You could then write one or both of these to your spreadsheet. See the w3schools Javascript Date Reference for more information:
http://www.w3schools.com/jsref/jsref_obj_date.asp
If you If you want to specify the string format, you can try formatDate in the Utilities service:
https://developers.google.com/apps-script/reference/utilities/utilities#formatdatedate-timezone-format
You could just use the Math.floor() function
http://www.w3schools.com/jsref/jsref_floor.asp
which will round the real number to an integer. Your line would then read:
var details=[[mycal,events[i].getTitle(), events[i].getDescription(), events[i].getLocation(), Math.floor(events[i].getStartTime()), myformula_placeholder, ('')]];
I am in the unfortunate position that I need to use a composite id in a Grails app where I work with legacy data. This means I have to override some actions in the controller, but as I did this I was struck by the fact that I could not use use a date argument directly as a parameter to a dynamic method.
Instead of just doing MyLegacyObj.findBySystemIdAndLogDate(params.systemId, params.logDate), I first needed to parse the date string before giving it to the dynamic method. To further complicate matters I had no idea what format the date string had (until I added lots of log.debug() string to the output). So now I have a bit of code looking like this
def formatter = new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy")
MyLegacyObj.findBySystemIdAndLogDate(params.systemId, formatter.parse(params.logDate));
This feels unoptimal, no to say dangerous (what if the date format changes with the locale?)? What would be a recommended way of doing this, and do I really need to parse dates at all?
Date is a pretty complex object and params are just Strings, so Date is submitted in parts. It is "magically" assembled from the parts when assigning x.properties = params.
Command object will do the work for you, if you add a Date field to it.
It has nothing to do with methods' dynamic or static invocation. Your GSP that renders Date editor might interfere too.
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/