I'm a little stuck trying to create a function that takes a single, optional argument. Instead of this being a string I'd like it to be the result of a function (or even better, a DateTime object). Essentially - I want the user to either pass in a DateTime object, or for the function to resort to todays date if no arguments are supplied. Is this possible with PHP? By trying to create the new object in the function header as such
function myDateFunction($date = new DateTime()){
//My function goes here.
}
causes PHP to fall over.
Many thanks.
Yes. It is possible if you move $date instantiation to function body:
<?php
header('Content-Type: text/plain');
function myDateFunction(DateTime $date = null){
if($date === null){
$date = new DateTime();
}
return $date->format('d.m.Y H:i:s');
}
echo
myDateFunction(),
PHP_EOL,
myDateFunction(DateTime::createFromFormat('d.m.Y', '11.11.2011'));
?>
Result:
15.09.2013 17:25:02
11.11.2011 17:25:02
From php.net:
Type hinting allowing NULL value
The default value must be a constant expression, not (for example) a variable, a class member or a function call.
http://php.net/manual/en/functions.arguments.php#example-154
You can do it this way:
function myDateFunction($date = null){
if(is_null($date) || !($date instanceof DateTime)) {
$date = new DateTime();
}
return $date;
}
var_dump(myDateFunction());
You can use other option:
function myDateFunction($date = null){
if(is_null($date)) $date = new DateTime();
}
function myDateFunc($date = null){
if(!isset($date) || $date !instanceof DateTime){
$date = new DateTime()
}
/* YOur code here*/
}
for optional argument in your function, you can write code like
function myDateFunction($date = ''){
//My function goes here.
if($date==''){ $date = new DateTime()}
}
hope it helps
Related
I'm creating a validation for the post in my Controller.
I have a problem about the date.
I have created in applications/libraries a MY_Form_validation.php page in which I wrote this:
public function valid_date($date, $format = 'Y-m-d') {
$d = DateTime::createFromFormat($date, $format);
return $d && $d->format($format) === $date;
}
Then I have created the form_validation_lang.php with
<?php
$lang['form_validation_valid_date'] = 'The field {field} is not a valid date';
While in my controller I have:
//load the libraries
$this->form_validation->set_rules('birthdate', 'birthdate', 'trim|required|valid_date');
if($this->form_validation->run() == FALSE){
$errors = $this->form_validation->error_array();
var_dump($errors);
$this->response($errors, 500);
return;
}
$person = array(
//..
'birthdate' => $this->post('birthdate'),
//...
);
It prints me that:
'birthdate' => 'string'
About my DB at the beginning my field was Data, and i received this error. Than I have changed to Datetime but the error is the same.
Now it's:
birthdate date NOT NULL,
Now for example I'm trying to post a date like '1960-04-20' and I receive back the error:
"birthdate": "The field birthdate is not a valid date"
I want to write in the db only the date and not the time, could be this the problem??
How can I do to validate date?
Thank you
Replace your function with next:
public function valid_date($date, $format = 'Y-m-d') {
$d = new DateTime($date);
return $d && $d->format($format) === $date;
}
Demo
Or with next one:
public function valid_date($date, $format = 'Y-m-d') {
$d = DateTime::createFromFormat($format,$date); // <-- replace arguments
return $d && $d->format($format) === $date;
}
Demo
DateTime::createFromFormat() documentation
You need to cast this string, after success validation, into DATE datatype and only then send it into DB. Or you can write "to_date($stringvar,$format)" in your SQL query.
Seems like CodeIgniter doesn't likes default variables in validation options. I mean, you can't declare $format = 'Y-m-d' as an argument. So, it would work if you won't use it:
public function valid_date_dmY($str)
{
$d = DateTime::createFromFormat('d.m.Y',$str);
return $d && $d->format('d.m.Y') === $str;
}
Currently I am using such fragment:
if (is_numeric($date)) {
$datetime = new DateTime('#'.$date);
} else {
$datetime = new DateTime($date);
}
Where $date is either unixtimestamp or some date string like 2016/03/03.
What are the best failsafe equivalent for this construct ?
Maybe some one-liner ? A wrapper class ?
I am asking about some shorter construct as we are using the above in many places in our system. So I would like to replace this with something more readable and failsafe.
If you want something reusable across your project, wrap it in your own class, e.g.
class AcmeDateTime extends \DateTime
{
public function __construct($time = "now", DateTimeZone $timezone = null)
{
$time = is_numeric($time) ? "#" . $time : $time;
parent::__construct($time, $timezone);
}
}
You could use ternary operator in php to shorten the 4 lines to a single line like this:
$datetime = new DateTime((is_numeric($date) ? '#' : '') . $date);
In my app I can create projects and for each project I can record work reports. Each report has a start and an end timestamp. A table in the project description shows every report for this project and it also calculates the duration (with DateTime and the diff() function used between start and end timestamp). Now I want to calculate the total work time on the projects but I have no idea how I can do this. I already tried looping through all reports somehow and then use DateTime functions, but I'm getting nowhere... My last desperate attempt was this:
public static function calculateDuration($start, $end)
{
$start = date('Y-m-d H:i:s', $start);
$end = date('Y-m-d H:i:s', $end);
$s = new \DateTime($start);
$e = new \DateTime($end);
$interval = $e->diff($s);
return $interval->format('%H:%I');
}
public static function calculateTotal($idProject)
{
$reports = self::find('id_project = "' . $idProject . '"');
$totalReports = new \DateTime();
foreach ($reports as $report) {
$totalReports->add(new \DateInterval(self::calculateDuration($report->getStart(), $report->getEnd())));
}
/*echo '<pre>';
die(var_dump($totalReports));
echo '</pre>';*/
return $totalReports->format('H:I');
}
the calculateDuration functions works perfectly, but of course calculateTotal doesn't, because DateInterval does not take a string like "0:30". So that is completely useless...
I hope I provided all the needed information, just let me know if you need something else.
For clarity on my comments: You already have calculateDuration doing all the work, and it internally deals with a DateInterval object. So why not make use of it? Here, getInterval is a protected method that's used by both existing methods and returns the DateInterval object directly. Now calculateDuration becomes a simple formatting function, and calculateTotal has access to the DateInterval object.
protected static function getInterval($start, $end)
{
$s = new \DateTime('#' . $start);
$e = new \DateTime('#' . $end);
return $e->diff($s);
}
public static function calculateDuration($start, $end)
{
return self::getInterval($start, $end)->format('%H:%I');
}
public static function calculateTotal($idProject)
{
// ...
$totalReports = new \DateTime();
$totalReportsEnd = clone $totalReports;
foreach ($reports as $report) {
$totalReportsEnd->add(self::getInterval(
$report->getStart(),
$report->getEnd()
));
}
$totalInterval = $totalReportsEnd->diff($totalReports);
// do as you wish with the interval
}
Just create a valid DateInterval format instead of returning hours and minutes:
// Untested. Might need escaping.
return $interval->format('PT%HH%IM');
I wonder if this is possible somehow …
function get_event_list( $year = date('Y') ) {
}
So I could call this function like get_event_list(2012), but when not adding a para it always retrieves all events from the current year (2014);
Kind Regards,
Matt
The best way to achieve it is to do:
function get_event_list( $year = null ) {
if ( is_null($year) ) {
$year = date('Y');
}
}
You can't use a built-in function as the default argument.
From the PHP manual:
The default value must be a constant expression, not (for example) a variable, a class member or a function call.
What you need can be achieved as follows:
function get_event_list($year = null) {
if(!isset($year)) {
$year = date('Y');
}
}
You could make the parameter nullable like so:
function get_event_list( $year = NULL ){
$year = is_null( $year) ? date('Y') : $year;
//Code here
}
A way to call this function would be get_event_list(2012) or get_event_list()
You can do it like this
function get_even_list($year = ""){
if(empty($year)){
$year = date('Y');
}
// Whatever you wann do here
}
Steve
How do you validate ISO 8601 date string (ex: 2011-10-02T23:25:42Z).
I know that there are several possible representations of ISO 8601 dates, but I'm only interested in validating the format I gave as an example above.
Thanks!
I want to share my lightweight solution. It is not ideal, but might be helpful for someone.
function validISO8601Date($value)
{
if (!is_string($value)) {
return false;
}
$dateTime = \DateTime::createFromFormat(\DateTime::ISO8601, $value);
if ($dateTime) {
return $dateTime->format(\DateTime::ISO8601) === $value;
}
return false;
}
Atention!
Some valid ISO8601 dates will fail Look at the list below
NOT VALID --> '' // Correct
NOT VALID --> 'string' // Correct
VALID --> '2000-01-01T01:00:00+1200' // This is the only format function returns as valid
NOT VALID --> '2015-01 first' // Correct
NOT VALID --> '2000-01-01T01:00:00Z' // Must be valid!
NOT VALID --> '2000-01-01T01:00:00+01' // Must be valid!
This worked for me, it uses a regular expression to make sure the date is in the format you want, and then tries to parse the date and recreate it to make sure the output matches the input:
<?php
$date = '2011-10-02T23:25:42Z';
var_dump(validateDate($date));
$date = '2011-17-17T23:25:42Z';
var_dump(validateDate($date));
function validateDate($date)
{
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', $date, $parts) == true) {
$time = gmmktime($parts[4], $parts[5], $parts[6], $parts[2], $parts[3], $parts[1]);
$input_time = strtotime($date);
if ($input_time === false) return false;
return $input_time == $time;
} else {
return false;
}
}
You could expand further to use checkdate to make sure the month day and year are valid as well.
Edit: By far the easiest method is to simply try to create a DateTime object using the string, eg
$dt = new DateTime($dateTimeString);
If the DateTime constructor cannot parse the string, it will throw an exception, eg
DateTime::__construct(): Failed to parse time string (2011-10-02T23:25:72Z) at position 18 (2): Unexpected character
Note that if you leave off the time zone designator, it will use the configured default timezone.
Second easiest method is to use a regular expression. Something like this aught to cover it
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(\+|-)\d{2}(:?\d{2})?)$/', $dateString, $parts)) {
// valid string format, can now check parts
$year = $parts[1];
$month = $parts[2];
$day = $parts[3];
// etc
}
See http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/. It gives this regex to use:
^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$
I suppose this does not answer your question exactly, since it will match any valid ISO 8601 date, but if that is alright then this works perfectly.
This is the function I use. It is similar to the answer of drew010 but works also for timestamps ending with "+01:00" or "-01:00".
function isTimestampIsoValid($timestamp)
{
if (preg_match('/^'.
'(\d{4})-(\d{2})-(\d{2})T'. // YYYY-MM-DDT ex: 2014-01-01T
'(\d{2}):(\d{2}):(\d{2})'. // HH-MM-SS ex: 17:00:00
'(Z|((-|\+)\d{2}:\d{2}))'. // Z or +01:00 or -01:00
'$/', $timestamp, $parts) == true)
{
try {
new \DateTime($timestamp);
return true;
}
catch ( \Exception $e)
{
return false;
}
} else {
return false;
}
}
Carbon is handling this part really well
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
...
/** #test */
public function checking_iso8601_date()
{
$this->expectException(InvalidFormatException::class);
Carbon::createFromFormat('c', '2021-05-11 20:03:45+02:00');
$this->assertInstanceOf(Carbon::class, Carbon::createFromFormat('c', '2021-05-11T20:03:45+02:00'));
}
You may do the same with DateTime
use DateTime;
...
/** #test */
public function checking_iso8601_date()
{
$this->assertInstanceOf(DateTime::class, DateTime::createFromFormat('Y-m-d\TH:i:sP', '2021-05-11T20:03:45+02:00'));
$this->assertFalse(DateTime::createFromFormat('Y-m-d\TH:i:sP', '2021-05-11 20:03:45+02:00'));
}